Генератор в целом выглядит как обычная функция, за исключением того, что вместо возвращения одного значения, генератор будет перебирать столько значений, сколько необходимо. Любая функция, содержащая yield, является функцией генератора.
Когда вызывается генератор, он возвращает объект, который можно итерировать. Когда вы итерируете этот объект (например, в цикле foreach), PHP вызывает методы итерации объекта каждый раз, когда вам нужно новое значение, после чего сохраняет состояние генератора и при следующем вызове возвращает следующее значение.
Когда все значения в генераторе закончились, генератор просто завершит работу, ничего не вернув. После этого основной код продолжит работу, как если бы в массиве закончились элементы для перебора.
Замечание:
Генераторы могут возвращать значения, которые можно получить с помощью Generator::getReturn().
Вся суть генератора заключается в ключевом слове 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
Замечание:
Последовательность целых чисел будет связана с получаемыми от генератора значениями, как будто перебирается неассоциативный массив.
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 Любит блоки
Для получения null
нужно вызвать "yield" без аргументов. Ключ сгенерируется
автоматически.
Пример #3 Получение null
<?php
function gen_three_nulls() {
foreach (range(1, 3) 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...
Делегирование генератора позволяет вам получать значения из другого генератора, объекта Traversable, или массива, используя yield from. Внешний генератор будет возвращать значения из внутреннего генератора, объекта или массива, до того момента, пока они их отдают, после чего продолжится выполнение внешнего генератора.
Если генератор используется с yield from, то выражение yield from также будет возвращать значения из внутреннего генератора.
yield from не сбрасывает ключи. Ключи, возвращённые из объекта Traversable или массива, сохранятся. Таким образом, некоторые значения, могут пересекаться по ключам с другими yield или yield from, что, при записи в массив, повлечёт за собой перезапись прежних значений.
Общий случай, когда это имеет значение, это когда iterator_to_array()
возвращает массив с ключами по умолчанию. В этом случае можно получить
неожиданный результат. iterator_to_array() имеет второй параметр
preserve_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 [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 yield from и возвращаемые значения
<?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