php spl что это
Standard PHP Library (SPL) — Часть 1: Структуры данных
Привет, Хабр! В данной статье речь пойдет про Standard PHP Library (SPL). На хабре до сих пор нет толкового мануала об этой библиотеке, которая уже стала частью ядра PHP (с версии 5.3). Данная библиотека содержит набор интерфейсов, классов структур данных, итераторов и функций, с помощью которых можно значительно упростить себе жизнь и повысить качество кода. В данной статье я рассматриваю такую часть библиотеки, как структуры данных. Также я покажу альтернативные решения поставленных задач и сравню скорость выполнения в обоих случаях.
Итак. Прежде всего хочу дать ссылку на официальную документацию: php.net/manual/en/book.spl.php
В библиотеке SPL содержатся такие структуры данных:
Рассмотри по порядку каждую из структур.
SplDoublyLinkedList
Двусвязный список (DLL) — это список узлов, связанных в обоих направлениях друг между другом. Как известно, есть два принципа извлечения значения из списка – FIFO (First In First Out – первый зашел, первый ушел) и LIFO (Last In First Out – последний зашел, первый ушел). С помощью SplDoublyLinkedList можно извлекать значения по любому из этих принципов. Следовательно, с его помощью можно легко организовать стек или очередь.
SplStack
Данный класс является наследником SplDoublyLinkedList и реализует стек, например:
Ранее для создания мы использовали процедурный способ, а именно использовали функции array_push – добавление элементов в конец массива и array_pop – извлечение последнего элемента. Теперь мы работаем с объектами.
Сравним быстродействие двух способов. Тестовые условия: PHP 5.3.18, Core 2 Duo P7350, Windows. Добавляем в стек число от 1 до n и извлекаем все из стека.
Количество push и pop | Использование функций | Использование SplStack | Отношение |
---|---|---|---|
1000 | 0.007686 | 0.008559 | 0,898002 |
100000 | 0.793184 | 0.884979 | 0,896274375 |
В данном тесте способ с использованием функций сработал быстрее примерно на 10-15%.
Ради интереса провел еще тест в PHP 5.4.8
Количество push и pop | Использование функций | Использование SplStack | Отношение |
---|---|---|---|
1000 | 0.008186 | 0.008735 | 0,937149399 |
100000 | 0.732347 | 0.771456 | 0,949304951 |
По этому тесту можно увидеть, что PHP 5.4.8 быстрее чем PHP 5.3.18 при работе со стеком примерно на 10% и также улучшена работа с объектами. Поэтому все последующие тесты я буду проводить на этой версии PHP.
Однако если добавлять в стек более сложные объекты, то разница между результатами уже на уровне погрешности.
В этом тесте мы добавляли и извлекали из стека следующий объект:
Количество push и pop | Использование функций | Использование SplStack | Отношение |
---|---|---|---|
1000 | 0.007974 | 0.008301 | 0,960607156 |
100000 | 0.818596 | 0.826363 | 0,990600983 |
В реальных приложениях объекты будут намного сложнее, поэтому смею предположить, что значительный перевес будет на стороне метода из SPL.
SplQueue
Данная структура используется для создания очередей. Все аналогично стеку, рассмотрим лишь небольшой пример:
SplHeap
Кучи являются древовидными структурами: каждый узел больше или равен своим потомкам, при этом для сравнения используется внедренный метод сравнения, который является общим для всей кучи. SplHeap реализует основную функциональность кучи и является абстрактным классом.
SplMaxHeap и SplMinHeap
От SplHeap наследуются два класса: SplMaxHeap – для сортировки массива по убыванию его значений, SplMinHeap – для сортировки массива по возрастанию.
SplPriorityQueue
Данная структура представляет собой очередь с приоритетами. Для каждого элемента можно задать его приоритет. Сортировка производится в зависимости от приоритета.
SplFixedArray
Структура представляет собой массив с фиксированным количеством элементов. SplFixedArray хранит данные в непрерывном виде, доступные через индексы, а обычные массивы реализованы в виде упорядоченных хэш-таблиц. Данный вид массива работает быстрее, чем обычные массивы, но существуют некоторые ограничении:
Данная структура хорошо подходит для нумерованных списков. Давайте рассмотрим пример и проведем тесты:
Количество push и pop | Обычный массив | Использование SplFixedArray | Отношение |
---|---|---|---|
100 | 8.2 х 10E-5 | 6.3 х 10E-5 | 1,301587301 |
10000 | 0.004953 | 0.003983 | 1,243535024 |
100000 | 0.053586 | 0.0385701 | 1,389314521 |
1000000 | 0.533003 | 0.384391 | 1,386616752 |
Тесты подтвердили, что при любом заранее известном количестве элементов массива лидирует SplFixedArray. Однако если в процессе размер массива изменяется, то преимущество становится менее существенным: (первоначально размер был задан 10000, потом расширен до 100000):
Количество push и pop | Обычный массив | Использование SplFixedArray | Отношение |
---|---|---|---|
1000000 | 0.051937 | 0.049462 | 1,050038413 |
SplObjectStorage
Данная структура представляет собой хранилище объектов. Объекты можно прикреплять к хранилищу, удалять, получать текущий объект. Рассмотрим несколько примеров с оффициального мануала:
На этом мы закончили изучать структуры данных SPL. Мы научились быстро создавать стек, очередь и списки. Мы теперь знаем о SplFixedArray, который работает быстрей чем обычный массив. Однако это очень маленькая часть применения данной библиотеки. В следующих статьях будут рассмотрены итераторы, интерфейсы, функции и обработка исключений.
Какой могла бы быть Standard PHP Library
Каждая статья с критикой PHP, следом за объяснением почему «$» в обозначении переменной портит синтаксис, обязательно расскажет вам о том, как плохо организовано именование функций в ядре PHP и расширениях. И если «$» останется в стандарте языка и наших сердцах навсегда (хотя бы просто как напоминание о старом-добром PHP), то с именованием функций давно пора что-то делать.
Автор предлагает свои правила именования функций ядра PHP и распределение их по пространствам имен.
Standard PHP Library
Прежде всего, расскажу немного о той терминологической путанице, которую я намеренно ввожу в данной статье.
Standard PHP Library (SPL) уже существует в качестве отдельного расширения. SPL предоставляет структуры данных, исключения, итераторы и многое другое, но к базовым функциям, например к функциям для работы со строками, никакого отношения не имеет. Они, на мой далёкий от устройства интерпретатора PHP взгляд, просто существуют где-то в ядре и являются как бы частью стандарта языка.
Я считаю, что базовые функции следует выделить в отдельное расширение и самым подходящим для него названием стало бы именно Standard PHP Library.
Проблемы
Недостатков в именовании функций и, в целом, в их организации много. Хрестоматийным примером являются функции для работы со строками, поэтому далее рассматривать буду именно их.
Наиболее заметные проблемы:
одновременно существуют несколько подходов к именованию: с префиксом «str_» (str_split), с префиксом «str» (strrev), с префиксом «substr_» (substr_compare), без какого-либо префикса (trim);
однородные функции имеют аргументы с разными именами, например, в strncmp два аргумента: «str1» и «str2», в strnatcasecmp: «string1» и «string2», а в substr_compare: «haystack» и «needle»;
однородные функции имеют разный порядок следования однородных же аргументов — все помнят историю про порядок аргументов в implode и explode;
совершенно нечитаемые и непроизносимые имена функций: strtr, strncmp, strpbrk, strrchr и т.д.;
некоторые функции могут менять тип возвращаемого значения в зависимости от типов переданных им аргументов, например, substr_replace непринужденно жонглирует массивами и строками;
зачастую функции возвращают false в случае ошибки или неверно заданных аргументов, например, strrpos;
некоторые функции выглядят довольно экзотично и используются крайне редко: soundex, levenshtein и т.д.
Причины
Не берусь утверждать, что я точно знаю чем это вызвано. Хотя уверен, что мое предположение будет недалеко от истины. Все ошибки на проектах, застигнутых внезапной лавинообразной популярностью, объединяет одна причина — «так исторически сложилось».
Никто не мог предположить, что продвинутый html-шаблонизатор станет едва ли не самым популярным языком для создания сайтов. А когда это все же случилось, то оказалось, что экосистема в десятки миллионов строк кода с большой неприязнью относится к изменениям интерпретатора, ломающим обратную совместимость.
Я прекрасно понимаю, что функции есть и они успешно работают в огромном множестве проектов, а эстетика написания кода — дело десятое. Более того, я понимаю, что взять на себя ответственность за решение, которое, вероятно, оттолкнет от PHP часть старых и новых пользователей, довольно сложно. Так есть ли шансы? Я вижу некоторые робкие шаги в этом направлении, но пока нет ничего, что попало бы в релиз.
Как бы это сделал я.
PHP 8.2 — создать и принять новый регламент об именовании функций и их аргументов;
PHP 8.3 — добавить новые функции согласно регламента, старые оставить в качестве псевдонимов, выпустить инструмент, который преобразует старые вызовы в новые;
PHP 9 — объявить старые функции deprecated, вынести новые и старые функции в отдельные расширения, при этом legacy расширение по умолчанию не будет идти в составе сборки,
PHP 10 — end of life для legacy расширения.
Учитывая, что между 7 и 8 версиями пролетело каких-то 5 лет, я думаю, что 10 лет экосистеме хватит на переход.
Что я предлагаю
Выделить функции из состава ядра в отдельные расширения
Отдельные расширения проще тестировать и их релизы можно выпускать отдельно от больших релизов языка. Здесь я ничего нового не скажу — преимущества модульности давно известны.
Добавить пространства имен для расширений
Да, пространства имен нужны и для ядра, и для расширений. Довольно странно создавать стандарты, которые будут различаться для пользовательского кода и кода, предоставленного создателями языка. И если для ядра я еще могу представить корневое «\», то для расширений без них никак не обойтись.
Я не буду выдумывать что-то новое и просто использую PSR-4. В качестве имени вендора очевидно следует использовать «Php», а дальше добавить имя расширения. Таким образом, будущие функции стандартной библиотеки будут в пространстве имен «Php\Spl».
Договориться о правилах именования функций и аргументов
Снова предлагаю существующие стандарты: частично использовать PSR-12 и частично правила именования функций от дядюшки Боба.
Имена функций не должны содержать какого-либо явного префикса расширения, в котором определены. Иными словами, не должно быть никаких «str_» или «str», для этих целей служит пространство имен.
Имена функций и аргументов должны быть заданы в lowerCamelCase.
Имена функций должны быть читаемыми и содержать в себе явное описание выполняемого действия. Уместно использовать для этого некий глагол.
Имена аргументов также должны быть читаемыми и содержать в себе явное описание передаваемых данных. Уместно использовать в качестве имен аргументов существительные.
Порядок и имена аргументов для однородных функций, например для функций сравнения строк, должны совпадать.
Включить строгую проверку типов данных
Тут давно пора навести порядок. Например sscanf может возвращать массив, целочисленный результат или null. Я уверен, что взрослый язык может позволить себе согласовать типы аргументов и возвращаемых значений для функций стандартной библиотеки.
Я во многом полагаюсь на подсказки IDE и на статический анализ, а непредсказуемые типы сильно этому мешают и создают дополнительный шум в виде явных кастов или условных выражений.
Функции стандартной библиотеки должны использовать строгую проверку типов аргументов и возвращаемых значений.
Для аргументов и для возвращаемых значений должны быть явно указаны типы. Использование «mixed» недопустимо.
Для аргументов допустимо использовать union-типы и nullable-типы.
Возвращаемое значение должно иметь один явный тип или быть «null» там, где это оправданно.
В случае исключительной ситуации или неверно заданных аргументов функция должна выбрасывать одно из стандартных исключений с четким описанием проблемы или ошибки.
Практическая реализация
В качестве proof of concept я создал библиотеку, которая следует всем правилам, указанным выше, и предлагает новые имена функций для работы со строками — https://github.com/marvin255/php-spl-proposal/blob/master/src/StringUtilities.php.
Первый и главный вопрос реализации — использовать отдельные функции в пространстве имен или статические методы класса.
Я решил использовать статические методы класса. Возможно, это не очень изящное решение, зато крайне удобное в быту: весь набор функций импортируется одной короткой строкой, с помощью IDE можно с легкостью посмотреть весь список функций только для работы со строками, механизм наследования пригодится для переопределения методов в фрэймворках и cms.
Пара примеров для ознакомления.
Символов стало больше и это может показаться минусом, но, на мой взгляд, в современном интерпретируемом языке читаемость метода важнее мнимой экономии. Соглашусь, что имена новых функций не везде подобраны удачно, но не судите строго — в конце концов это одна из главных проблем программирования.
Интересно, что первоначально я использовал json файл, задающий соответствия между новыми именами и старыми, и специальный генератор для создания статических методов. К сожалению, он превратился в жуткого монстра, который молит о рефакторинге — так много было различных исключений и условий при конвертации. Решил его не публиковать.
Вместо заключения
Статья не претендует на какую-либо объективность. Более того, очевидно, что я не смог за пару вечеров исправить один из коренных недостатков PHP.
Я вижу как данную проблему из релиза в релиз обходят стороной в погоне за новыми фичами и синтаксическим сахаром. Не подумайте, я не имею ничего против изменений, введенных в PHP 7 и 8, но и для возвращения технического долга нужно найти время.
Своей статьей я хотел лишь спровоцировать диалог и заронить семена сомнений. Кто-то должен этим заняться, и я уверен, что именно сообщество должно подтолкнуть мэйнтейнеров к мысли о неизбежности этих изменений.
Standard PHP Library (SPL) — Часть 1: Структуры данных
Привет, Хабр! В данной статье речь пойдет про Standard PHP Library (SPL). На хабре до сих пор нет толкового мануала об этой библиотеке, которая уже стала частью ядра PHP (с версии 5.3). Данная библиотека содержит набор интерфейсов, классов структур данных, итераторов и функций, с помощью которых можно значительно упростить себе жизнь и повысить качество кода. В данной статье я рассматриваю такую часть библиотеки, как структуры данных. Также я покажу альтернативные решения поставленных задач и сравню скорость выполнения в обоих случаях.
Итак. Прежде всего хочу дать ссылку на официальную документацию: php.net/manual/en/book.spl.php
В библиотеке SPL содержатся такие структуры данных:
Рассмотри по порядку каждую из структур.
SplDoublyLinkedList
Двусвязный список (DLL) — это список узлов, связанных в обоих направлениях друг между другом. Как известно, есть два принципа извлечения значения из списка – FIFO (First In First Out – первый зашел, первый ушел) и LIFO (Last In First Out – последний зашел, первый ушел). С помощью SplDoublyLinkedList можно извлекать значения по любому из этих принципов. Следовательно, с его помощью можно легко организовать стек или очередь.
SplStack
Данный класс является наследником SplDoublyLinkedList и реализует стек, например:
Ранее для создания мы использовали процедурный способ, а именно использовали функции array_push – добавление элементов в конец массива и array_pop – извлечение последнего элемента. Теперь мы работаем с объектами.
Сравним быстродействие двух способов. Тестовые условия: PHP 5.3.18, Core 2 Duo P7350, Windows. Добавляем в стек число от 1 до n и извлекаем все из стека.
Количество push и pop | Использование функций | Использование SplStack | Отношение |
---|---|---|---|
1000 | 0.007686 | 0.008559 | 0,898002 |
100000 | 0.793184 | 0.884979 | 0,896274375 |
В данном тесте способ с использованием функций сработал быстрее примерно на 10-15%.
Ради интереса провел еще тест в PHP 5.4.8
Количество push и pop | Использование функций | Использование SplStack | Отношение |
---|---|---|---|
1000 | 0.008186 | 0.008735 | 0,937149399 |
100000 | 0.732347 | 0.771456 | 0,949304951 |
По этому тесту можно увидеть, что PHP 5.4.8 быстрее чем PHP 5.3.18 при работе со стеком примерно на 10% и также улучшена работа с объектами. Поэтому все последующие тесты я буду проводить на этой версии PHP.
Однако если добавлять в стек более сложные объекты, то разница между результатами уже на уровне погрешности.
В этом тесте мы добавляли и извлекали из стека следующий объект:
Количество push и pop | Использование функций | Использование SplStack | Отношение |
---|---|---|---|
1000 | 0.007974 | 0.008301 | 0,960607156 |
100000 | 0.818596 | 0.826363 | 0,990600983 |
В реальных приложениях объекты будут намного сложнее, поэтому смею предположить, что значительный перевес будет на стороне метода из SPL.
SplQueue
Данная структура используется для создания очередей. Все аналогично стеку, рассмотрим лишь небольшой пример:
SplHeap
Кучи являются древовидными структурами: каждый узел больше или равен своим потомкам, при этом для сравнения используется внедренный метод сравнения, который является общим для всей кучи. SplHeap реализует основную функциональность кучи и является абстрактным классом.
SplMaxHeap и SplMinHeap
От SplHeap наследуются два класса: SplMaxHeap – для сортировки массива по убыванию его значений, SplMinHeap – для сортировки массива по возрастанию.
SplPriorityQueue
Данная структура представляет собой очередь с приоритетами. Для каждого элемента можно задать его приоритет. Сортировка производится в зависимости от приоритета.
SplFixedArray
Структура представляет собой массив с фиксированным количеством элементов. SplFixedArray хранит данные в непрерывном виде, доступные через индексы, а обычные массивы реализованы в виде упорядоченных хэш-таблиц. Данный вид массива работает быстрее, чем обычные массивы, но существуют некоторые ограничении:
Данная структура хорошо подходит для нумерованных списков. Давайте рассмотрим пример и проведем тесты:
Количество push и pop | Обычный массив | Использование SplFixedArray | Отношение |
---|---|---|---|
100 | 8.2 х 10E-5 | 6.3 х 10E-5 | 1,301587301 |
10000 | 0.004953 | 0.003983 | 1,243535024 |
100000 | 0.053586 | 0.0385701 | 1,389314521 |
1000000 | 0.533003 | 0.384391 | 1,386616752 |
Тесты подтвердили, что при любом заранее известном количестве элементов массива лидирует SplFixedArray. Однако если в процессе размер массива изменяется, то преимущество становится менее существенным: (первоначально размер был задан 10000, потом расширен до 100000):
Количество push и pop | Обычный массив | Использование SplFixedArray | Отношение |
---|---|---|---|
1000000 | 0.051937 | 0.049462 | 1,050038413 |
SplObjectStorage
Данная структура представляет собой хранилище объектов. Объекты можно прикреплять к хранилищу, удалять, получать текущий объект. Рассмотрим несколько примеров с оффициального мануала:
На этом мы закончили изучать структуры данных SPL. Мы научились быстро создавать стек, очередь и списки. Мы теперь знаем о SplFixedArray, который работает быстрей чем обычный массив. Однако это очень маленькая часть применения данной библиотеки. В следующих статьях будут рассмотрены итераторы, интерфейсы, функции и обработка исключений.
Standard PHP Library: взгляд изнутри
Сегодня речь пойдёт о встроенной в PHP библиотеке SPL. В сети интернет достаточно много справочной информации по разным частям библиотеки. Я решил свести всё воедино. Получилась, этакая, лекция-дайджест.
1. Что такое SPL?
Стандартная библиотека PHP (SPL) — это набор интерфейсов и классов, предназначенных для решения стандартных задач. Не требуется никаких внешних библиотек для сборки этого расширения, и оно доступно по умолчанию в PHP 5.0.0 и выше.
2. Структуры данных в SPL
2.1. Двусвязный список ( DLL )
Вообще, связный список — это базовая динамическая структура данных, состоящая из узлов, каждый из которых содержит как данные, так и ссылки на следующий и/или предыдущий узел списка.
Зачем они нужны? Их преимущество перед массивом — это структурная гибкость: порядок элементов может не совпадать с порядком расположения данных в памяти, а обход всегда явно задаётся внутренними связями. Списки бывают трёх типов: одно-, дву- и XOR-связные.
Линейный однонаправленный список — это структура данных, состоящая из элементов одного типа, связанных между собой последовательно посредством указателей. Каждый элемент списка имеет указатель на следующий элемент. Последний элемент списка указывает на NULL. Элемент, на который нет указателя, является первым (головным) элементом списка. Здесь ссылка в каждом узле указывает на следующий узел в списке. В односвязном списке можно передвигаться только в сторону конца списка. Узнать адрес предыдущего элемента, опираясь на содержимое текущего узла, невозможно.
Двусвязный список — это список, ссылки в каждом узле которого указывают на предыдущий и на последующий узел. По двусвязному списку можно эффективно передвигаться в любом направлении — как к началу, так и к концу. В этом списке проще производить удаление и перестановку элементов, так как легко доступны адреса тех элементов списка, указатели которых направлены на изменяемый элемент.
XOR-связный список — структура данных, похожая на обычный двусвязный список, однако в каждом элементе хранящая только один адрес — результат выполнения операции XOR над адресами предыдущего и следующего элементов списка. Чтобы перемещаться по списку, необходимо взять два последовательных адреса и выполнить над ними операцию XOR, которая и даст реальный адрес следующего элемента.
Мы же рассмотрим встроенную в SPL структуру данных — двусвязный список. В SPL он является основой для двух крайне важных и интересных структур. Это стэк и очередь.
Сам класс обеспечивает основные возможности двусвязного списка. Не секрет, что есть два принципа работы с элементами списка — FIFO и LIFO. В DLL можно работать по любому из этих принципов. Это и позволяет реализовывать стэк и очередь.
Все операции итератора, удаления, добавления и т. п. имеют алгоритмическую сложность O(1), что является очень дешёвой операцией.
Направление итерации (одно из двух): — SplDoublyLinkedList::IT_MODE_LIFO (стэк); — SplDoublyLinkedList::IT_MODE_FIFO (очередь).
2.1.1. SplStack
SplStack — это наследник DLL, реализующий структуру стэка. Стэк — это абстрактный тип данных, представляющий собой список элементов, организованных по принципу LIFO.
Вот пример реализации простого стэка:
Наследует основные операции от DLL.
2.1.2. SplQueue
SplQueue – структура для создания очереди. Очередь — абстрактный тип данных с дисциплиной доступа к элементам FIFO. Добавление элемента (принято обозначать словом enqueue — поставить в очередь) возможно лишь в конец очереди, выборка — только из начала очереди (что принято называть словом dequeue — убрать из очереди), при этом выбранный элемент из очереди удаляется.
По сути – то же самое, что стэк, только FIFO:
2.2. SplHeap
SplHeap — древовидная структура: каждый узел больше или равен своим потомкам, при этом для сравнения используется внедренный метод сравнения, который является общим для всей кучи. SplHeap реализует основную функциональность кучи и является абстрактным классом. От SplHeap наследуются два класса: SplMaxHeap – для сортировки массива по убыванию его значений, SplMinHeap – для сортировки массива по возрастанию.
2.3. SplPriorityQueue
Обеспечивает основные функциональные возможности приоритетной очереди, реализованный при помощи кучи (max-heap). Сортировка производится в зависимости от приоритета. Далее следует обычный алгоритм вытаскивания элементов из структуры.
2.4. SplFixedArray
Представляет собой массив с фиксированным количеством элементов. SplFixedArray хранит данные в непрерывном виде, доступные через индексы, а обычные массивы реализованы в виде упорядоченных хэш-таблиц. Данный вид массива работает быстрее, чем обычные массивы, но существуют некоторые ограничения: 1) в качестве ключей могут быть только целые числа > 0; 2) длина может быть изменена, но это затратная операция.
Тут стоит уделить внимание тому, как же реализованы массивы в PHP – именно те, с которыми мы обычно работаем. На уровне PHP, массив — это упорядоченный список, скрещенный с мэпом. Грубо говоря, PHP смешивает эти два понятия, в итоге получается, с одной стороны, очень гибкая структура данных, с другой стороны, возможно, не самая оптимальная.
На уровне C (да и вообще на системном уровне) не бывает массивов с нефиксированным размером. Каждый раз, когда вы создаете массив в C, вы должны указать его размер, чтобы система знала, сколько нужно памяти на него выделить.
Тогда как это реализовано в PHP?
Когда вы создаёте пустой массив, PHP создаёт его с определённым размером. Если вы заполняете массив и в какой-то момент достигаете и превышаете этот размер, то создаётся новый массив с вдвое большим размером, все элементы копируются в него и старый массив уничтожается.
На самом деле для реализации массивов в PHP используется вполне себе стандартная структура данных Hash Table. Этот самый Hash Table хранит в себе указатель на самое первое и последнее значения, указатель на текущее значение, кол-во элементов, представленных в массиве, массив указателей на Bucket-ы (о них далее), и ещё кое-что. Есть две главные сущности, первая — это собственно сам Hash Table, и вторая — это Bucket (далее ведро).
В вёдрах хранятся сами значения, то есть на каждое значение — своё ведро. Но помимо этого в ведре хранится оригинал ключа, указатели на следующее и предыдущее вёдра (они нужны для упорядочивания массива, ведь в PHP ключи могут идти в любом порядке, в каком вы захотите), и, опять же, ещё кое-что.
Таким образом, когда вы добавляете новый элемент в массив, если такого ключа там ещё нет, то под него создаётся новое ведро и добавляется в Hash Table. У Hash Table есть некий массив указателей на вёдра, при этом вёдра доступны в этом массиве по некоему индексу, а этот индекс можно вычислить, зная ключ ведра.
То, что происходит после переполнения массива, называется rehash. И именно тут происходит операция копирования массива, о которой я сказал ранее. И именно по этой причине в PHP нельзя применять стандартные оценки сложности сортировок и других алгоритмов при работе со стандартными массивами.
Теперь вернёмся к нашему SplFixedArray. Учитывая описание данной структуры, можно смело сказать, что это реализация массива в классическом его понимании. Данная структура хорошо подходит для нумерованных списков.
3. Итераторы
Раз уж мы говорим о структурах данных, то стоит упомянуть и такие механизмы, как итераторы.
Итератор – это интерфейс, предоставляющий доступ к элементам коллекции (массива или контейнера) и навигацию по ним.
Обработка объектов итераторов очень похожа на обработку массивов. Многие начинающие программисты начинают с использования итераторов для массивов. Но реальные преимущества итераторы дают при переборе большого количества гораздо более сложных данных, чем простой массив.
Цикл foreach делает копию массива, который ему передаётся. Если происходит обработка большого объёма данных, то копирование массивов при каждом использовании цикла foreach может быть нежелательным. Итераторы SPL инкапсулируют список и делают видимым один элемент в конкретный момент времени, что гораздо эффективнее.
При создании структур данных итераторы могут существенно повлиять на производительность, так как позволяют организовать отложенную загрузку данных. Отложенная загрузка даёт возможность получать данные только тогда, когда они нужны. Также можно манипулировать данными (фильтровать, преобразовывать и так далее) перед передачей их пользователю.
Решение об использовании итераторов всегда остаётся за разработчиком. Итераторы имеют ряд преимуществ, но в некоторых случаях их применение может оказаться избыточным (например, для небольших наборов данных). Поэтому нужно принимать во внимание все факторы проекта.
В SPL существует несколько типов итераторов. Я рассмотрю, на мой взгляд, самые популярные.
3.1. ArrayIterator
Позволяет сбрасывать и модифицировать значения и ключи в процессе итерации по массивам и объектам. Конструктор принимает массив в качестве параметра и предоставляет набор методов для его обработки.
Обратите внимание, что, хотя ArrayObject и ArrayIterator ведут себя как массивы в данном контексте, они являются объектами. Попытка использовать встроенные функции для массивов, например, sort() или array_keys() для них приведет к ошибке.
Использование ArrayIterator ограничивается одномерными массивами. Тогда, когда можно обрабатывать многомерные массивы, нужно использовать RecursiveArrayIterator.
3.2. DirectoryIterator
Итератор для работы с директориями:
В SPL также имеется RecursiveDirectoryIterator, который можно использовать также, как и RecursiveArrayIterator. Есть одна особенность. RecursiveDirectoryIterator не возвращает пустых директорий: если директория содержит много поддиректорий, но без файлов, результат будет пустым.
4. SPL функции
Ну что ж, лекция окончена. Буду рад ответить на возникшие вопросы, пишите их в комментариях.