Базовые понятия
Итераторы (Iterators) — это классы которые реализуют интерфейс Iterator. Реализация данного интерфейса необходима при организации доступа к циклическим структурам (массивы, списки и т.д.).
В () существует большое количество готовых классов, реализующих интерфейс Iterator.
Далее перечислены классы, которые непосредственно реализуют интерфейс Iterator: Traversable, IteratorAgregate, OuterIterator, RecursiveIterator. Остальные классы-итераторы SPL наследуют свои интерфейсы уже от них.
Не смотря на все обилие существующих классов иногда их оказывается недостаточно для решения поставленной задачи, тогда программист может создать свои собственные классы реализующие интерфейс Iterator.
Так как интерфейс Iterator входит в библиотеку стандартных классов PHP (Standard PHP Library), то его реализация выполнена в исходниках самого интерпретатора (имеются в виду коды программы на языке «С»). Если выразить этот интерфейс на языке PHP, то он будет выглядеть так:
interface Iterator{
public function current(); // возвращает значение текущего элемента
public function key(); // возвращает значения текущего ключа элемента (индекс массива например)
public function next(); // смещает указатель на следующий элемент
public function rewind(); // смещает указатель на первый элемент
public function valid(); // определяет наличие элемента (выполняется после next или rewind)
}
Итераторы могут быть использованы во многих областях программирования. Например, при обработке набора значений полученных из Базы данных или обработке значений массива, или обработке набора файлов и т.д.
Вспомогательные функции при работе с итераторами.
iterator_to_array(Iterator $iteratorObject) — возвращает содержимое объекта-итератора в виде массива.
iterator_count(Iterator $iteratorObject)) — возвращает количество элементов в объекте-итераторе.
Пример использования:
$a = Array('a', 'b', 'c');
$iterator = new ArrayIterator($a);
echo iterator_count($iterator); // Результат - 3
iterator_apply(Iterator iteratorObject, function callback, [user_data) — данная функция позволяет с помощью callback-а обработать каждый элемент содержащийся в объекте-итераторе.
Пример использования:
function print_entry($iteratorObject) {
var_dump($iterator->current());
return true;
}
$a = Array('a', 'b', 'c');
$iterator = new ArrayIterator($a);
iterator_apply($iterator, 'print_entry', array($iterator));
Если функция callback возвращает значение отличное от true перебор элементов объекта-итератора прекращается.
В старых версиях PHP для работы с файлами и каталогами предусмотрены такие функции как open_dir, read_dir и т.д. Начиная с 5-ой версии PHP для работы с файловой структурой намного удобнее использовать классы (). Для наглядности предлагаю разобрать небольшой пример.
Допустим, нам необходимо просканировать дерево каталогов и найти в них все файлы. Затем полученный список нужно отсортировать в алфавитном порядке. Вот как эту задачу можно решить используя SPL:
$dirTree = new RecursiveDirectoryIterator("./testDir");
$sortedFileList = new FileSorterIterator(new RecursiveItertatorIterator($dirTree));
foreach($sortedFileList as $file) {
echo $file->getFilename() . '<br />';
}
class FileSorterIterator extends SplHeap
{
public function __constructor(Iterator $iterator) {
foreach ($iterator as $item) {
if ($item->isFile()) {
// отсеиваем директории,
// так как в условиях нашей задачи нужно получить только список файлов
$this->insert($item);
}
}
}
public function compare($b,$a)
{
return strcmp($a->getFilename(), $b->getFilename());
}
}
Как видите, пример получился очень простым. Если в старых версиях PHP приходилось самостоятельно отслеживать указатель на открытую директорию и своевременно его закрывать, то теперь всю «грязную» работу берут на себя классы SPL.
Теперь давайте разберемся какие классы SPL используются в примере:
Класс RecursiveDirectoryIterator
RecursiveDirectoryIterator — это класс SPL который рекурсивно просматривает заданную директорию и составляет дерево всех файлов и директорий. При создании класса нужно указать директорию в которой должен осуществляться поиск.
Для того, чтобы работать с построенным деревом используются стандартные функции интерфейса , а так же несколько .
При работе с данным классом нужно помнить, что функции на подобие foreach перебирают только элементы файловой структуры, находящиеся в корне построенного дерева. Для того, чтобы перебрать элементы расположенные ниже по структуре (имеются в виду вложенные директории) следует использовать специальный класс RecursiveIteratorIterator.
Класс RecursiveIteratorIterator
RecuriveIteratorIterator — это класс созданный для последовательно перебора элементов древовидных итераторов. В нашем примере данный класс используется для того, чтобы обработать файлы расположенные в поддиректориях корневой директории.
Класс FileSorterIterator
FileSorterIterator — это пользовательский класс созданный на базе класса . Данный класс нужен для того, чтобы отсортировать файлы в алфавитном порядке. Для этого, используется функция compare, которая содержит условие сортировки, исходя из которого и определяется место элемента в общем массиве.
Исключительные ситуации
Теперь, хочется пару слов сказать о том, что делать если в результате работы указанных классов возникает какая-то ошибка. Например, мы указали для сканирования несуществующую директорию:
$dirTree = new RecursiveDirectoryIterator("./noneExistDirectory");
При возникновении ошибки в работе SPL классов генерируется соответствующее исключение, в случае если эти исключения не обрабатываются, они приводят к останову работы программы в целом. Поэтому лучше предусмотреть обработку наиболее вероятных исключительных ситуаций в программе.
Исключительные ситуации обрабатываются следующим образом:
try{
$dirTree = new RecursiveDirectoryIterator("./testDasdfsdf");
}catch(Exception $e) {
var_dump($e->getMessage());
}
В примере вся обработка сводится к выводу сообщения о полученном исключении на экран — RecursiveDirectoryIterator::__construct(./testDasdfsdf): failed to open dir: No such file or directory. В реальном приложении такая обработка, естественно, не подходит, поэтому вместо вывода сообщений на экран стоит придумать, что-то посерьезнее.
SPL — это набор стандартных библиотек PHP (Standard PHP Library) появившихся начиная с 5-ой версии PHP.
Если рассматривать SPL с позиции разработчика, то легко выделить шесть основных классов и интерфейсов, функционал которых расширяется в большом множестве классов-потомков.
Класс ArrayAccess
ArrayAccess — это интерфейс который позволяет создать классы которые будут вести себя как массивы. В других языках эта возможность наиболее часто предоставляется с помощью интерфейсов-счетчиков (indexer).
Класс Exception;
Exception — это родительский класс SPL для большого количества классов-потомков (LogicExeption, DomainEception, RangeException и т.д.), которые «выбрасываются» при наступлении той или иной исключительной ситуации.
Класс Iterator
Iterator — это интерфейс SPL который предназначен для реализации объектов с циклической структурой. Реализация данного интерфейса в разрабатываемых классах позволяет стандартным функциям PHP (например foreach) работать с пользовательскими объектами.
Класс IteratorAgregate
IteratorAgregate — это интерфейс SPL который содержит только один метод — «getIterator». Этот интерфейс позволяет не реализовывать в классе интерфейс «Itertator», а использовать вместо этого внешний итератор.
Класс Serializable
Serializable — это интерфейс который позволяет сериализовать любой объект с помощью функций Serialize и Unserrialize
Класс Traversable
Traversable — это служебный класс SPL который используется в интерфейсах Iterator и IteratorAgregate. Реализовать данный интерфейс в PHP-программе нельзя т.к. он реализован на урове C-кода интерпретатора PHP.
Кроме этого есть несколько других возможностей таких как шаблон проектирования «observer», вспомогательные функции для идентификации объекта и для работы с итераторами. Так же есть функционал позволяющий автоматизировать процесс загрузки классов и интерфейсов.