что такое генераторы и как их использовать php
Что такое генераторы и как их использовать php
В этом разделе помещены уроки по PHP скриптам, которые Вы сможете использовать на своих ресурсах.
Фильтрация данных с помощью zend-filter
Когда речь идёт о безопасности веб-сайта, то фраза «фильтруйте всё, экранируйте всё» всегда будет актуальна. Сегодня поговорим о фильтрации данных.
Контекстное экранирование с помощью zend-escaper
Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.
Подключение Zend модулей к Expressive
Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.
Совет: отправка информации в Google Analytics через API
Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.
Подборка PHP песочниц
Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.
Совет: активация отображения всех ошибок в PHP
При поднятии PHP проекта на новом рабочем окружении могут возникнуть ошибки отображение которых изначально скрыто базовыми настройками. Это можно исправить, прописав несколько команд.
Агент
PHP парсер юзер агента с поддержкой Laravel, работающий на базе библиотеки Mobile Detect.
Рассматриваем PHP генераторы
Рассматриваем PHP генераторы
PHP генераторы — полезное понятие, чтобы понять мир разработки PHP.
Когда дело доходит до управления автомобилем, скорость — это не все, что необходимо. Но в интернете, скорость означает нечто иное. Чем быстрее ваше приложение, тем лучше опыт пользователя. Эта статья посвящена PHP генераторам, но почему мы говорим о скорости? Скоро вы поймете, генераторы имеют большое влияние на скорость и управление памятью.
Что такое PHP генераторы?
Добавленные в PHP в версии 5.5, генераторы являются функциями, которые обеспечивают простой способ для перебора данных без необходимости создания массива в памяти. Еще немного путаетесь? Пример — хороший способ показать генераторы в действии.
Во-первых, быстро создадим файл generator.php который мы будем использовать в этом руководстве. После создания файла, мы добавим этот небольшой фрагмент кода.
Мы можем быстро распаковать встроенный PHP-сервер в каталог, где мы создали файл generator.php:
Поэтому если мы перейдем по адресу (http://localhost:8000/generator.php), мы получим что-то вроде следующего:
Dataset 1
Dataset 2
Dataset 3
Dataset 4
Dataset 5
Dataset 6
Dataset 7
Dataset 8
Dataset 9
Dataset 10
Dataset 11
Dataset 12
Dataset 13
Dataset 14
Dataset 15
Код скажет все сам за себя, и его, безусловно, не так много. Но если мы вернемся в наш код и сделаем небольшие изменения, то получим:
Теперь upper range (max) с сгенерированных чисел — PHP_INT_MAX, что является наибольшим числом, которого может достичь ваша версия PHP. После этого, зайдите в браузер и обновите его. Но на этот раз вы увидите что-то другое. Генератор выдаст ошибку.
Fatal error: Allowed memory size of 209715200 bytes exhausted (tried to allocate 4104 bytes) in X:\home\localhost\www\bookflow\includes\index.php on line 637
Состоялось переполнения памяти. Возможные решения, которые приходят на ум: обратиться в php.ini и увеличить memory_limit. Зададим себе вопрос, действительно ли это будет эффективно? Хотим ли мы иметь один скрипт, который будет содержать всю память нашего сервера? Ответы — нет и нет. Это не эффективно, и мы не хотим скрипт, который будет использовать всю нашу память.
Использование генераторов
Определим ту же функцию, которую использовали выше, вызовем ее с тем же значением PHP_INT_MAX и запустим программу. Но, на этот раз, мы будем создавать функцию-генератор.
Если вы зайдете в свой браузер, вы должны увидеть данные, которые отображаются на странице.
Примечание: Генераторы могут быть использованы только в функции.
Зачем нужны генераторы?
Есть моменты, когда мы хотели бы разобрать большой набор данных (это могут быть лог-файлы), выполнять вычисления на больших базах данных и т.д. Но мы не хотим искажать всю память. Мы должны стараться сохранить память как можно дольше. Данные не обязательно должны быть большими — генераторы являются эффективными независимо от того, насколько маленький набор данных. Не забывайте, что наша цель заключается в увеличении скорости при использовании меньшего количества памяти.
Возвращаемые значения
Есть моменты, когда наши данные имеют смысл только тогда, когда они являются ключевыми. При использовании генераторов, мы можем возвращать значение, например:
Затем мы можем пойти дальше и использовать пары значений, поскольку мы могли бы сделать это с любым массивом, например:
Отправка переменных к генераторам
Передача значения, сделайте следующее:
Примечание: Использование return в генераторе нарушит функцию генератора.
Не злоупотребляйте генераторами
Использование PHP_INT_MAX немного неуместно. Для меня PHP_INT_MAX это 2147483647, а именно:
Генераторы должны быть эффективными. Это не значит, что они не будут вызывать ту же проблему, которую они пытаются решить при неправильном использовании.
Что такое генераторы в PHP
При вождении автомобиля – скорость это далеко не все. Но в WEB все решает скорость. Чем быстрее ваше приложение, тем лучше пользовательский опыт. Хорошо, эта статья о генераторах в PHP, так почему же мы говорим о скорости? Как вы увидите вскоре, генераторы привносят большие изменения по части скорости и потреблении памяти приложением.
Что такое PHP генераторы?
Добавленные в PHP в версии 5.5, генераторы представляют собой функции, обеспечивающие простой механизм для циклической обработки данных, без необходимости создавать массив данных в памяти. Все еще не понимаете о чем речь? Тогда давайте посмотрим на PHP генераторы в действии.
Создаем файл generator_test.php со следующим содержанием:
Затем в папке где у нас лежит этот файл открываем консоль и пишем следующее
Дальше открываем браузер и идем по следующему адресу:
Результат будет такой:
Данные 1
Данные 2
….
Данные 15
Код выше достаточно прост. Однако, давайте сделаем небольшое изменение в нем:
Теперь диапазон генерируемых чисел находится в пределах от 0 до константы PHP_INT_MAX, которая представляет собой наибольшее целое число, которое способен представить интерпретатор PHP. После этого опять идем в браузер и обновляем страницу. Однако на этот раз, вместо обычного текста получаем сообщение о том, что превышен объем доступной памяти, вследствие чего работа скрипта была аварийно завершена.
Что за досада – у PHP закончилась память! Первое что приходит на ум – это редактировать настройку memory_limit в php.ini. Но давайте спросим себя – действительно ли это так эффективно? Неужели мы хотим, чтобы какой-то единственный скрипт занимал всю доступную память?
Используем генераторы
Давайте напишем ту же самую функцию, что и выше, вызовем ее с тем же значением PHP_INT_MAX и запустим снова. Но в этот раз мы создадим функцию-генератор.
Определяя функцию getRange на этот раз, мы всего лишь проходим по значениям и генерируем вывод. Ключевое слово yield похоже на инструкцию return тем, что возвращает значение из функции, но единственное отличие заключается в том, что yield возвращает значение только тогда, когда это необходимо и не пытается вместить весь массив данных в память за один раз. Перейдя к браузеру, вы должны увидеть данные, отображаемые на странице. Обратите внимание на тот факт, что генераторы в PHP могут быть использованы только лишь из функции.
Зачем нужны генераторы?
Время от времени возникают такие задачи, когда нам необходимо обработать большие объемы данных (например, файлы логов), выполнить вычисления на больших выборках из базы и т.д. И мы отнюдь не хотим, чтобы эти операции занимали всю доступную память, так мы должны стараться сохранять память насколько это возможно. Данные не обязательно должны быть большими – PHP генераторы эффективны вне зависимости от размера данных. И не забывайте, что наша цель – сделать приложение быстрым и при этом таким, чтобы оно потребляло как можно меньше памяти.
Возврат ключей
Бывают случаи, когда нам необходимо возвращать не просто значение, а пару ключ-значение. При использовании генераторов, мы можем генерировать пары ключ-значение следующим образом.
Использовать данную функцию мы можем также как и простой массив:
Отсылка значений генераторам
Генераторы также могут принимать значения. Под этим подразумевается, что генераторы позволяют нам вставлять значения, которое может представлять собой подобие команды или еще что-то. Например, мы можем отправить значение в наш генератор, которое сигнализирует о необходимости остановки исполнения или изменения выходных данных. Далее пример кода:
Отмечу, что использование инструкции return в функции-генераторе приведет к немедленному выходу из этой функции.
Кстати, о генераторах я подробно рассказываю в моем курсе PHP и MySQL с Нуля до Гуру 2.0. Там есть и примеры и задания, которые помогут лучше усвоить материал.
Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!
Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.
Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления
Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.
Порекомендуйте эту статью друзьям:
Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):
Комментарии ( 2 ):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.
Copyright © 2010-2021 Русаков Михаил Юрьевич. Все права защищены.
Использование php-генераторов
Несмотря на то, что php-генераторы доступны с php 5.5.0, они все еще почти не используются. Более того, большинство разработчиков, которых я знаю, понимают, как работают генераторы, но не видят, когда они могут быть полезны в реальной жизни.
Да, генераторы определенно смотрятся хорошо, но знаете. Я не понимаю, где они могут быть полезными для меня, разве что для расчета последовательности Фибоначчи.
Но даже с этих простых пример мы можем понять ключевые преимущества использования генераторов : они просто упрощают итераторы.
Генераторы позволяют вам написать код, который использует foreach для итерации множества данных без необходимости выделения памяти под массив.
Держа этот факт в памяти, я попытаюсь объяснить, почему генераторы насколько здорово помогли мне решить задачи над которыми я работал в компании.
Сначала немного контекста
Я работаю в TEA. В основном, мы разрабатываем экосистему для электронных книжек. Это покрывает весь путь от получения файлов нужного формата от издателей до размещения их на e-commerce сайте и предоставления конечному потребителю возможности читать онлайн (используя браузер, написанный @johanpoirier) или с электронной книги.
Итерация по крупному множеству данных
Для первого примера использования, давайте предположим, что у меня есть большая коллекция книг и я хочу отфильтровать те, которые можно читать у веб-просмотрщике.
По традиции я должен был бы написать что-то вроде:
Бонус: RulerZ внутри использует генераторы, так что мы можем переписать метод и остаться с той же оптимизацией по выделению памяти.
Агрегация нескольких источников данных
Мы можем написать простой метод, агрегирующий эти два источники:
Но еще раз, количество потребляемой памяти при использовании подобного подхода очень зависит от количества книг, хранимых в базе данных и Elasticsearch.
Мы можем начать использовать генераторы и возвратить результат:
Так, конечно, лучше, но у нас все равно есть проблема: наш метод getBooks выполняет слишком много работы! Мы должны разделить две ответственности (считывание данных с БД и вызов Elasticsearch ) в два метода:
Вы могли заметить использование yield from оператора (доступен с php 7.0), который позволяет делегировать использование генераторов. Это идеально, к примеру, для агрегации нескольких источников данных, которые используют генераторы.
yield from оператор работает с любым Traversable объектом, так что массивы и итераторы также могут быть использованы с этим оператором.
Используя такую конструкцию, мы можем агрегировать несколько источников данных пару строками кода:
Сложная ленивая (по требованию) гидрация записей БД
Мне нужно было импортировать сотни тысяч заказов с давней БД в нашу систему, каждый заказ содержал несколько пунктов.
Наличие заказов и «пунктов заказов» было предпосылкой к тому, что мы должны сделать. Я написал метод, который возвращает сгидрированые заказы и при этом не становится слишком медленным или прожорливым.
Идея слегка наивная: сджойнить заказы с пунктами, и сгруппировать заказы и пункты заказов в цикле.
Имитация асинхронных задач
Последнее, но тем не менее важное: генераторы так же могут быть использованы для имитации асинхронных задач. Пока я писал эту заметку, я наткнулся на пост @nikita_ppv на такую же тематику, и так как он первый реализовал генераторы в php, я просто оставлю ссылку на его пост.
Он быстро объясняет что такое генераторы и (в деталях) как мы можем получить преимущества из-за того, что они могут быть прерваны и отправлять/принимать данные для реализации сопрограмм и даже многозадачности.
Подводя итог
Генераторы.
Генераторы в действии
Небольшое вступление
Не так давно я решил для себя, что пора восполнить большой пробел в знаниях и решил прочитать про переходы между версиями PHP, т.к. понимал, что остался где-то между 5.2 и 5.3 и этот пробел необходимо как-то устранить. До этого я читал про namespaces, traits и т.д, но дальше чтения не уходило. И вот тут я заметил генераторы, почитал документацию, одну из статей на хабре на этот счет и после этого возникла мысль — а как раньше без них жили-то?
Данным переводом хочу помочь хотя бы новичкам, поскольку на php.net документация по генераторам на английском и, на мой взгляд, должным образом не раскрывает всю идею и места применения. Текста много, кода чуть меньше, картинок нет. Потребуются общие знания, например, про итераторы. Очевидный код комментировать не буду, а вот сложные для понимания примеры постараюсь объяснить в силу своих знаний.
UPD1: Изменил расплывчатую формулировку, про которую говорили в комментариях.
UPD2: Добавил решение с принудительным break.
Теория
Сразу скажу главную вещь — генераторы никоим образом не позволят сделать что-то новое, чего нельзя было сделать раньше, поскольку генераторов до PHP 5.5 нет. Это лишь новая возможность, которая несколько меняет обычное поведение языка. Везде, где используются генераторы, можно также использовать итераторы. Теперь, зная об этом, сразу взглянем на пример. Скажем, нам необходимо пройтись по строкам в файле. В процедурном стиле это можно сделать как-то так:
Это обычное решение, ничего странного тут нет. Но что если нам нужно что-то более абстрактное? Скажем, генерировать строки из абстрактного источника. Да, сегодня это может быть файл, но завтра мы решим, что более удачным решением будет база данных или вообще что-то иное.
Сейчас у нас есть два пути решения данной задачи — мы можем вернуть массив или итератор. Но возвращая массив есть несколько проблем: во-первых мы не знаем сколько нам нужно памяти (вдруг файл у нас размером 30 гб?), а во-вторых, возможно, мы и вовсе не сможем описать наш источник как массив (например, мы можем возвращать бесконечные порции данных и попробуй угадай когда этот поток закончится, если ты клиент).
Итак, остаются итераторы. Наш пример очень просто описать через итератор. Тем более, что в PHP уже есть готовый класс для этого — SPLFileObject. Но давайте оставим его и напишем что-то свое.
Совсем просто, не так ли? Хорошо, не совсем, но уже что-то. Хотя если мы взглянем на пример внимательнее, то увидим, что мы не совсем точно описали итератор, поскольку двойной вызов метода current() не даст нам ожидаемый результат в виде одного и того же значения.
Я (автор статьи, не «переводчик») сделал это специально, чтобы показать, что замена процедуры на итератор не всегда является простой задачей, поскольку в реальных ситуациях все куда сложнее. Давайте сделаем правильный итератор для нашего файла.
Боже, как много всего для, казалось бы, простой задачи типа обхода файла, да и основная работа все равно спрятана внутри функций работы с файлами. Теперь, представим, что нам нужно сделать реализовать более сложный алгоритм. Если продолжать текущий подход, то он может стать еще сложнее и понять его работу будет труднее. Давайте решим нашу проблему с помощью генераторов.
Намного проще! Да, это почти как первый пример с функцией, только появилось исключение и ключевое слово yield.
Итак, как оно работает?
Очень важно понимать, что в примере выше изменяется возвращаемое значение функции. Это не null, как может показаться с первого взгляда. Наличие yield говорит о том, что PHP вернет нам специальный класс — генератор. Генератор ведет себя также, как и итератор, поскольку он реализует его. И использовать генератор можно аналогично итераторам.
Вся фишка здесь в том, что мы можем писать код как угодно и просто выбрасывать (yield, йелднуть, йелдануть… не знаю как перевести правильнее, когда есть бросание исключений) каждый раз новое значение когда нам это надо. Итак, как же оно работает? Когда мы вызываем функцию getLines(), PHP выполнит код до первой встречи ключевого слова yield, на котором он запомнит это значение и вернет генератор. Затем, будет вызов метода next() у генератора (который описан нами или итератором), PHP снова выполнит код, только начнет его не с самого начала, а начиная с прошлого значения, которое мы благополучно выбросили и забыли о нем, и опять, до следующего yield или же конца функции, или return. Зная этот алгоритм, теперь можно сделать полезный генератор:
Возможно, с первого взгляда не совсем понятно что это, да и вообще бесконечный цикл все испортит. Да, эта функция и будет работать как бесконечный цикл. Но посмотрите внимательнее — это ведь числа Фибоначчи.
Нужно отметить, что генераторы не являются заменой итераторам. Это лишь простой путь их получения. Итераторы по-прежнему являются мощным инструментом.
Сложный пример
Нам нужно сделать собственный ArrayObject. Вместо того, чтобы делать итератор, сделаем небольшой трюк с генератором. Интерфейс IteratorAggregate требует от нас всего один метод — getIterator(). Так как генератор возвращает объект, реализующий итератор, то мы можем переопределить этот метод таким образом, чтобы он возвращал генератор. Все просто:
В точку! Теперь мы можем перебрать все свойства нашего массива через генератор или через обычный синтаксис обращения по ключу.
Отправляем данные обратно
Генераторы позволяют отправлять себе данные, используя метод send(). В некоторых случаях это может быть очень удобно. Например, когда надо сделать какой-то лог-файл. Вместо того, чтобы писать целый класс для него, можно просто воспользоваться генераторами:
Довольно просто и быстро. Чтобы немного усложнить задачу, посмотрим пример, где функции работают совместно, перекидывая управление между собой при помощи генераторов. Нам нужно построить очередь, которая получает и отправляет данные пакетами. Иногда такие задачи появляются, когда мы читаем бинарный поток и нужно контролировать размер пакета.
Немного сложно, но, надеюсь, вы поняли как это работает. Мы разделили обработку и получение данных определенного размера в нужный момент + остается возможность повторного использования кода.
Нужно боольше примеров!
Вообще генераторы можно применять во многих задачах. Одна из них — симуляция потоков. Сначала мы определяем каждый поток как генератор. Затем выбрасываем сигнал управления родителю, чтобы тот смог передать сигнал для работы следующему потоку. Построим такую систему, которая работает с разными источниками данных (работаем с неблокирующим вводом-выводом). Вот пример такой системы:
Заключение
Генераторы — ОЧЕНЬ мощная штука. Они позволяют очень сильно упростить код. Подумайте только, вы можете написать функцию для диапазона чисел в одну строчку кода:
Коротко и просто. Легко читается, легко понять как работает и очень производительно — быстрее, чем с итератором.
Оригинал статьи — Anthony Ferrara @ blog.ircmaxell.com
В комментариях возник популярный вопрос о том, что делать, когда генератор (вернее сказать, его перебор через foreach) принудительно завершает свою работу, например, через break. В таком случае, если мы имеем дело с перебором файла, как из первого примера, то есть риск того, что никогда не сработает fclose, так как генератор попросту «забывает» о нем. Одно из самых верных решений предложил weirdan (#) — использовать конструкцию try <… >finally <… >, где в блоке finally очищаем открытые ресурсы. Данный блок сработает всегда при завершении перебора генератора, но есть маленький нюанс: если перебор генератора отработал до конца (без break) нормально, то выполнится и код после блока finally.
Кратко о генераторах
— Не добавляют нового функционала в язык
— Быстрее*
— Возобновление работы генератора происходит с последнего «выброса» yield
— В генератор можно отправлять значения и исключения (через метод throw())
— Генераторы однонаправлены, т.е. нельзя вернуться назад
— Меньше кода в большинстве случаев, более простые для понимания конструкции
* Основываясь на этих результатах.
При больших масштабах перебора — генераторы быстрее. Примерно в 4 раза быстрее чем итераторы и на 40% быстрее обычного перебора. При небольшом количестве элементов могут быть медленнее обычного перебора, но все еще быстрее итераторов.
Если сообщество одобрит перевод и посчитает его хорошим (а главное, не утверждающим чепуху и не меняющим суть кода), мне будет интересно иногда переводить другие статьи.
Думаю, не будет лишним перевести и собрать в кучку статьи, публикуемые сейчас на phpmaster про структуры данных.
Также буду рад любым замечаниям, наставлениям, комментариям про ошибки как в тексте с кодом, так и в самом переводе.