Start · Sprachen · PHP · Referenz · IteratorIterator

IteratorIterator

Klasse

Wandelt ein beliebiges <code>Traversable</code>-Objekt in einen vollständigen <code>Iterator</code> um, sodass es in Iterator-Chains und SPL-Klassen verwendet werden kann.

seit PHP 5.1.0 Kategorie: oop

Signatur

class IteratorIterator implements OuterIterator

Beschreibung

IteratorIterator dient als Adapter, der jedes Objekt, das Traversable implementiert (z. B. IteratorAggregate), in einen echten Iterator verwandelt. Dies ist notwendig, weil viele SPL-Klassen wie FilterIterator oder LimitIterator explizit einen Iterator erwarten und ein reines Traversable nicht akzeptieren.

Die Klasse delegiert alle Iterator-Methoden (current(), key(), next(), rewind(), valid()) an den gekapselten inneren Iterator. Über getInnerIterator() (aus OuterIterator) kann jederzeit auf das ursprüngliche Objekt zugegriffen werden.

IteratorIterator eignet sich besonders als Basisklasse für eigene Iterator-Dekoratoren. Durch Ableitung und Überschreiben einzelner Methoden lässt sich das Verhalten selektiv anpassen, ohne die gesamte Iterator-Logik neu implementieren zu müssen.

Typische Einsatzbereiche sind das Einwickeln von Datenbankresultatmengen (z. B. PDOStatement implementiert Traversable) oder das Normalisieren fremder Traversable-Objekte für SPL-Pipelines.

Parameter

Name Typ Default Beschreibung
$iterator Pflicht Traversable Das zu kapselnde Traversable-Objekt. Dies kann ein Iterator, ein IteratorAggregate oder ein anderes Traversable sein.

Beispiele

IteratorAggregate in einen Iterator verwandeln

<?php
class NumberCollection implements IteratorAggregate
{
    private array $numbers;

    public function __construct(array $numbers)
    {
        $this->numbers = $numbers;
    }

    public function getIterator(): ArrayIterator
    {
        return new ArrayIterator($this->numbers);
    }
}

$collection = new NumberCollection([10, 20, 30, 40, 50]);

// NumberCollection ist Traversable, aber kein Iterator
// LimitIterator benötigt einen Iterator – daher IteratorIterator als Adapter
$iterator = new IteratorIterator($collection);
$limited  = new LimitIterator($iterator, 1, 3);

foreach ($limited as $key => $value) {
    echo "$key => $value\n";
}
1 => 20 2 => 30 3 => 40

Eigener Dekorator auf Basis von IteratorIterator

<?php
class UpperCaseIterator extends IteratorIterator
{
    public function current(): string
    {
        return strtoupper(parent::current());
    }
}

$words = new ArrayIterator(['hello', 'world', 'php']);
$upper = new UpperCaseIterator($words);

foreach ($upper as $word) {
    echo $word . PHP_EOL;
}
HELLO WORLD PHP

PDOStatement als Iterator nutzen

<?php
// PDOStatement implementiert Traversable, aber nicht Iterator
// IteratorIterator macht daraus einen vollständigen Iterator

$pdo  = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE users (id INTEGER, name TEXT)');
$pdo->exec("INSERT INTO users VALUES (1,'Alice'),(2,'Bob'),(3,'Charlie')");

$stmt = $pdo->query('SELECT * FROM users', PDO::FETCH_ASSOC);
$iter = new IteratorIterator($stmt);

foreach ($iter as $row) {
    echo $row['id'] . ': ' . $row['name'] . PHP_EOL;
}
1: Alice 2: Bob 3: Charlie

// Wichtig · Fallstricke

Nicht alle Traversables sind rückspulbar: Wenn das zugrundeliegende Objekt rewind() nicht unterstützt (z. B. ein einmalig lesbarer Generator oder ein Datenbankresultat), löst ein erneutes rewind() auf dem IteratorIterator möglicherweise einen Fehler aus oder liefert keine Ergebnisse mehr.

Doppeltes Wrapping vermeiden: Ist ein Objekt bereits ein Iterator, ist IteratorIterator in der Regel unnötig – es kann direkt verwendet werden. Das Wrapping fügt lediglich einen zusätzlichen Delegations-Layer hinzu.

Generator-Einschränkung: Generatoren implementieren zwar Iterator, unterstützen aber kein echtes Zurückspulen. Auch bei Kapselung in IteratorIterator bleibt diese Einschränkung erhalten.