Iteratory w PHP - część 2: własny iterator
W PHP5 pojawiła się bardzo interesująca możliwość - nadpisanie domyślnego sposobu iteracji własnych klas. Dotychczas konstrukcja (w wersjach wcześniejszych niż PHP5):
$klasa = new mojaklasa();
foreach($klasa as $name => $value) {
// ...
}
Pozwalała jedynie na operowanie właściwościach klasy. PHP5 pozwala to zmienić i stworzyć własne sposoby obsługi iteracji.
Wbudowane interfejsy
PHP w wersji 5 posiada zestaw wbudowanych interfejsów, których implementacja w klasie pozwala na nadpisanie domyślnych sposobów iteracji:
Interfejs Traversable
Podstawowym interfejsem jest Traversable. Każda klasa implementująca ten interfejs może być użyta z konstrukcją foreach. Interfejs Traversable nie posiada żadnych właściwości ani metod (jest pusty), dlatego nie powinien on byc implementowany bezpośrednio. Zamiast niego zaleca się implementowanie interfejsu Iterator lub IteratorAggregate.
Interfejs Iterator
Iterator jest głównym interfejsem, który definiuje metody, jakie należy zaimplementować aby poprawnie móc używać klasy w konstrukcji foreach.
Ten wbudowany w PHP5 interfejs prezentuje się tak:
interface Iterator extends Traversable {
function current();
function key();
function next();
function rewind();
function valid();
}
Poniżej zamieszczam krótki opis jego metod:
- void rewind() - przewija listę iteratora na początek
- void next() - przechodzi do następnego elementu
- bool valid() - zwraca true jeśli istnieją jeszcze nieprzetworzone elementy
- mixed current() - zwraca wartość bieżącego elementu
- mixed key() - zwraca klucz bieżącego elementu
Interfejs IteratorAggregate
Klasa może obsłużyć iterację także na drugi sposób - implementując interfejs IteratorAggregate wyglądający tak:
interface IteratorAggregate implements Traversable {
function getIterator();
}
Interfejs ten posiada jedną metodę - Iterator getIterator(), która powinna tworzyć i zwracać obiekt iteratora. Interfejs ten jest szczególnie przydatny, gdy nie chcemy "mieszać" metod specyficznych danej klasy z metodami iteratora - pozwala nam on na odseparowanie kodu iteratora od kodu danej klasy.
W praktyce
Jako przykład zastosowania implementacji własnego iteratora w klasie zaprezentuję klasę do obsługi prostego katalogu stron.
Klasa ta zawiera kilka własnych metod, m.in. do dodawania do niej strony oraz do jej usuwania, poza tym implementuje ona interfejs Iterator, dzięki czemu przy jej pomocy możemy w łatwy sposób przeglądać dwa wybrane z niej pola - tytuł strony oraz jej adres.
Dane będą przechowywane w wielowymiarowej tablicy, choć oczywiście nic nie stoi na przeszkodzie, aby były przechowywane gdzie indziej. Nie chcąc jednak komplikować kodu, wybrałem właśnie tą metodę.
class webcatalog implements Iterator {
private $data=array(); // tablica do skladowania danych
private $ipointer=0; // pomocniczy wskaznik dla metod iteratora
/*
* Funkcja sluzaca do dodawania stron do katalogu
*/
public function addPage($url,$title,$mail,$description,$keywords) {
$this->data[]=array(
"url"=>$url,
"title"=>$title,
"mail"=>$mail,
"description"=>$description,
"keywords"=>$keywords
);
}
/*
* Funkcja do usuwania stron na podstawie adresu URL
*/
public function delPageByURL($url) {
foreach($this->data as $cnt => $item) {
if($item["url"]==$url) {
unset($this->data[$cnt]);
return true;
}
}
return false;
}
/*
* Przechodzimy do czesci wlasciwej, czyli implementacji iteratora
*/
public function current() {
return $this->data[$this->ipointer]["title"];
}
public function key() {
return $this->data[$this->ipointer]["url"];
}
public function next() {
$this->ipointer++;
}
public function rewind() {
$this->ipointer=0;
}
public function valid() {
return ($this->ipointer<sizeof($this->data));
}
}
Teraz spójrzmy jak nasza przykładowa klasa działa:
$wc=new webcatalog();
/*
* Dodajemy strony do naszego katalogu przy pomocy metody addPage
*/
$wc->addPage("http://luktom.net/","luktom online blog",
"Blog o sieciach komputerowych, routerach Cisco i webmasteringu",
"sieci komputerowe, cisco, routery, webmastering, php"
);
$wc->addPage("http://ccie.pl",
"Forum CCIE.pl",
"Forum CCIE.pl",
"cisco, ccna, ccnp, ccie, routery"
);
/*
* Dzieki implementacji interfejsu Iterator, mozemy ladnie wyswietlic
* dane, przy uzyciu konstrukcji foreach
*/
foreach($wc as $url => $title) {
print "$title ($url)<br/>";
}
Jak widać, iteratory są bardzo przyjemnym dodatkiem do codziennego programowania :)
Subskrybuj kanał RSS bloga
Amuuuuused ;)
Anyway, właściwość $ipointer można o kant dupy potłuc w tym przykładzie. ;) Zupełnie jest niepotrzebna. Spójrz na moją implementację: http://www.patternsforphp.com/wiki/Iterator#Implementating_an_Album_Iterator
Jest - eee ;P - chyba ździebko lepsza. ;)