Skip to content

13 generadores en php

@intelguasoft edited this page Jan 22, 2020 · 1 revision

Curso Master PHP

Generadores (Generators)

Los iteradores en PHP son muy útiles pero a veces puede resultar tedioso a la hora de implementar las numerosas interfaces para crear el objeto a iterar. Desde PHP 5.5 están disponibles los generadores.

Un generador es como una función que actúa como un iterador y que devuelve un array. Un generador permite emplear foreach para iterar datos de forma que no se necesite cargar el array en la memoria o se requiera mucho tiempo para generarse. En su lugar se escribe una función generadora, que es como una función normal.

En lugar de usar la palabra return usa yield. Yield significa producir/ceder (en este contexto). Yield funciona de forma similar a return ya que devuelve un valor, pero en lugar de remover la función de la pila, yield guarda su estado, de esta forma puede continuar por donde estaba si se le llama otra vez. No es posible devolver un valor desde un generador pero si que se puede emplear return sin un valor para terminar su ejecución.

Por ejemplo en la función range() PHP tiene que generar un array con cada uno de los valores y devolverlo, lo que puede resultar en grandes arrays y consumir muchos recursos. Como alternativa se puede generar un generador xrange() que sólo necesitará memoria para crear un objeto Iterator y controlar el estado actual del generador.

function xrange($start, $limit, $step = 1){
    if ($start < $limit){
        if($step <= 0){
            throw new LogicException("Step tiene que ser positivo");
        }
        for ($i = $start; $i <= $limit; $i += $step){
            yield $i;
        }
    } else {
        if($step >= 0){
            throw new LogicException("Step tiene que ser negativo");
        }
        for ($i = $start; $i <= $limit; $i += $step){
            yield $i;
        }
    }
}

Tanto range() como xrange() producirán la misma salida:

echo "Números impares de una cifra con range():";
foreach(range(1, 9, 2) as $numero){
    echo "$numero, ";
}
echo "<br>";
echo "Numero impares de una cifra con xrange():";
foreach(xrange(1, 9, 2) as $numero){
    echo "$numero, ";
}

Cuando se llama a una función generator, devuelve un objeto con el que se puede iterar. Este objeto es de la clase Generator e implementa la interface Iterator de la misma forma en que lo hace un objeto iterador. Cuando iteras sobre ese objeto, PHP llama al generador cada vez que necesita un valor. El estado se guarda cuando el generador hace yield por lo que puede resumirse después cuando se necesite el próximo valor. Un ejemplo más sencillo:

function numeros(){
    echo "El generador ha empezado <br>";
    for ($i = 0; $i < 5; ++$i){
        yield $i;
        echo "Se ha hecho yield en $i <br>";
    }
    echo "El generador ha terminado <br>";
}
foreach(numeros() as $numero);
/*
Devuelve:
El generador ha empezado
Se ha hecho yield en 0
Se ha hecho yield en 1
Se ha hecho yield en 2
Se ha hecho yield en 3
Se ha hecho yield en 4
El generador ha terminado
*/

Los generadores no son un concepto nuevo, ya existen en C#, Python, JavaScript y Ruby (enumerators), y se suelen identificar con el uso de la palabra yield. Ahora vamos a emplear un ejemplo para recorrer las líneas de un archivo:

function lineasDeArchivo($nombreArchivo){
    $archivo = fopen($nombreArchivo, 'r');
    while (($linea = fgets($archivo)) !== false){
        yield $linea;
    }
    fclose($archivo);
}
foreach (lineasDeArchivo("archivo.txt") as $linea){
    echo $linea . "<br>";
}

La función generator abre el archivo y hace yield en cada línea del archivo cuando se le ordena. Cada vez que se llama a generator, continúa desde donde lo dejó. Cuando se han leído todas las líneas el generador termina junto con el loop.

Los iteradores en PHP están formados por parejas key/value. En el ejemplo anterior sólo se ha devuelto value, las keys eran numéricas (es así por defecto). Para devolver un array asociativo simplemente hay que usar la misma sintaxis que con los arrays asociativos:

foreach (lineasDeArchivo($nombreArchivo) as $key => $value) {
...
}

Pero yield no sólo devuelve valores, puede recibirlos también. Esto se hace mediante el método send() del objeto generator con el valor que quieras pasar. Este valor puede usarse para computar o para cualquier otra cosa:

