Générateurs et Itérateurs
Fait partie de la section Programmation Orientée Objet du Journey PHP de Coddy — leçon 62 sur 91.
Les générateurs fournissent un moyen simple de créer des itérateurs sans implémenter l'interface Iterator complète. Une fonction génératrice utilise yield au lieu de return pour produire des valeurs une à une, en mettant l'exécution en pause entre chaque valeur.
Voici un générateur de base qui produit une séquence de nombres :
<?php
function countTo(int $max): Generator {
for ($i = 1; $i <= $max; $i++) {
yield $i;
}
}
foreach (countTo(3) as $number) {
echo "$number\n";
}
Résultat :
1
2
3L'avantage principal est l'efficacité de la mémoire. Au lieu de construire un tableau entier en mémoire, les générateurs produisent des valeurs à la demande. C'est crucial lors de l'utilisation de grands ensembles de données - un générateur traitant un million d'enregistrements utilise la même quantité de mémoire qu'un générateur en traitant dix.
Les générateurs peuvent également produire des paires clé-valeur :
<?php
function userGenerator(): Generator {
yield 'admin' => 'Alice';
yield 'editor' => 'Bob';
}
foreach (userGenerator() as $role => $name) {
echo "$role: $name\n";
}
Sortie :
admin: Alice
editor: BobPour rendre vos propres classes itérables avec foreach, implémentez l'interface Iterator. Cependant, dans la plupart des cas, les générateurs offrent une solution plus propre avec moins de code répétitif. Ils sont particulièrement utiles en POO lorsqu'une classe doit exposer une collection d'éléments sans tout charger en mémoire en même temps.
Défi
FacileConstruisons un système d'inventaire de produits qui utilise des générateurs pour itérer efficacement à travers de grandes collections sans tout charger en mémoire en même temps. Vous allez créer une classe qui stocke des produits et fournit des méthodes de générateur pour itérer à travers eux de différentes manières.
Vous organiserez votre code sur trois fichiers :
Product.php— Créez une classeProductqui représente un article dans l'inventaire. Utilisez la promotion de constructeur pour définir un$skupublic (string), un$namepublic (string), et un$pricepublic (float).Inventory.php— Créez une classeInventoryqui gère une collection de produits et fournit des méthodes de générateur pour itérer à travers eux. Incluez le fichier Product. La classe doit :- Avoir une propriété de tableau privée pour stocker les produits
- Avoir une méthode
addProduct(Product $product)qui ajoute un produit à la collection - Avoir une méthode de générateur
allProducts(): Generatorqui produit (yield) chaque produit un par un - Avoir une méthode de générateur
expensiveProducts(float $minPrice): Generatorqui produit uniquement les produits dont le prix est supérieur ou égal au minimum donné, en produisant des paires clé-valeur où la clé est le SKU et la valeur est le produit
main.php— Incluez le fichier Inventory. Vous recevrez une entrée : un seuil de prix minimum.Créez un
Inventoryet ajoutez ces trois produits :- SKU:
"A001", Name:"Laptop", Price:999.99 - SKU:
"A002", Name:"Mouse", Price:29.99 - SKU:
"A003", Name:"Monitor", Price:349.99
Tout d'abord, itérez à travers
allProducts()et affichez le nom de chaque produit sur sa propre ligne.Ensuite, affichez une ligne vide, suivie d'une itération à travers
expensiveProducts()en utilisant le seuil d'entrée. Pour chaque produit cher, affichez"[sku]: [name] - $[price]"sur sa propre ligne. Formatez le prix avec exactement 2 décimales.- SKU:
Ce défi démontre comment les générateurs vous permettent d'itérer à travers des collections de manière paresseuse (lazy) — chaque produit n'est produit que lorsqu'il est demandé, ce qui rend cette approche économe en mémoire pour les grands inventaires. La production de paires clé-valeur dans expensiveProducts() montre comment les générateurs peuvent fournir des données d'itération structurées.
Aide-mémoire
Les générateurs offrent un moyen efficace en mémoire de créer des itérateurs en utilisant yield au lieu de return. Ils produisent des valeurs une à la fois, en mettant l'exécution en pause entre chaque valeur.
Fonction de générateur de base :
<?php
function countTo(int $max): Generator {
for ($i = 1; $i <= $max; $i++) {
yield $i;
}
}
foreach (countTo(3) as $number) {
echo "$number\n";
}
Les générateurs peuvent produire des paires clé-valeur :
<?php
function userGenerator(): Generator {
yield 'admin' => 'Alice';
yield 'editor' => 'Bob';
}
foreach (userGenerator() as $role => $name) {
echo "$role: $name\n";
}
L'avantage principal est l'efficacité de la mémoire - les générateurs produisent des valeurs à la demande plutôt que de construire des tableaux entiers en mémoire. Ceci est particulièrement utile lors de la manipulation de grands ensembles de données ou de collections dans des contextes de POO (Programmation Orientée Objet).
Essayez vous-même
<?php
require_once 'Inventory.php';
// Lire l'entrée
$minPrice = floatval(trim(fgets(STDIN)));
// TODO: Créer une instance d'Inventory
// TODO: Ajouter trois produits :
// SKU: "A001", Nom: "Laptop", Prix: 999.99
// SKU: "A002", Nom: "Mouse", Prix: 29.99
// SKU: "A003", Nom: "Monitor", Prix: 349.99
// TODO: Parcourir allProducts() et afficher le nom de chaque produit
// TODO: Afficher une ligne vide
// TODO: Parcourir expensiveProducts() en utilisant $minPrice
// Pour chaque produit cher, afficher "[sku]: [name] - $[price]"
// Formater le prix avec exactement 2 décimales
?>Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Les bases de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe mot-clé $thisMéthodesPropriétésConstructeur (__construct)Destructeur (__destruct)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLe mot-clé parent::Redéfinition de méthodeLe mot-clé finalClasses abstraitesRécapitulatif - Hiérarchie des employés7Encapsulation
Public, Protégé, PrivéModificateurs d'accès en détailGetters et SettersMasquage d'informationPromotion de constructeur (8.0)Récapitulatif - Système de gestion d'étudiants10Concepts avancés de la POO
Composition vs HéritageInjection de dépendancesClasses anonymesÉnumérations (PHP 8.1)Fibers (PHP 8.1)Clonage d'objets : analyse approfondieGénérateurs et Itérateurs13Patrons de conception Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode ModèlePatron ÉtatPatron CompositePatron Repository2Espaces de noms et Autoloading
Introduction aux espaces de nomsLe mot-clé useStandard d'Autoloading PSR-4L'Autoloader ComposerRécapitulatif - Projet organisé5Interfaces et Contrats
Introduction aux InterfacesImplémentation d'InterfacesImplémentation d'Interfaces MultiplesInterface vs Classe AbstraiteType Hinting avec les InterfacesRécapitulatif - Calculateur de Formes8Méthodes magiques
Introduction aux méthodes magiques__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Clonage d'objets__serialize & __unserializeRécapitulatif - Collection personnalisée3Propriétés de classe
Propriétés d'instance vs Propriétés statiquesConstantes dans les classesMéthodes et propriétés statiquesPropriétés privées et protégéesPropriétés readonly (PHP 8.1)Récapitulatif - Gestionnaire de compte bancaire6Polymorphisme
Retour sur la redéfinition de méthodePolymorphisme via les interfacesType Hinting et Types d'unionLiaison statique tardiveRécapitulatif - Processeur de paiement9Traits
Introduction aux TraitsUtilisation de plusieurs TraitsRésolution des conflits de TraitsMéthodes abstraites dans les TraitsTraits vs Héritage12Patrons de conception Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron ObservateurPatron Stratégie