Рассматриваем PHP генераторы
PHP генераторы — полезное понятие, чтобы понять мир разработки PHP.
Когда дело доходит до управления автомобилем, скорость — это не все, что необходимо. Но в интернете, скорость означает нечто иное. Чем быстрее ваше приложение, тем лучше опыт пользователя. Эта статья посвящена PHP генераторам, но почему мы говорим о скорости? Скоро вы поймете, генераторы имеют большое влияние на скорость и управление памятью.
Что такое PHP генераторы?
Добавленные в PHP в версии 5.5, генераторы являются функциями, которые обеспечивают простой способ для перебора данных без необходимости создания массива в памяти. Еще немного путаетесь? Пример — хороший способ показать генераторы в действии.
Во-первых, быстро создадим файл generator.php который мы будем использовать в этом руководстве. После создания файла, мы добавим этот небольшой фрагмент кода.
function getRange ($max = 10) { $array = []; for ($i = 1; $i < $max; $i++) { $array[] = $i; } return $array; } foreach (getRange(15) as $range) { echo "Dataset {$range} "; }
Мы можем быстро распаковать встроенный PHP-сервер в каталог, где мы создали файл generator.php:
php -S localhost:8000
Поэтому если мы перейдем по адресу (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
Код скажет все сам за себя, и его, безусловно, не так много. Но если мы вернемся в наш код и сделаем небольшие изменения, то получим:
foreach (getRange(PHP_INT_MAX) as $range) { echo "Dataset {$range} "; }
Теперь 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 и запустим программу. Но, на этот раз, мы будем создавать функцию-генератор.
function getRange ($max = 10) { for ($i = 1; $i < $max; $i++) { yield $i; } } foreach (getRange(PHP_INT_MAX) as $range) { echo "Dataset {$range} "; }
Рассматривая функцию getRange, мы только выполняем цикл по значениям и даем исходную информацию — yield. yield похож на return, поскольку он возвращает значение из функции, но с той лишь разницей, что yield возвращает значение только тогда, когда это необходимо, и не пытается сохранить весь набор данных в памяти.
Если вы зайдете в свой браузер, вы должны увидеть данные, которые отображаются на странице.
Примечание: Генераторы могут быть использованы только в функции.
Зачем нужны генераторы?
Есть моменты, когда мы хотели бы разобрать большой набор данных (это могут быть лог-файлы), выполнять вычисления на больших базах данных и т.д. Но мы не хотим искажать всю память. Мы должны стараться сохранить память как можно дольше. Данные не обязательно должны быть большими — генераторы являются эффективными независимо от того, насколько маленький набор данных. Не забывайте, что наша цель заключается в увеличении скорости при использовании меньшего количества памяти.
Возвращаемые значения
Есть моменты, когда наши данные имеют смысл только тогда, когда они являются ключевыми. При использовании генераторов, мы можем возвращать значение, например:
function getRange ($max = 10) { for ($i = 1; $i < $max; $i++) { $value = $i * mt_rand(); yield $i => $value; } }
Затем мы можем пойти дальше и использовать пары значений, поскольку мы могли бы сделать это с любым массивом, например:
foreach (getRange(PHP_INT_MAX) as $range => $value) { echo "Dataset {$range} has {$value} value "; }
Отправка переменных к генераторам
Генераторы могут также принимать переменные. Это означает, что генераторы позволяют вводить значения в них, это может быть команда или что-то другое. Например, мы можем послать переменную к нашему генератора, чтобы остановить выполнение или изменить output. С помощью функции getRange, которая уже была приведена выше, мы можем сделать это:
function getRange ($max = 10) { for ($i = 1; $i < $max; $i++) { $injected = yield $i; if ($injected === 'stop') return; } }
Передача значения, сделайте следующее:
$generator = getRange(PHP_INT_MAX); foreach ($generator as $range) { if ($range === 10000) { $generator->send('stop'); } echo "Dataset {$range} "; }
Примечание: Использование return в генераторе нарушит функцию генератора.
Не злоупотребляйте генераторами
Использование PHP_INT_MAX немного неуместно. Для меня PHP_INT_MAX это 2147483647, а именно:
Генераторы должны быть эффективными. Это не значит, что они не будут вызывать ту же проблему, которую они пытаются решить при неправильном использовании.