Uma função generator se parece com uma função normal, exceto que ao invés de retornar um valor, um generator pode entregar o resultado quantas vezes forem necessárias.
Quando uma função generator é chamada, ela retorna um objeto que pode ser iterado. Quando você itera através desse objeto (por exemplo, por um loop foreach), o PHP irá chamar a função generator toda vez que precisar de um valor, em seguida salva o estado do generator quando o valor é produzido, de modo que possa ser retomado quando o próximo valor for necessário.
Uma vez que não há mais valores a serem produzidos, a função generator pode simplesmente sair, e a chamada de código continua como se um array tivesse executado os valores.
Nota:
Um generator não pode retornar um valor: isso resultará num erro de compilação. Um retorno vazio é uma sintaxe válida e fará com que o generator seja encerrado.
O coração de uma função generator é a palavra chave yield. Na sua forma mais simples, uma declaração yield se parece muito com um retorno, exceto que em vez de parar a execução da função e retornar, o yield fornece um valor para o código de loop sobre o generator e pausa a execução da função do generator.
Exemplo #1 Um exemplo simples de valores yield
<?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";
}
?>
O exemplo acima irá imprimir:
1 2 3
Nota:
Internamente, chaves inteiras sequenciais serão pareadas com os valores entregues, assim como um array não associativo.
Se você usar um yield em um contexto da expressão (por exemplo, a direita de uma atribuição), você deve colocar a declaração yield entre parênteses no PHP 5. Por exemplo, isso é válido:
$data = (yield $value);
Mas isso não é válido terá como resultado um parse error no PHP 5:
$data = yield $value;
Os parênteses não são necessário no PHP 7.
A sintaxe pode ser usada em conjunto com o método Generator::send().
O PHP suporta arrays associativos e generators não são diferentes. Além do produzir valores simples, como mostrado acima, você também pode produzir uma chave ao mesmo tempo.
A sintaxe para preparar um par de chave/valor é muito semelhante ao utilizado para definir um array associativo, como mostrado abaixo.
Exemplo #2 Produzindo um par de chave/valor
<?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";
}
?>
O exemplo acima irá imprimir:
1: PHP Likes dollar signs 2: Python Likes whitespace 3: Ruby Likes blocks
Da mesma forma como acontece com o yield de valores simples mostrados anteriormente, produzir um par de chave/valor num contexto da expressão requer que a declaração do yield esteja entre parênteses:
$data = (yield $key => $value);
O yield pode ser chamado sem um argumento para produzir um valor NULL
com uma chave automática.
Exemplo #3 Produzindo valores NULL
os
<?php
function gen_three_nulls() {
foreach (range(1, 3) as $i) {
yield;
}
}
var_dump(iterator_to_array(gen_three_nulls()));
?>
O exemplo acima irá imprimir:
array(3) { [0]=> NULL [1]=> NULL [2]=> NULL }
Funções generator são capazes de produzir valores por referência bem como por valor. Isso é feito da mesma forma que retornar referências de funções: incluindo um & no início do nome da função.
Exemplo #4 Produzindo valores por referência
<?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).'... ';
}
?>
O exemplo acima irá imprimir:
2... 1... 0...
No PHP 7 a delegação de gerador permite retornar valores de outro gerador, objeto Traversable ou um array utilizando para isso a instrução yield from. O gerador externo retornará todos os valores do gerador interno, objeto ou array até que o mesmo não seja mais válido, a partir de onde a execução continuará no gerador externo.
Se um gerador é utilizado com yield from, a expressão yield from também retornará qualquer valor retornado pelo gerador interno.
Exemplo #5 Uso básico de 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 ";
}
?>
O exemplo acima irá imprimir:
1 2 3 4 5 6 7 8 9 10
Exemplo #6 yield from e valores retornados
<?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();
?>
O exemplo acima irá imprimir:
1 2 3 4 5 6 7 8 9 10