Menu
Coddy logo textTech

Generators & Iterators

Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 62 of 91.

Generators provide a simple way to create iterators without implementing the full Iterator interface. A generator function uses yield instead of return to produce values one at a time, pausing execution between each value.

Here's a basic generator that produces a sequence of numbers:

<?php
function countTo(int $max): Generator {
    for ($i = 1; $i <= $max; $i++) {
        yield $i;
    }
}

foreach (countTo(3) as $number) {
    echo "$number\n";
}

Output:

1
2
3

The key advantage is memory efficiency. Instead of building an entire array in memory, generators produce values on-demand. This is crucial when working with large datasets - a generator processing a million records uses the same memory as one processing ten.

Generators can also yield key-value pairs:

<?php
function userGenerator(): Generator {
    yield 'admin' => 'Alice';
    yield 'editor' => 'Bob';
}

foreach (userGenerator() as $role => $name) {
    echo "$role: $name\n";
}

Output:

admin: Alice
editor: Bob

To make your own classes iterable with foreach, implement the Iterator interface. However, for most cases, generators offer a cleaner solution with less boilerplate code. They're particularly useful in OOP when a class needs to expose a collection of items without loading everything into memory at once.

challenge icon

Challenge

Easy

Let's build a product inventory system that uses generators to efficiently iterate through large collections without loading everything into memory at once. You'll create a class that stores products and provides generator methods to iterate through them in different ways.

You'll organize your code across three files:

  • Product.php — Create a Product class that represents an item in inventory. Use constructor promotion to define a public $sku (string), a public $name (string), and a public $price (float).
  • Inventory.php — Create an Inventory class that manages a collection of products and provides generator methods to iterate through them. Include the Product file. The class should:
    • Have a private array property to store products
    • Have a method addProduct(Product $product) that adds a product to the collection
    • Have a generator method allProducts(): Generator that yields each product one at a time
    • Have a generator method expensiveProducts(float $minPrice): Generator that yields only products with a price greater than or equal to the given minimum, yielding key-value pairs where the key is the SKU and the value is the product
  • main.php — Include the Inventory file. You'll receive one input: a minimum price threshold.

    Create an Inventory and add these three products:

    • SKU: "A001", Name: "Laptop", Price: 999.99
    • SKU: "A002", Name: "Mouse", Price: 29.99
    • SKU: "A003", Name: "Monitor", Price: 349.99

    First, iterate through allProducts() and print each product's name on its own line.

    Then print an empty line, followed by iterating through expensiveProducts() using the input threshold. For each expensive product, print "[sku]: [name] - $[price]" on its own line. Format the price with exactly 2 decimal places.

This challenge demonstrates how generators let you iterate through collections lazily — each product is yielded only when requested, making this approach memory-efficient for large inventories. The key-value yielding in expensiveProducts() shows how generators can provide structured iteration data.

Cheat sheet

Generators provide a memory-efficient way to create iterators using yield instead of return. They produce values one at a time, pausing execution between each value.

Basic generator function:

<?php
function countTo(int $max): Generator {
    for ($i = 1; $i <= $max; $i++) {
        yield $i;
    }
}

foreach (countTo(3) as $number) {
    echo "$number\n";
}

Generators can yield key-value pairs:

<?php
function userGenerator(): Generator {
    yield 'admin' => 'Alice';
    yield 'editor' => 'Bob';
}

foreach (userGenerator() as $role => $name) {
    echo "$role: $name\n";
}

The main advantage is memory efficiency - generators produce values on-demand rather than building entire arrays in memory. This is especially useful when working with large datasets or collections in OOP contexts.

Try it yourself

<?php

require_once 'Inventory.php';

// Read input
$minPrice = floatval(trim(fgets(STDIN)));

// TODO: Create an Inventory instance

// TODO: Add three products:
// SKU: "A001", Name: "Laptop", Price: 999.99
// SKU: "A002", Name: "Mouse", Price: 29.99
// SKU: "A003", Name: "Monitor", Price: 349.99

// TODO: Iterate through allProducts() and print each product's name

// TODO: Print an empty line

// TODO: Iterate through expensiveProducts() using $minPrice
// For each expensive product, print "[sku]: [name] - $[price]"
// Format price with exactly 2 decimal places

?>
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming