Синтаксис генераторов

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

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

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

Замечание:

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

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

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

Пример #1 Простой пример выдачи значений

<?php
function gen_one_to_three() {
    for (
$i 1$i <= 3$i++) {
        
// Обратите внимание, что $i сохраняет свое значение между вызовами.
        
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
/* $input содержит пары ключ/значение разделенные точкой с запятой */

$input = <<<'EOF'
1;PHP;Любит знаки доллара
2;Python;Любит пробелы
3;Ruby;Любит блоки
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:
    Python
    Любит пробелы
3:
    Ruby
    Любит блоки
Предостережение

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

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

Получение NULL

Для получения null нужно вызвать "yield" без аргументов. Ключ сгенерируется автоматически.

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

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

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

Результат выполнения данного примера:

array(3) {
  [0]=>
  NULL
  [1]=>
  NULL
  [2]=>
  NULL
}

Получение значения по ссылке

Генераторы могут отдавать значения по ссылке. Это делается так же, как возврат ссылок из функций": добавление амперсанда (&) перед именем функции.

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

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

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

/* Обратите внимание, что мы можем изменять $number в цикле, и
 * так как генератор возвращает ссылку, $value
 * в gen_reference() также изменится. */
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, для генерации собственных ключей и игнорировании ключей, переданных из объекта Generator.

Пример #5 yield from с iterator_to_array()

<?php
function inner() {
    yield 
1// ключ 0
    
yield 2// ключ 1
    
yield 3// ключ 2
}
function 
gen() {
    yield 
0// ключ 0
    
yield from inner(); // ключи 0-2
    
yield 4// ключ 1
}
// Задайте false вторым параметром для получения массива [0, 1, 2, 3, 4]
var_dump(iterator_to_array(gen()));
?>

Результат выполнения данного примера:

array(3) {
  [0]=>
  int(1)
  [1]=>
  int(4)
  [2]=>
  int(3)
}

Пример #6 Основы использования yield from

<?php
function count_to_ten() {
    yield 
1;
    yield 
2;
    yield from [
34];
    yield from new 
ArrayIterator([56]);
    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 yield from и возвращаемые значения

<?php
function count_to_ten() {
    yield 
1;
    yield 
2;
    yield from [
34];
    yield from new 
ArrayIterator([56]);
    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