PHP / Magento Dev Blog

  • Home

Iterator and Generator in PHP – the pattern and modern syntax

by Henryk Tews / Monday, 12 October 2026 / Published in Wzorce projektowe

The Iterator pattern defines sequential access to collection elements without exposing its internal structure. PHP implements it through the Iterator and IteratorAggregate interfaces. Generators (available since PHP 5.5) are syntactic sugar that simplifies writing iterators considerably – especially for lazily generating large datasets.

The Iterator interface in PHP

<?php

declare(strict_types=1);

class ProductCollection implements Iterator
{
    private array $products = [];
    private int $position = 0;

    public function add(array $product): void
    {
        $this->products[] = $product;
    }

    public function current(): mixed
    {
        return $this->products[$this->position];
    }

    public function key(): int
    {
        return $this->position;
    }

    public function next(): void
    {
        $this->position++;
    }

    public function rewind(): void
    {
        $this->position = 0;
    }

    public function valid(): bool
    {
        return isset($this->products[$this->position]);
    }
}

$collection = new ProductCollection();
$collection->add(['sku' => 'SKU-001', 'name' => 'Widget', 'price' => 29.99]);
$collection->add(['sku' => 'SKU-002', 'name' => 'Gadget', 'price' => 49.99]);

foreach ($collection as $key => $product) {
    echo "{$key}: {$product['sku']} - {$product['name']}\n";
}

IteratorAggregate – simpler approach

<?php

declare(strict_types=1);

class OrderRepository implements IteratorAggregate
{
    private array $orders = [];

    public function add(array $order): void
    {
        $this->orders[] = $order;
    }

    public function getIterator(): ArrayIterator
    {
        return new ArrayIterator($this->orders);
    }

    public function pending(): ArrayIterator
    {
        $pending = array_filter(
            $this->orders,
            fn($o) => $o['status'] === 'pending'
        );
        return new ArrayIterator(array_values($pending));
    }
}

Generator – iterator without boilerplate

A generator is a function with a yield statement. PHP automatically implements the Generator interface (which implements Iterator) for it. Useful when generating elements is expensive or the dataset is too large to hold in memory:

<?php

declare(strict_types=1);

// Infinite Fibonacci generator
function fibonacci(): Generator
{
    [$a, $b] = [0, 1];
    while (true) {
        yield $a;
        [$a, $b] = [$b, $a + $b];
    }
}

// Stream large CSV without loading into memory
function readCsvRows(string $filePath): Generator
{
    $handle = fopen($filePath, 'r');
    if ($handle === false) {
        throw new \RuntimeException("Cannot open: {$filePath}");
    }

    $headers = fgetcsv($handle);

    try {
        while (($row = fgetcsv($handle)) !== false) {
            yield array_combine($headers, $row);
        }
    } finally {
        fclose($handle);
    }
}

// Processes 100k CSV rows with constant memory
foreach (readCsvRows('/tmp/products.csv') as $row) {
    processProduct($row);
}

// yield from - delegate to another generator
function mergeGenerators(Generator ...$generators): Generator
{
    foreach ($generators as $gen) {
        yield from $gen;
    }
}

// Generator with key => value
function indexedProducts(array $products): Generator
{
    foreach ($products as $product) {
        yield $product['sku'] => $product;
    }
}

Generators and send() – two-way communication

<?php

function logger(): Generator
{
    $messages = [];
    while (true) {
        $message = yield count($messages);
        if ($message === null) break;
        $messages[] = $message;
        echo "[LOG] {$message}\n";
    }
    return $messages;
}

$log = logger();
$log->current(); // initialise

$log->send('Order #123 accepted');
$log->send('Payment confirmed');
$log->send('Shipping dispatched');
$log->send(null);

$allMessages = $log->getReturn();

Iterator in Magento 2

Magento\Framework\Data\Collection implements IteratorAggregate – which is why you can foreach over it. ResourceModel\Collection\AbstractCollection adds lazy loading from the database: the collection is actually loaded at the first iteration or getItems() call. For large datasets (reindexing, for example) Magento uses BatchProviderInterface which internally acts like a generator, fetching data page by page.

About Henryk Tews

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}