Генератори

Генератори

Старий інструмент, який більшість досі не використовує

  • Додано у версії: PHP 5.5
  • Що дає: функції з yield, які повертають елементи поступово — замість того, щоб збирати все в памʼяті й віддавати одразу.

Реальність така

Хоча генератори існують у PHP з 5.5, на співбесідах більшість розробників або не можуть пояснити, де саме вони реально економлять памʼять, або згадують їх лише теоретично — на кшталт "це щось для великих даних". А по практичним кейсам — нуль ідей.

Тому давайте я накину вам трохи ідей як і де їх можна використовувати.

Як вони працюють насправді

На відміну від звичайних ітераторів чи масивів:

  • генератор не знає, скільки буде елементів;
  • не знає, чи є наступний;
  • не пам’ятає, чи була попередня ітерація;
  • однопрохідний — пройшов, і все, повторно не повернешся.

А попри всі ці “обмеження” — він має одну суттєву перевагу: він може призупиняти власне виконання, що дозволяє не тримати в памʼяті всі стани, а тільки поточний.

Це не “масив”, а скоріше потік — і саме тому він безцінний у правильному місці.

Реальні кейси, де yield справді потрібен

1. Посторінкове читання з БД

function fetchUsers(PDO $db, int $limit = 500): Generator 
{
    $offset = 0;
    while (true) {
        $rows = $db->query("SELECT * FROM users LIMIT $limit OFFSET $offset")->fetchAll();
        if (!$rows) break;
        yield from $rows;
        $offset += $limit;
    }
}

Економія памʼяті колосальна — працює навіть на таблицях у сотні тисяч рядків.

2. Потоковий парсинг великих файлів

function readCsv(string $path): Generator 
{
    $h = fopen($path, 'r');
    while (($row = fgetcsv($h)) !== false) {
        yield $row;
    }
    fclose($h);
}

Читає рядок за рядком — не вантажить весь CSV в памʼять.

3. Потоковий запис результату в файл

function streamCsvRows(iterable $data): Generator 
{
    foreach ($data as $row) {
        yield implode(',', $row) . "\n";
    }
}

header('Content-Type: text/csv');
foreach (streamCsvRows($repository->getLargeDataset()) as $line) {
    echo $line;
    flush();
}

Відправка великих звітів чи експорту — без буферизації, у реальному часі.

4. Поступове завантаження з API

function fetchPages(ApiClient $client): Generator 
{
    $page = 1;
    do {
        $data = $client->getPage($page++);
        yield from $data['items'];
    } while ($data['has_more']);
}

Ідеально для стрімінгової обробки даних без буферизації.

5. Обхід великої файлової структури

function scanDirRecursive(string $path): Generator 
{
    foreach (scandir($path) as $item) {
        if ($item === '.' || $item === '..') continue;
        $full = "$path/$item";
        if (is_dir($full)) {
            yield from scanDirRecursive($full);
        } else {
            yield $full;
        }
    }
}

foreach (scanDirRecursive('/var/log') as $file) {
    echo $file . PHP_EOL;
}

Ідеально для обходу каталогів, навіть якщо їх тисячі.

Висновок

Генератори — це інструмент не для зручності, а для ефективності. Вони не дають можливості перемотати чи дізнатись розмір, але саме тому й дозволяють працювати з необмеженими потоками даних.

Якщо ти колись писав скрипт, який падав через out of memory, — yield твій новий друг.

← Попередня: Стрілкові функції · Уся серія · Наступна: DateTimeImmutable →


Вперше опубліковано в Telegram-каналі «Створений щоб помирати» 18.10.2025.