Signatur
Beschreibung
Iterator ist ein eingebautes PHP-Interface, das Klassen ermöglicht, das Verhalten bei einer foreach-Schleife vollständig selbst zu steuern. Wer dieses Interface implementiert, legt fest, wie der interne Zeiger initialisiert, vorgerückt und ausgewertet wird – ohne dass PHP auf ein Array oder eine externe Datenstruktur angewiesen ist.
Typische Einsatzgebiete sind das lazy Loading großer Datensätze (z. B. aus einer Datenbank oder Datei), das Erzeugen von Sequenzen zur Laufzeit sowie das Kapseln komplexer Traversierungs-Logik in einem wiederverwendbaren Objekt. Im Gegensatz zu IteratorAggregate – das einen externen Iterator zurückliefert – iteriert eine Iterator-Klasse sich selbst.
Das Interface schreibt fünf Methoden vor: current(), key(), next(), rewind() und valid(). PHP ruft sie in einer festgelegten Reihenfolge auf: zuerst rewind(), dann in jedem Schleifendurchlauf valid() → current() + key() → next().
Da Iterator das Traversable-Interface erweitert, kann ein Iterator-Objekt auch dort verwendet werden, wo Traversable als Typ-Hint erwartet wird, etwa in Funktionen, die sowohl Arrays als auch Objekte akzeptieren sollen.
Beispiele
Fibonacci-Zahlenfolge als Iterator
<?php
class FibonacciIterator implements Iterator
{
private int $limit;
private int $current;
private int $next;
private int $key;
public function __construct(int $limit = 10)
{
$this->limit = $limit;
}
public function rewind(): void
{
$this->current = 0;
$this->next = 1;
$this->key = 0;
}
public function valid(): bool
{
return $this->key < $this->limit;
}
public function current(): int
{
return $this->current;
}
public function key(): int
{
return $this->key;
}
public function next(): void
{
[$this->current, $this->next] = [$this->next, $this->current + $this->next];
$this->key++;
}
}
foreach (new FibonacciIterator(8) as $index => $value) {
echo "Fib[$index] = $value\n";
}
Datenbankzeilen schrittweise laden (Lazy Loading)
<?php
class DatabaseRowIterator implements Iterator
{
private \PDOStatement $stmt;
private int $key = 0;
private array|false $current = false;
public function __construct(private \PDO $pdo, private string $query) {}
public function rewind(): void
{
$this->stmt = $this->pdo->query($this->query);
$this->key = 0;
$this->current = $this->stmt->fetch(\PDO::FETCH_ASSOC);
}
public function valid(): bool
{
return $this->current !== false;
}
public function current(): array
{
return $this->current;
}
public function key(): int
{
return $this->key;
}
public function next(): void
{
$this->current = $this->stmt->fetch(\PDO::FETCH_ASSOC);
$this->key++;
}
}
// Verwendung – lädt jeweils nur eine Zeile in den Speicher
$pdo = new \PDO('sqlite::memory:');
$iter = new DatabaseRowIterator($pdo, 'SELECT 1 AS id UNION SELECT 2 UNION SELECT 3');
foreach ($iter as $row) {
echo 'ID: ' . $row['id'] . "\n";
}
// Wichtig · Fallstricke
Reihenfolge der Methodenaufrufe: PHP ruft zu Beginn jeder foreach-Schleife zunächst rewind() auf. Wird derselbe Iterator in zwei verschachtelten foreach-Schleifen verwendet, setzt die innere Schleife den Zeiger zurück, was zu unerwartetem Verhalten führt. In solchen Fällen ist eine eigene Klon-Strategie oder IteratorAggregate mit separaten Iterator-Instanzen die bessere Wahl.
Typen der Rückgabewerte: Ab PHP 8.0 können die Methoden mit präzisen Rückgabetypen deklariert werden. Das Interface selbst erzwingt keine konkreten Typen für current() und key() – vernünftige Typen zu definieren verbessert jedoch die Lesbarkeit und IDE-Unterstützung erheblich.
Alternativen: Für einfachere Fälle, bei denen intern ein Array oder ein anderes Traversable-Objekt vorhanden ist, ist IteratorAggregate weniger aufwendig zu implementieren. Für unendliche oder berechnete Sequenzen sind PHP-Generatoren (seit PHP 5.5) oft die elegantere Lösung, da sie keinen Boilerplate-Code benötigen.