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

Когда вызывается функция-генератор, она возвращает объект, который можно повторить. Когда вы перебираете этот объект (например, через цикл foreach ), PHP будет вызывать методы итерации объекта каждый раз, когда ему нужно значение, а затем сохраняет состояние генератора, когда генератор возвращает значение, чтобы его можно было возобновить, когда требуется следующее значение.

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

Примечание :

Генератор может возвращать значения, которые можно получить с помощью Generator::getReturn() .

Ключевое слово yield

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

Пример #1 Простой пример получения значений

<?php
function gen_one_to_three() {
    for ($i = 1; $i <= 3; $i++) {
        // Note that $i is preserved between yields.
        yield $i;
    }
}

$generator = gen_one_to_three();
foreach ($generator as $value) {
    echo "$value\n";
}
?>

Приведенный выше пример выведет:

1
2
3

Примечание :

Внутри последовательные целочисленные ключи будут связаны с полученными значениями, как и с неассоциативным массивом.

Осторожность

Значение, которое будет присвоено $data , является значением, переданным в Generator::send() , или nullесли вместо этого вызывается Generator::next() .

Получение значений с ключами

PHP также поддерживает ассоциативные массивы, и генераторы ничем не отличаются. В дополнение к получению простых значений, как показано выше, вы также можете одновременно получить ключ.

Синтаксис для получения пары ключ/значение очень похож на тот, который используется для определения ассоциативного массива, как показано ниже.

Пример #2 Получение пары ключ/значение

<?php
/*
 * The input is semi-colon separated fields, with the first
 * field being an ID to use as a key.
 */

$input = <<<'EOF'
1;PHP;Likes dollar signs
2;Python;Likes whitespace
3;Ruby;Likes blocks
EOF;

function input_parser($input) {
    foreach (explode("\n", $input) as $line) {
        $fields = explode(';', $line);
        $id = array_shift($fields);

        yield $id => $fields;
    }
}

foreach (input_parser($input) as $id => $fields) {
    echo "$id:\n";
    echo "    $fields[0]\n";
    echo "    $fields[1]\n";
}
?>

Приведенный выше пример выведет:

1:    PHP    Любит знаки доллара 2:    Питон    Любит пробелы 3:    Рубин    Лайки блоки

Осторожность

Как и в случае простых значений yield, показанных ранее, для получения пары ключ/значение в контексте выражения оператор yield должен быть заключен в скобки:

$data = (yield $key => $value);

Получение нулевых значений

Yield можно вызвать без аргумента, чтобы получить nullзначение с автоматическим ключом.

Пример #3 Получение nulls

<?php
function gen_three_nulls() {
    foreach (range(1, 3) as $i) {
        yield;
    }
}

var_dump(iterator_to_array(gen_three_nulls()));
?>

Приведенный выше пример выведет:

массив (3) {
  [0]=>
  НУЛЕВОЙ
  [1]=>
  НУЛЕВОЙ
  [2]=>
  НУЛЕВОЙ
}

Выход по ссылке

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

Пример #4 Получение значений по ссылке

<?php
function &gen_reference() {
    $value = 3;

    while ($value > 0) {
        yield $value;
    }
}

/*
 * Note that we can change $number within the loop, and
 * because the generator is yielding references, $value
 * within gen_reference() changes.
 */
foreach (gen_reference() as &$number) {
    echo (--$number).'... ';
}
?>

Приведенный выше пример выведет:

2... 1... 0... 

Делегирование генератора через yield from

Делегирование генератора позволяет вам получать значения из другого генератора, объекта Traversable или массива с помощью ключевого слова yield from . Затем внешний генератор будет выдавать все значения из внутреннего генератора, объекта или массива до тех пор, пока они не станут недействительными, после чего выполнение продолжится во внешнем генераторе.

Если генератор используется с yield from , выражение yield from также вернет любое значение, возвращаемое внутренним генератором.

Осторожность

Сохранение в массив (например, с помощью iterator_to_array() )

yield from не сбрасывает ключи. Он сохраняет ключи, возвращаемые объектом Traversable , или массивом . Таким образом, некоторые значения могут иметь общий ключ с другим yield или yield from , который при вставке в массив перезапишет прежние значения с этим ключом.

Распространенным случаем, когда это имеет значение, является iterator_to_array() , возвращающий по умолчанию массив с ключами, что может привести к неожиданным результатам. iterator_to_array() имеет второй параметр use_keys, который можно установить falseдля сбора всех значений, игнорируя при этом ключи, возвращаемые генератором .

Пример #5 выход из с помощью iterator_to_array()

<?php
function inner() {
    yield 1; // key 0
    yield 2; // key 1
    yield 3; // key 2
}
function gen() {
    yield 0; // key 0
    yield from inner(); // keys 0-2
    yield 4; // key 1
}
// pass false as second parameter to get an array [0, 1, 2, 3, 4]
var_dump(iterator_to_array(gen()));
?>

Приведенный выше пример выведет:

массив (3) {
  [0]=>
  интервал (1)
  [1]=>
  интервал (4)
  [2]=>
  интервал(3)
}

Пример #6 Основное использование доходности от

<?php
function count_to_ten() {
    yield 1;
    yield 2;
    yield from [3, 4];
    yield from new ArrayIterator([5, 6]);
    yield from seven_eight();
    yield 9;
    yield 10;
}

function seven_eight() {
    yield 7;
    yield from eight();
}

function eight() {
    yield 8;
}

foreach (count_to_ten() as $num) {
    echo "$num ";
}
?>

Приведенный выше пример выведет:

1 2 3 4 5 6 7 8 9 10 

Пример #7 выход из и возвращаемые значения

<?php
function count_to_ten() {
    yield 1;
    yield 2;
    yield from [3, 4];
    yield from new ArrayIterator([5, 6]);
    yield from seven_eight();
    return yield from nine_ten();
}

function seven_eight() {
    yield 7;
    yield from eight();
}

function eight() {
    yield 8;
}

function nine_ten() {
    yield 9;
    return 10;
}

$gen = count_to_ten();
foreach ($gen as $num) {
    echo "$num ";
}
echo $gen->getReturn();
?>

Приведенный выше пример выведет:

1 2 3 4 5 6 7 8 9 10