Menu
Coddy logo textTech

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
3

L'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: Bob

Pour 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.

challenge icon

Défi

Facile

Construisons 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 classe Product qui représente un article dans l'inventaire. Utilisez la promotion de constructeur pour définir un $sku public (string), un $name public (string), et un $price public (float).
  • Inventory.php — Créez une classe Inventory qui 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(): Generator qui produit (yield) chaque produit un par un
    • Avoir une méthode de générateur expensiveProducts(float $minPrice): Generator qui 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 Inventory et 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.

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

?>
quiz iconTestez-vous

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