Generadores e Iteradores
Parte de la sección Programación Orientada a Objetos del Journey de PHP de Coddy — lección 62 de 91.
Generadores proporcionan una forma sencilla de crear iteradores sin implementar la interfaz Iterator completa. Una función generadora utiliza yield en lugar de return para producir valores uno a la vez, pausando la ejecución entre cada valor.
Aquí tienes un generador básico que produce una secuencia de números:
<?php
function countTo(int $max): Generator {
for ($i = 1; $i <= $max; $i++) {
yield $i;
}
}
foreach (countTo(3) as $number) {
echo "$number\n";
}
Resultado:
1
2
3La ventaja clave es la eficiencia de memoria. En lugar de construir un array completo en memoria, los generadores producen valores bajo demanda. Esto es crucial cuando se trabaja con grandes conjuntos de datos: un generador que procesa un millón de registros utiliza la misma memoria que uno que procesa diez.
Los generadores también pueden emitir pares clave-valor:
<?php
function userGenerator(): Generator {
yield 'admin' => 'Alice';
yield 'editor' => 'Bob';
}
foreach (userGenerator() as $role => $name) {
echo "$role: $name\n";
}
Salida:
admin: Alice
editor: BobPara hacer que sus propias clases sean iterables con foreach, implemente la interfaz Iterator. Sin embargo, en la mayoría de los casos, los generadores ofrecen una solución más limpia con menos código repetitivo. Son particularmente útiles en la POO cuando una clase necesita exponer una colección de elementos sin cargar todo en la memoria a la vez.
Desafío
FácilVamos a construir un sistema de inventario de productos que utiliza generadores para iterar eficientemente a través de grandes colecciones sin cargar todo en la memoria a la vez. Crearás una clase que almacena productos y proporciona métodos generadores para iterar a través de ellos de diferentes maneras.
Organizarás tu código en tres archivos:
Product.php— Crea una claseProductque represente un artículo en el inventario. Usa la promoción de constructor para definir un$sku(string) público, un$name(string) público y un$price(float) público.Inventory.php— Crea una claseInventoryque gestione una colección de productos y proporcione métodos generadores para iterar a través de ellos. Incluye el archivo Product. La clase debe:- Tener una propiedad de array privada para almacenar productos
- Tener un método
addProduct(Product $product)que añada un producto a la colección - Tener un método generador
allProducts(): Generatorque devuelva (yield) cada producto uno a la vez - Tener un método generador
expensiveProducts(float $minPrice): Generatorque devuelva (yield) solo los productos con un precio mayor o igual al mínimo dado, devolviendo pares clave-valor donde la clave es el SKU y el valor es el producto
main.php— Incluye el archivo Inventory. Recibirás una entrada: un umbral de precio mínimo.Crea un
Inventoryy añade estos tres productos:- SKU:
"A001", Name:"Laptop", Price:999.99 - SKU:
"A002", Name:"Mouse", Price:29.99 - SKU:
"A003", Name:"Monitor", Price:349.99
Primero, itera a través de
allProducts()e imprime el nombre de cada producto en su propia línea.Luego imprime una línea vacía, seguida de la iteración a través de
expensiveProducts()utilizando el umbral de entrada. Para cada producto costoso, imprime"[sku]: [name] - $[price]"en su propia línea. Formatea el precio con exactamente 2 decimales.- SKU:
Este desafío demuestra cómo los generadores te permiten iterar a través de colecciones de forma perezosa (lazy) — cada producto se genera solo cuando se solicita, lo que hace que este enfoque sea eficiente en memoria para inventarios grandes. La generación de pares clave-valor en expensiveProducts() muestra cómo los generadores pueden proporcionar datos de iteración estructurados.
Hoja de referencia
Los generadores proporcionan una forma eficiente en memoria de crear iteradores usando yield en lugar de return. Producen valores uno a la vez, pausando la ejecución entre cada valor.
Función generadora básica:
<?php
function countTo(int $max): Generator {
for ($i = 1; $i <= $max; $i++) {
yield $i;
}
}
foreach (countTo(3) as $number) {
echo "$number\n";
}
Los generadores pueden emitir pares clave-valor:
<?php
function userGenerator(): Generator {
yield 'admin' => 'Alice';
yield 'editor' => 'Bob';
}
foreach (userGenerator() as $role => $name) {
echo "$role: $name\n";
}
La principal ventaja es la eficiencia de memoria: los generadores producen valores bajo demanda en lugar de construir arrays completos en memoria. Esto es especialmente útil cuando se trabaja con grandes conjuntos de datos o colecciones en contextos de POO.
Pruébalo tú mismo
<?php
require_once 'Inventory.php';
// Leer la entrada
$minPrice = floatval(trim(fgets(STDIN)));
// TODO: Crear una instancia de Inventory
// TODO: Agregar tres productos:
// SKU: "A001", Name: "Laptop", Price: 999.99
// SKU: "A002", Name: "Mouse", Price: 29.99
// SKU: "A003", Name: "Monitor", Price: 349.99
// TODO: Iterar a través de allProducts() e imprimir el nombre de cada producto
// TODO: Imprimir una línea vacía
// TODO: Iterar a través de expensiveProducts() usando $minPrice
// Para cada producto costoso, imprimir "[sku]: [name] - $[price]"
// Formatear el precio con exactamente 2 decimales
?>Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a OOPClases vs ObjetosLa palabra clave $thisMétodosPropiedadesConstructor (__construct)Destructor (__destruct)Resumen - Calculadora simple4Herencia
Herencia básicaLa palabra clave parent::Sobrescritura de métodosLa palabra clave finalClases abstractasResumen - Jerarquía de empleados7Encapsulamiento
Público, Protegido, PrivadoModificadores de acceso a fondoGetters y SettersOcultamiento de informaciónPromoción de constructor (8.0)Resumen - Sistema de registros de estudiantes10Conceptos avanzados de POO
Composición vs. HerenciaInyección de dependenciasClases anónimasEnums (PHP 8.1)Fibers (PHP 8.1)Clonación de objetos a fondoGeneradores e Iteradores13Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository2Espacios de nombres y Autoloading
Introducción a los espacios de nombresLa palabra clave useEstándar de Autoloading PSR-4Autoloader de ComposerResumen - Proyecto organizado5Interfaces y Contratos
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesInterfaz vs Clase AbstractaType Hinting con InterfacesResumen - Calculadora de Formas8Métodos mágicos
Introducción a los métodos mágicos__toString y __debugInfo__get, __set, __isset, __unset__call y __callStatic__clone y clonación de objetos__serialize y __unserializeResumen - Colección personalizada3Propiedades de clase
Propiedades de instancia vs. estáticasConstantes en clasesMétodos y propiedades estáticosPropiedades privadas y protegidasPropiedades de solo lectura (PHP 8.1)Resumen - Gestor de cuenta bancaria6Polimorfismo
Sobrescritura de métodos revisadaPolimorfismo mediante interfacesType Hinting y tipos de uniónEnlace estático tardíoResumen - Procesador de pagos9Traits
Introducción a los TraitsUso de múltiples TraitsResolución de conflictos de TraitsMétodos abstractos en TraitsTraits frente a herencia12Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy