Una función generadora es igual que una función normal, con la diferencia de que en vez de devolver un valor, un generador invoca yield tantas veces como necesita.
Cuando se llama a una función generadora, devuelve un objeto que puede ser iterado. Cuando se itera sobre ese objeto (por ejemplo, con un bucle foreach), PHP llamará a la función generadora cada vez que necesite un valor, y guardará el estado del generador cuando este provea un valor con yield para que ese estado pueda ser recuperado cuando el próximo valor sea requerido.
Cuando no hay más valores que se puedan proporcionar, la función generadora puede simplemente terminar, y el código desde el que se la llama continuará como si un array se hubiera quedado sin valores.
Nota:
Un generador no puede retornar un valor: hacerlo resultaría en un error de compilación. Un return vacío es válido en cuanto a sintaxis dentro de un generador y terminará el generador.
La clave de una función generadora es la palabra reservada yield. En su forma más simple, la sentencia yield es parecida a la sentencia return, excepto en que en vez de detener la ejecución de la función y devolver un valor, yield facilita el valor al bucle que itera sobre el generador y pausa la ejecución de la función generadora.
Ejemplo #1 Ejemplo sencillo de facilitar valores con yield
<?php
function gen_one_to_three() {
for ($i = 1; $i <= 3; $i++) {
// Observe que $i es preservado entre yields
yield $i;
}
}
$generator = gen_one_to_three();
foreach ($generator as $value) {
echo "$value\n";
}
?>
El resultado del ejemplo sería:
1 2 3
Nota:
Internamente, las claves enteras secuenciales serán asociadas con los valores sobre los que se usa yield, como un array no asociativo.
Si se utiliza yield en el contexto de una expresión (por ejemplo, en el lado derecho de una asignación), se debe poner la sentencia yield entre paréntesis. Por ejemplo, esto es válido:
$data = (yield $value);
Pero esto no lo es, y resultará en un error del intérprete:
$data = yield $value;
Esta sintaxis podría usarse junto con el método Generator::send().
PHP soporta arrays asociativos, y los generadores no son menos. Además de facilitar valores simples, como se muestra arriba, también se puede facilitar una clave al mismo tiempo.
La sintaxis para facilitar un par clave-valor es muy similar a la utilizada para definir un array asociativo, como se muestra a continuación.
Ejemplo #2 Facilitar un par clave-valor
<?php
/*
* La entrada son campos separados por punto y coma, con el primer
* campo siendo la ID utilizada como clave.
*/
$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";
}
?>
El resultado del ejemplo sería:
1: PHP Likes dollar signs 2: Python Likes whitespace 3: Ruby Likes blocks
Como en el ejemplo anterior, facilitar un par clave-valor en contexto de expresión requiere que la sentencia yield sea puesta entre paréntesis:
$data = (yield $key => $value);
Yield puede ser invocado sin argumentos para facilitar un valor NULL
con una
clave automática.
Ejemplo #3 Yielding NULL
s
<?php
function gen_three_nulls() {
foreach (range(1, 3) as $i) {
yield;
}
}
var_dump(iterator_to_array(gen_three_nulls()));
?>
El resultado del ejemplo sería:
array(3) { [0]=> NULL [1]=> NULL [2]=> NULL }
Las funciones generadoras son capaces de facilitar valores por referencia igual que lo hacen por valor. Esto se hace de la misma forma que devolviendo referencias desde funciones: poniendo un ampersand (signo &) delante del nombre de la función.
Ejemplo #4 Facilitar valores por referencia
<?php
function &gen_reference() {
$value = 3;
while ($value > 0) {
yield $value;
}
}
/*
* Observe que es posible cambiar $number desde dentro del bucle, y
* dado que el generador está facilitando referencias, $value
* dentro de gen_reference() cambia.
*/
foreach (gen_reference() as &$number) {
echo (--$number).'... ';
}
?>
El resultado del ejemplo sería:
2... 1... 0...