(PHP 5 >= 5.5.0, PHP 7, PHP 8)
Los generadores proporcionan una manera sencilla de implementar iteradores sin el costo ni la complejidad de desarrollar una clase que implemente la interfaz Iterator.
Un generador ofrece un medio conveniente para proporcionar datos a las bucles foreach sin tener que construir un array en memoria de antemano, lo cual podría llevar al programa a exceder un límite de memoria o requerir un tiempo de procesamiento considerable para generarlos. En su lugar, se puede utilizar una función generadora, que es idéntica a una función normal, excepto que en lugar de devolver una sola vez, un generador puede utilizar yield tantas veces como sea necesario, para proporcionar los valores a recorrer. Al igual que con los iteradores, el acceso aleatorio a los datos no es posible.
Un ejemplo sencillo de este mecanismo es la reimplementación de la función range() en forma de generador. La función estándar range() debe generar un array que contenga cada valor y devolverlo, lo cual puede llevar a arrays de gran tamaño: por ejemplo, la llamada al código range(0, 1000000) puede consumir significativamente más de 100 MB de memoria.
Como alternativa, se puede implementar un generador
xrange()
, que solo necesitará memoria para la creación de un objeto Iterator, y deberá mantener internamente el estado actual del generador, lo cual resulta en un consumo de memoria inferior a 1 KB.
Ejemplo #1 Implementación de la función range() en forma de generador
<?php
function xrange($start, $limit, $step = 1) {
if ($start <= $limit) {
if ($step <= 0) {
throw new LogicException('El paso debe ser positivo');
}
for ($i = $start; $i <= $limit; $i += $step) {
yield $i;
}
} else {
if ($step >= 0) {
throw new LogicException('El paso debe ser negativo');
}
for ($i = $start; $i >= $limit; $i += $step) {
yield $i;
}
}
}
/*
* Es de notar que las funciones range() y xrange() producen el
* mismo resultado, a continuación.
*/
echo 'Números impares de un solo dígito desde range(): ';
foreach (range(1, 9, 2) as $number) {
echo "$number ";
}
echo "\n";
echo 'Números impares de un solo dígito desde xrange(): ';
foreach (xrange(1, 9, 2) as $number) {
echo "$number ";
}
?>
El resultado del ejemplo sería:
Números impares de un solo dígito desde range(): 1 3 5 7 9 Números impares de un solo dígito desde xrange(): 1 3 5 7 9
Cuando se llama a una función generadora, se devuelve un objeto de la clase interna Generator. Este objeto implementa la interfaz Iterator de la misma manera que lo haría un objeto iterador que solo avanza, y proporciona los métodos que pueden ser llamados para manipular el estado del generador, incluyendo el envío de valores y sus retornos.