PHP bietet mit der SPL (StandardPHPLibrary) eine Vielzahl fertiger Iteratoren an, mit denen Daten in Schleifen verarbeitet werden können, aber auch zwei Interfaces durch die eigene Objekte iterierbar werden.
Ganz klar, es handelt sich um die Interfaces Iterator und IteratorAggregate. Was aber unterscheidet die beiden?
Um den Aufbau der beiden Klassen gegenüberzustellen habe ich zwei einfache Beispiel-Klassen geschrieben, mit denen wir ein Array in einer foreach-Schleife auslesen können.
Iterator
Das Interface Iterator schreibt fünf Methoden vor, die zur Verarbeitung des Objektes in einer Schleife benötigt werden: current(), next(), key(), valid()
und rewind()
.
<?php class TestA implements Iterator { private $_items; public function __construct(array $items = array()) { $this->_items = $items; } public function current() { return current($this->_items); } public function next() { next($this->_items); } public function key() { return key($this->_items); } public function valid() { return false !== current($this->_items); } public function rewind() { reset($this->_items); } }
Auf den ersten Blick lässt sich erkennen, dass relativ viel Code benötigt benötigt wird und dieser starr am Objekt hängt. Ok, für diesen Zweck hätte man auch den ArrayIterator nehmen können, aber dann gäbe es weniger zu gucken.
IteratorAggregate
Das zweite Iterface ist dagegen wesentlich schlanker, denn es kommt mit der einzigen Methode getIterator()
aus.
<?php class TestB implements IteratorAggregate { private $_items; public function __construct(array $items = array()) { $this->_items = $items; } public function getIterator() { return new ArrayIterator($this->_items); } }
Die Methode erzeugt einen externen Iterator und lagert damit die Funktionalität in eine andere Klasse aus. Dadurch bleibt unsere Klasse übersichtlich und das Iterator-Verhalten lässt sich einfach austauschen.
Fazit
Für einfache Strukturen würde ich das IteratorAggregate-Interface vorziehen, da es sehr einfach aufgebaut ist und unseren Code schlank und übersichtlich hält. Die Funktionalität ist zudem durch verschiedene externe Klassen variabel und viele Standardfälle können mit den SPL-Klassen abgedeckt werden.
Lässt sich eine Struktur aus irgendeinem Grund nicht an einen externen Iterator übergeben, bzw. will man für diesen speziellen Fall schreiben, dann kommt das Iterator-Interface zum Einsatz.
Genaugenommen lässt sich das mit den beiden Beispielklassen schon gut testen. Einfach in der Klasse TestB
public function getIterator() { return new ArrayIterator($this->_items); }
gegen
public function getIterator() { return new TestA($this->_items); }
austauschen und etwas damit rumspielen.
In der Anwendung sind beide später identisch:
<?php $a = new TestA(array('A','B','C','D','E')); foreach ($a as $k => $v) { echo "$k = $v <br/>"; } $b = new TestB(array('A','B','C','D','E')); foreach ($b as $k => $v) { echo "$k = $v <br/>"; }
Viel Spaß damit! Eure Erkenntnisse zu dem Thema könnt ihr wie immer gerne in den Kommentaren verewigen…