Статьи

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

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

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

Итератор

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

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

 <?php class Library implements Iterator { // an internal pointer to the current position // in the dataset protected $position = 0; // an array of the titles of the books in the library protected $books = [ "Professional PHP Programming", "Programming Perl", "A Byte of Python", "The Ruby Way" ]; // this method takes the pointer back to the beginning // of the dataset to restart the iteration public function rewind() { echo "rewinding <br>"; $this->position = 0; } // this method returns the value at the current // position of the dataset public function current() { echo "current <br>"; return $this->books[$this->position]; } // this should return the current value of the pointer public function key() { echo "key <br>"; return $this->position; } // this method moves the pointer to the next value // in the data set public function next() { echo "next <br>"; ++$this->position; } / /this returns a boolean indicating if the there // is data at the current position in the dataset public function valid() { echo "valid <br>"; return isset($this->books[$this->position]); } } $library = new Library(); foreach ($library as $key => $value) { echo $key . ": " . $value . "<br>"; } 

Вывод вышеуказанного кода:

  перемотка 
 действительный 
 текущий 
 ключ 
 0: Профессиональное программирование на PHP
 следующий 
 действительный 
 текущий 
 ключ 
 1: программирование на Perl
 следующий 
 действительный 
 текущий 
 ключ 
 2: Байт Питона
 следующий 
 действительный 
 текущий 
 ключ 
 3: Рубиновый Путь
 следующий 
 действительный 

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

Iterator дает вам возможность создавать очень мощные итераторы с нуля и лучше всего подходит для создания внутренних итераторов, как описано выше. Конечно, приведенный выше пример прост и предназначен только для того, чтобы дать вам обзор необходимых методов и того, как они работают. В идеале, с набором данных, таким как приведенный выше (массив), вам будет лучше использовать IteratorAggregate .

IteratorAggregate

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

 <?php class Library implements IteratorAggregate { protected $books = [ "Professional PHP Programming", "Programming Perl", "A Byte of Python", "The Ruby Way" ]; // return an Iterator of your data public function getIterator() { echo "getIterator <br>"; return new ArrayIterator($this->books); } } $library = new Library(); foreach($library as $key => $value) { echo $key . ": " . $value . "<br>"; } 

Вывод вышеуказанного кода:

  getIterator 
 0: Профессиональное программирование на PHP
 1: программирование на Perl
 2: Байт Питона
 3: Рубиновый Путь 

В начале итерации PHP вызывает getIterator() который возвращает итератор, в ArrayIterator случае это SPL ArrayIterator . Затем PHP делает все соответствующие вызовы для этого итератора. По сути, вы передаете данные внешнему итератору, который выполняет всю работу за вас, пока вы расслабляетесь.

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

Резюме

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

Изображение через Mushakesa / Shutterstock