PHP: Własna klasa implementująca Iterator i Countable
Wczoraj, przy okazji kowersji stdClass do array, wspomniałem o interfejsie Iterator. Dzisiaj chciałem pokazać jak stworzyć w pełni sprawny "pojemnik" na dane korzystając z interfejsu Countable i Iterator.
Iterator
Interfejs Iterator zakłada definicję pięciu metod w klasie która ten interfejs implementuje. Są to:
- current - zwraca wartość elementu, na który jest ustawiony wskaźnik
- next - przesuwa wskaźnik o jedną pozycję do przodu
- rewind - ustawia wskaźnik na pierwszy element
- valid - sprawdza, czy element, na który ustawiony jest wskaźnik, istnieje
- key - zwraca wartość wskaźnika
Jeżeli stworzymy klasę, której metody będą działały tak jak w moich opisach, to stworzymy kopię klasy stdClass, która istnieje w PHP. Dzięki interfejsowi Iterator możemy stworzyć klasę, która przy użyciu pętli foreach będzie zwracała elementy od ostatniego do pierwszego.
Countable
Interfejs Countable zakłada, że klasa go implementująca będzie posiadała metodę count wywoływaną bez parametrów, zwracającą typ integer. Metoda ta jest wywoływana podczas użycia obiektu takiej klasy jako parametru funkcji count (nie metody!).
Dlaczego mam korzystać z interfejsów?
Wiele osób na pewno zada to pytanie. Odpowiedź jest banalna. Jeżeli klasa implementuje jakiś interfejs, to musi zawierać wszystkie właściwości i metody, które zostały zdefiniowane w tym interfejsie. W przeciwnym wypadku interpreter przerwie wykonywanie kodu. Pętla foreach przyjmuje tylko obiekty, które implementują interfejs Iterator. Dzięki temu ma pewność, że obiekt posiada wszystkie potrzebne jej metody (valid, current, next itd.). W końcu dzięki nim może swobodnie poruszać się po kolekcji.
Jeśli sami chcemy żeby nasze funkcje jako parametry przyjmowały tylko obiekty, które implementują np. interfejs Iterator, musimy podać go jako typ danych:
<?php
function ile(Countable $object) {
return $object->count();
}
?>
Powyższy kod prezentuje funkcję, która działa dokładnie tak samo jak wbudowana w język PHP funkcja count.
Klasa MyBox
Tak więc teraz, korzystając z dwóch interfejsów, które przed chwilą opisałem, stworzymy klasę, która będzie pojemnikiem na dane. Przy przeglądaniu tego obiektu w pętli foreach dane mają być ułożone od ostatniego do pierwszego.
W tym celu przy dodawaniu kolejnej wartości do naszego magazynu, wskaźnik będzie zwiększany o jeden, aby zawierał indeks najnowszej wartości (czyli ostatniej, a dla pętli foreach - pierwszej). Metoda next będzie natomiast wskaźnik zmniejszała o jeden.
Reszta metod definiujemy tak, jak to opisałem wcześniej.
Gotową klasę możemy zobaczyć na listingu nr 1. Skrypt, który ją testuje znajduję się na listingu nr 2.
Demo działania znajdziecie tutaj.