Signatur
Beschreibung
RecursiveIterator erweitert das Standard-Iterator-Interface um zwei zusätzliche Methoden: hasChildren() und getChildren(). Diese ermöglichen es, während der Iteration festzustellen, ob das aktuelle Element selbst Kindelemente besitzt, und diese als eigenen RecursiveIterator abzurufen.
Das Interface wird vor allem in Kombination mit RecursiveIteratorIterator verwendet, der die rekursive Traversierung automatisch übernimmt. Typische Einsatzgebiete sind das Durchlaufen von Verzeichnisstrukturen (RecursiveDirectoryIterator), verschachtelten Arrays (RecursiveArrayIterator) oder eigener Baumstrukturen.
Eigene Klassen, die RecursiveIterator implementieren, müssen alle Methoden von Iterator (current(), key(), next(), rewind(), valid()) sowie hasChildren() und getChildren() implementieren. getChildren() muss dabei eine Instanz zurückliefern, die ebenfalls RecursiveIterator implementiert.
Durch die Verwendung von RecursiveIteratorIterator lassen sich Tiefen-Traversierungen (depth-first) ohne manuelle Rekursion realisieren, was den Code deutlich sauberer und wartbarer macht.
Beispiele
Eigene Baumstruktur mit RecursiveIterator traversieren
<?php
class TreeNode
{
public string $value;
/** @var TreeNode[] */
public array $children;
public function __construct(string $value, array $children = [])
{
$this->value = $value;
$this->children = $children;
}
}
class TreeIterator implements RecursiveIterator
{
private int $position = 0;
/** @param TreeNode[] $nodes */
public function __construct(private array $nodes) {}
public function current(): TreeNode { return $this->nodes[$this->position]; }
public function key(): int { return $this->position; }
public function next(): void { $this->position++; }
public function rewind(): void { $this->position = 0; }
public function valid(): bool { return isset($this->nodes[$this->position]); }
public function hasChildren(): bool
{
return !empty($this->nodes[$this->position]->children);
}
public function getChildren(): self
{
return new self($this->nodes[$this->position]->children);
}
}
$tree = [
new TreeNode('Root', [
new TreeNode('Kind 1', [
new TreeNode('Enkelkind 1.1'),
new TreeNode('Enkelkind 1.2'),
]),
new TreeNode('Kind 2'),
]),
];
$iterator = new RecursiveIteratorIterator(
new TreeIterator($tree),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $node) {
$indent = str_repeat(' ', $iterator->getDepth());
echo $indent . $node->value . PHP_EOL;
}
Verzeichnisstruktur rekursiv ausgeben (RecursiveDirectoryIterator)
<?php
$directory = new RecursiveDirectoryIterator(
'/var/www/html',
RecursiveDirectoryIterator::SKIP_DOTS
);
$iterator = new RecursiveIteratorIterator(
$directory,
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $entry) {
$indent = str_repeat(' ', $iterator->getDepth());
echo $indent . $entry->getFilename() . PHP_EOL;
}
// Wichtig · Fallstricke
Hinweis zu getChildren(): Diese Methode darf nur aufgerufen werden, wenn hasChildren() zuvor true zurückgeliefert hat. Ein Aufruf ohne vorherige Prüfung kann zu undefinierten Zuständen oder Fehlern führen.
Rekursionstiefe: Bei sehr tief verschachtelten Strukturen kann RecursiveIteratorIterator mit dem Parameter $maxDepth in setMaxDepth() begrenzt werden, um Stacküberlauf oder Performance-Probleme zu vermeiden.
Richtung der Traversierung: RecursiveIteratorIterator traversiert standardmäßig Blätter zuerst (LEAVES_ONLY). Mit SELF_FIRST oder CHILD_FIRST lässt sich die Reihenfolge anpassen.