PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

Iterator i Generator w PHP – wzorzec i nowoczesna składnia

by Henryk Tews / poniedziałek, 12 października 2026 / Opublikowano w Wzorce projektowe

Wzorzec Iterator definiuje sposób sekwencyjnego dostępu do elementów kolekcji bez ujawniania jej wewnętrznej struktury. PHP implementuje go przez interfejsy Iterator i IteratorAggregate. Generatory (dostępne od PHP 5.5) to składniowy cukier który znacznie upraszcza pisanie iteratorów – szczególnie przy leniwym generowaniu dużych zbiorów danych.

Interfejs Iterator w PHP

<?php

declare(strict_types=1);

// Klasyczna implementacja Iteratora - kolekcja produktów
class ProductCollection implements Iterator
{
    private array $products = [];
    private int $position = 0;

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

    // Metody interfejsu Iterator
    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]);
$collection->add(['sku' => 'SKU-003', 'name' => 'Doohickey', 'price' => 9.99]);

foreach ($collection as $key => $product) {
    echo "{$key}: {$product['sku']} - {$product['name']}\n";
}
// Kolekcja jest reużywalna - foreach możemy wywołać wielokrotnie

IteratorAggregate – prostsze podejście

<?php

declare(strict_types=1);

// IteratorAggregate - deleguje iterację do wewnętrznego iteratora
// Prostsze gdy kolekcja już ma strukturę iterowalną
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);
    }

    // Możesz dodać metody filtrujące zwracające nowy iterator
    public function pending(): ArrayIterator
    {
        $pending = array_filter(
            $this->orders,
            fn($o) => $o['status'] === 'pending'
        );
        return new ArrayIterator(array_values($pending));
    }
}

$repo = new OrderRepository();
foreach ($repo->pending() as $order) {
    // przetwarzaj zamówienia pending
}

Generator – iterator bez boilerplate

Generator to funkcja z instrukcją yield. PHP automatycznie implementuje za nią interfejs Generator (który implementuje Iterator). Przydatny szczególnie gdy generowanie kolejnych elementów jest kosztowne lub zbiór jest za duży żeby trzymać całość w pamięci:

<?php

declare(strict_types=1);

// Generator Fibonacciego - nieskończony ciąg, leniwie generowany
function fibonacci(): Generator
{
    [$a, $b] = [0, 1];
    while (true) {
        yield $a;
        [$a, $b] = [$b, $a + $b];
    }
}

$fib = fibonacci();
for ($i = 0; $i < 10; $i++) {
    echo $fib->current() . ' ';
    $fib->next();
}
// 0 1 1 2 3 5 8 13 21 34

// Praktyczny przykład: strumieniowe czytanie dużego CSV
// bez wczytywania całego pliku do pamięci
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);
    }
}

// Przetwarzanie 100k wierszy CSV - stałe zużycie pamięci
foreach (readCsvRows('/tmp/products.csv') as $row) {
    // $row to tablica asocjacyjna: ['sku' => '...', 'name' => '...', ...]
    // Każdy wiersz jest czytany na żądanie - nie wszystkie naraz
    processProduct($row);
}

// yield from - delegowanie do innego generatora
function mergeGenerators(Generator ...$generators): Generator
{
    foreach ($generators as $gen) {
        yield from $gen;
    }
}

// Generator z kluczem i wartością
function indexedProducts(array $products): Generator
{
    foreach ($products as $product) {
        yield $product['sku'] => $product; // klucz => wartość
    }
}

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

Generatory a send() – dwukierunkowa komunikacja

<?php

// Generator może odbierać wartości przez send()
// Przydatne do budowania prostych korutyn
function logger(): Generator
{
    $messages = [];
    while (true) {
        $message = yield count($messages); // yield zwraca liczbę zebranych wiadomości
        if ($message === null) break;
        $messages[] = $message;
        echo "[LOG] {$message}\n";
    }
    return $messages; // getReturn() po zakończeniu generatora
}

$log = logger();
$log->current(); // inicjalizacja - dochodzimy do pierwszego yield

$count = $log->send('Zamówienie #123 przyjęte'); // 1
$count = $log->send('Płatność zatwierdzona');    // 2
$count = $log->send('Wysyłka zlecona');          // 3
$log->send(null); // koniec

$allMessages = $log->getReturn();

Iterator w Magento 2

Magento 2 intensywnie używa tego wzorca. Klasa Magento\Framework\Data\Collection implementuje IteratorAggregate – stąd można po niej iterować foreach. ResourceModel\Collection\AbstractCollection rozszerza to o lazy loading z bazy danych: kolekcja jest faktycznie ładowana dopiero przy pierwszej iteracji lub wywołaniu getItems(). Do pracy z dużymi zbiorami danych (np. reindeksacja) Magento używa BatchProviderInterface który wewnętrznie działa jak generator – pobiera dane stronami.

About Henryk Tews

Co możesz przeczytać następne

Wzorzec Visitor – double dispatch, eksport CSV/PDF, walidacja bez modyfikacji klas
Wzorzec Factory Method – Simple Factory, GoF, auto-generowane fabryki Magento 2
Wzorzec Specification – enkapsulacja reguł biznesowych, AND/OR/NOT kompozycja, testowanie
  • Publikacje
  • O autorze
  • Kontakt

© 2026 Created by

GÓRA
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 Zawsze aktywne
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.
  • Zarządzaj opcjami
  • Zarządzaj serwisami
  • Zarządzaj {vendor_count} dostawcami
  • Przeczytaj więcej o tych celach
Zobacz preferencje
  • {title}
  • {title}
  • {title}