Использование итераторов SPL в PHP, часть 2






В предыдущей статье мы рассмотрели некоторые итераторы SPL и принцип работы с ними. В реальной жизни вы можете столкнуться с ситуацией, когда функциональности SPL-итераторов вам может не хватить и вам потребуется создать свой собственный класс-итератор. К счастью, сложностей здесь никаких не существует, благодаря интерфейсам SPL.

Для того, чтобы объект мог быть итерирован в цикле foreach, для PHP необходимо, чтобы этот объект реализовывал интерфейс Traversable. Однако, вы не можете реализовывать этот интерфейс непосредственно, и вместо этого должны использовать любой из двух дочерних интерфейсов: Iterator или IteratorAggregate.

Итератор

Iterator

Интерфейс Iterator предназначен для реализации классами, объекты которых могут быть интерированы непосредственно или использоваться для создания внешних итераторов. Интерфейс определяет пять методов для реализации: rewind(), current(), key(), next() и valid().

Предположим, у нас есть библиотека книг и мы хотим иметь возможность выполнять итерацию по всей коллекции книг. Ниже приведён пример класса, реализующего Iterator, который решает подобную задачу:

Вывод кода будет следующим:

Перед началом итерации PHP вызывает метод rewind(), чтобы установить указатель на начало данных. Затем при помощи вызова метода valid() он проверяет, есть ли данные по текущей позиции указателя. Если в ответ на вызов этого метода PHP получает true, то следующим шагом будет вызван метод current(), который вернёт данные, находящие относительно текущей позиции указателя. Если вы запросите ключ в конструкции foreach, то интерпретатор будет обращаться к методу key() вашего класса, чтобы получить его. Таким образом, итерация будет продолжнать до тех пор, пока объект не вернёт false в ответ на вызов метода valid().

Iterator позволяет создавать итераторы «с нуля» и отлично подходит для решения задач, вроде той, что мы только что рассмотрели. Конечно, приведённый пример очень простой, но его задачей как раз и является в простой и наглядной форме показать вам основные принципы работы класса Iterator. Однако, более лучшим решением для работы с данными вроде тех, что приведены в примере выше (обычный массив), будет другой класс — IteratorAggregate.

IteratorAggregate

IteratorAggregate требует реализации лишь одного метода, getIterator(). Этот метод должен возвращать внешний итератор, который и будет использоваться для выполнения итераций. Переписав наш пример с использованием IteratorAggregate, получим следующее:

В результате выполнения получим:

Перед началом итераций интерпретатор PHP вызывает метод getIterator(), который возвращает Iterator; в нашем случае это ArrayIterator. А дальше работа происходит уже с полученным итератором таким образом, как это описано выше. То есть, вы передаёте свои данные внешнему итератору, он делает всю работу, а вы спокойно курите в сторонке.

Поскольку оба интерфейса выполняют одну и ту же задачу, иногда бывает трудно определиться, что же нужно использовать в конкретной ситуации. Правило большого пальца гласит: используйте Iterator тогда, когда вам необходимо создать сложный итератор «с нуля», с вашим собственным блэкджеком и куртизанками. В остальных случаях не морочьте голову и пользуйтесь готовыми SPL итераторами, заворачивая в них данные и реализуйте интерфейс IteratorAggregate.

Итоги

В этой статье мы рассмотрели два SPL-интерфейса. предназначенных для создания собственных классов-итераторов. Я надеюсь, что вы смогли разобраться, понять всю простоту принципов работы итераторов и вскоре начнёте применять полученные знания в ваших проектах.

Источник: SitePoint