function numeros(){
    for ($i = 0; $i < 10; ++$i){
        // Obtener un valor de llamada
        $cmd = (yield $i);
        if ($cmd == 'parar'){
            return; // Salir del generador
        }
    }
}
$generador = numeros();
foreach($generador as $valor){
    // Sólo queremos mostrar hasta 5
    if($valor == 5){
        $generador->send('parar');
    }
    echo "$valor, ";
}
// Devuelve: 0, 1, 2, 3, 4, 5

Los generadores son especialmente útiles para calcular grandes conjuntos de datos y no se quiere emplear excesiva memoria para mostrar todos al mismo tiempo o cuando no se sabe si se necesitarán todos los resultados. Debido a la forma en que se procesan los resultados con los generadores, el uso de memoria se puede reducir al mínimo asignando la memoria sólo al resultado actual.

Vamos a poner un ejemplo de la función file(), que devuelve todas las líneas de un archivo como un array, frente a la función que hemos creado antes, lineasDeArchivo():

// Usando lineasDeArchivo()
$memoria = memory_get_peak_usage();
foreach (lineasDeArchivo("archivo.txt") as $a);
echo memory_get_peak_usage() - $memoria . "<br>"; // 10400
// Usando file()
$memoria = memory_get_peak_usage();
foreach(file("archivo.txt") as $a);
echo memory_get_peak_usage() - $memoria . "<br>"; // 25536

Si se emplea con pocas líneas emplea un poco más de memoria lineasDeArchivo(), pero cuando el archivo comienza a tener un número de líneas considerable, file() aumenta mucho su uso y lineasDeArchivo() mantiene su valor.

Anterior Siguiente

Indice de contenidos

Básicos Sintaxis básica
Operadores
Operadores bit a bit
Variables
Estructuras de control
Constantes y constructores base
Espacio de nombres
Extensiones
Configuraciones
Variables al descubierto
Recolector de basuras
Rendimiento (Performance)
Funciones Funciones
Argumentos en funciones
Funciones variables
Valores por referencia en funciones
Funciones que devuelven algo
Ámbito de variables
Funciones anónimas y closure's
Cadenas y patrones Las comillas y las cadenas de caracteres
Heredoc y Nowdoc
Comparando cadenas de caracteres
Extracción en cadenas de caracteres
Análisis en cadenas de caracteres
Reemplazos en cadenas de caracteres
Formato de cadena de caracteres
Expresiones regulares (RegEx)
Codificación de caracteres
Codificación en cadenas de caracteres
Arreglos (Array's) Arreglos
Arreglos asociativos
Iterar arreglos
Funciones de arreglos
SPL Arreglos mediante objetos
Conversión de arreglos
Archivos (I/O) Manejo de archivos
Lectura de archivos
Escritura de archivos
Funciones del sistema de archivos
Socket's y archivos
Streams (?)
Seguridad Concepto y funcionamiento CGI
Configurando la seguridad
Seguridad en Sesiones
Ataques XSS
Ataques CSRF
Ataques SQLInjection
Ataques CodeInjection
Ataques EmailInjection
Filtrado de datos de entrada
Escape de datos de salida
Encriptación y contraseñas
Seguridad en el almacenamiento de datos
Seguridad en la subida de datos
SSL y OpenSSL
Base de datos Principios básicos SQL
Los joins en SQL
Principales funciones SQL
Preparando sentencias SQL
Transacciones en SQL
Algo de PDO
Programación Orientada a Objetos Instancias de clases
Modificadores y herencia de clases
Interfaces
Excepciones
Auto-carga (Autoload)
Reflexión (Reflection)
Determinación de tipos (Type hinting)
Constantes de clase
Enlace estático de ejecución (Late Static Binding)
Métodos mágicos
Librería estándar PHP (SPL)
Generadores (Generators)
Traits
Clases abstractas
Formatos de información Algo de XML
Algo de SimpleXML
Algo de XML Parser
Algo de PHP DOM
Algo de Web Services
Web Services con SOAP
Algo de REST
Algo de JSON
Formato de fecha y hora
Características web's Sesiones
Formularios
Métodos GET y POST
Cookies
Protocolo HTTP y sus headers
Autenticación HTTP
Códigos de estado HTTP
Referencias Referencias
Recopilación
Conclusión

Clone this wiki locally