Вы когда-нибудь задавались вопросом: “Какая польза от yield в PHP?”. Позвольте мне избавить вас от поиска в Google; Я с удовольствием раскрою вам пару ключевых моментов о yield:
Что такое yield.
Различия между yield и return.
Варианты использования yield.
Заключение.
Ссылки.
1. Что такое “yield”
Функция-генератор выглядит так же, как и обычная функция, за исключением того, что вместо всего лишь одного значения генератор вырабатывает (yields) столько значений, сколько ему нужно.
Взгляните на следующий пример:
function getValues() {
yield 'value';
}
// вывод строки "value"
echo getValues();
Конечно, это не будет работать. Предыдущий пример выдаст ошибку: Object of class Generator could not be converted to string
. Позвольте мне объяснить почему:
2. Различия между “yield” и “return”
Полученная ошибка говорит нам о том, что функция getValues()
не возвращает строку, как мы могли бы этого ожидать. Давайте проверим ее тип:
function getValues() {
return 'value';
}
var_dump(getValues()); // string(5) "value"
function getValues() {
yield 'value';
}
var_dump(getValues()); // class Gene(0) {}rator#1
Класс Generator реализует интерфейс Iterator, поэтому для получения значений вам необходимо проитерировать по результатам функции getValue()
:
foreach (getValues() as $value) {
echo $value;
}
// можно также сделать это через переменную
$values = getValues();
foreach ($values as $value) {
echo $value;
}
Но различия на этом не заканчиваются!
Генератор позволяет вам писать код с использованием оператора foreach для итерации по набору данных без необходимости создавать в памяти массив, что, однако, может приводить к превышению лимита памяти.
В следующем примере мы создадим массив из 800000 элементов и вернем его из функции getValues()
, контролируя память, выделенную для этого фрагмента кода, с помощью функции memory_get_usage(). Мы будем запрашивать потребление памяти через каждые 200000 добавленных элементов, что означает, что контрольных точек будет четыре:
<?php
function getValues() {
$valuesArray = [];
// get the initial memory usage
echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB' . PHP_EOL;
for ($i = 1; $i < 800000; $i++) {
$valuesArray[] = $i;
// let us do profiling, so we measure the memory usage
if (($i % 200000) == 0) {
// get memory usage in megabytes
echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB'. PHP_EOL;
}
}
return $valuesArray;
}
$myValues = getValues(); // building the array here once we call the function
foreach ($myValues as $value) {}
Вот такие результаты потребления памяти мы получили для примера, приведенного выше:
0.34 MB
8.35 MB
16.35 MB
32.35 MB
Несколько строк нашего кода потребляют более 30 мегабайт памяти. Каждый раз, когда мы добавляем элемент в массив $valuesArray
, мы увеличиваем его размер в памяти.
Давайте рассмотрим тот же пример, только с использованием yield:
<?php
function getValues() {
// get the initial memory usage
echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB' . PHP_EOL;
for ($i = 1; $i < 800000; $i++) {
yield $i;
// let us do profiling, so we measure the memory usage
if (($i % 200000) == 0) {
// get memory usage in megabytes
echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB'. PHP_EOL;
}
}
}
$myValues = getValues(); // no action taken until we loop over the values
foreach ($myValues as $value) {} // start generating values here
Результат для этого варианта кода может вас поразить:
0.34 MB
0.34 MB
0.34 MB
0.34 MB
Конечно это не означает, что вам нужно повсеместно переходить от return к yield, но если в своем приложении вы создаете огромные массивы, которые могут вызывать проблемы с памятью на сервере, yield однозначно будет решением вашей проблемы.
3. Варианты использования “yield”
Существует много вариантов использования yield, но я выделю пару из них:
a. Используя yield, вы также можете использовать return:
function getValues() {
yield 'value';
return 'returnValue';
}
$values = getValues();
foreach ($values as $value) {}
echo $values->getReturn(); // 'returnValue'
b. Возврат пар ключ-значение:
function getValues() {
yield 'key' => 'value';
}
$values = getValues();
foreach ($values as $key => $value) {
echo $key . ' => ' . $value;
}
Подробнее об этом можно почитать здесь.
4. Заключение
Основная цель этой статьи - показать на примерах, в чем разница между yield и return в контексте потребления памяти. По моему мнению, это очень важно знать каждому разработчику.
5. Ссылки
Данная статья переведена в преддверии старта курса PHP Developer. Basic. Узнать подробнее о курсе можно по ссылке.