W3docs

yield

Ключевое слово yield используется в PHP для создания функции-генератора, которая может приостанавливаться и возобновляться во время выполнения.

Введение

Ключевое слово yield превращает обычную PHP-функцию в генератор — функцию, которая производит последовательность значений по одному за раз, приостанавливаясь после каждого значения и возобновляясь с того места, где остановилась, когда запрашивается следующее значение. На этой странице рассматривается, как работает yield, как возвращать ключи, как передавать значения обратно в генератор, синтаксис делегирования yield from, а также ситуации, когда генераторы экономят память по сравнению с созданием полного массива.

Функция становится генератором, как только содержит хотя бы один yield. Вызов такой функции не выполняет её тело — он возвращает объект Generator. Тело выполняется только по мере итерации, продвигаясь к следующему yield на каждом шаге. Поскольку генератор реализует интерфейс Iterator, его обычно обходят с помощью цикла foreach.

Первый генератор

Каждый yield передаёт одно значение вызывающему коду и замораживает функцию до тех пор, пока не будет запрошено следующее значение.

php— editable, runs on the server

myGenerator() возвращает объект генератора вместо немедленного выполнения. Цикл foreach извлекает из него значения: на каждом проходе функция выполняется до следующего yield, выдаёт это значение и приостанавливается. Вывод:

Hello World !

В отличие от функции, которая строит и возвращает массив через return, все три строки никогда не хранятся в памяти одновременно — каждая производится по требованию. Сравните это с тем, как обычная функция использует return для возврата одного значения и завершения работы.

Возврат ключей через yield

Генератор может возвращать пары ключ/значение с помощью синтаксиса yield $key => $value, подобно ассоциативному массиву. Ключи доступны внутри foreach:

<?php

function settings()
{
  yield "host" => "localhost";
  yield "port" => 5432;
  yield "user" => "admin";
}

foreach (settings() as $key => $value) {
  echo "$key = $value\n";
}

Вывод:

host = localhost
port = 5432
user = admin

Если ключи не указаны, PHP автоматически нумерует возвращаемые значения начиная с 0, как в индексированном массиве.

Генераторы ленивы: экономичный диапазон чисел

Главное преимущество yield заключается в том, что значения вычисляются только по мере необходимости. Генератор, производящий миллион чисел, использует постоянный объём памяти, тогда как массив из миллиона чисел выделяет память для всех сразу.

<?php

function gen_range($start, $end)
{
  for ($i = $start; $i <= $end; $i++) {
    yield $i;
  }
}

$sum = 0;
foreach (gen_range(1, 1000000) as $n) {
  $sum += $n;
}

echo $sum;

Вывод:

500000500000

Цикл никогда не создаёт массив [1, 2, …, 1000000]; в каждый момент времени существует только одно целое число. Это шаблон, к которому стоит прибегать при построчном чтении больших файлов или потоковой передаче строк из базы данных.

Возврат значения из генератора

Генератор также может использовать return для передачи финального значения (PHP 7+). Возвращаемое значение не является частью итерации — его считывают после завершения с помощью getReturn():

<?php

function counter()
{
  yield 1;
  yield 2;
  return "done";
}

$gen = counter();
foreach ($gen as $value) {
  echo $value . " ";
}
echo $gen->getReturn();

Вывод:

1 2 done

Вызов getReturn() до завершения итерации по генератору выбрасывает исключение, поэтому всегда сначала исчерпайте его полностью.

Передача значений с помощью send()

Генераторы работают в обоих направлениях: выражение yield также может получать значение, отправленное вызывающим кодом через send(). Это основа корутин.

<?php

function echoTimes()
{
  while (true) {
    $received = yield;
    echo "Got: $received\n";
  }
}

$gen = echoTimes();
$gen->current();      // prime the generator (run up to the first yield)
$gen->send("a");
$gen->send("b");

Вывод:

Got: a
Got: b

send($value) возобновляет работу генератора, заставляя приостановленное выражение yield вычислиться в $value, после чего выполнение продолжается до следующего yield.

Делегирование с помощью yield from

yield from (PHP 7+) возвращает каждое значение из другого генератора, массива или Traversable, встраивая его в текущий генератор без ручного цикла:

<?php

function inner()
{
  yield 2;
  yield 3;
}

function outer()
{
  yield 1;
  yield from inner();
  yield from [4, 5];
}

foreach (outer() as $value) {
  echo $value . " ";
}

Вывод:

1 2 3 4 5 

Когда использовать генератор

  • Большие или неограниченные последовательности — чтение многогигабайтного файла или бесконечного потока, когда хранить всё в массиве невозможно.
  • Дорогостоящие значения, которые могут не понадобиться — если потребитель может остановиться досрочно (break), генератор избавит от вычисления остального.
  • Более чистый код итерации — замена пользовательского класса Iterator одной функцией.

Избегайте генераторов, когда нужен произвольный доступ ($arr[42]), дешёвый вызов count() или многократная итерация по одним и тем же данным — генератор можно обойти только один раз. В таких случаях используйте обычный массив, возможно создав его с помощью PHP-функций, таких как array_map.

Практика

Практика
Какова функция ключевого слова 'yield' в PHP?
Какова функция ключевого слова 'yield' в PHP?
Was this page helpful?