Генератори
Старий інструмент, який більшість досі не використовує
- Додано у версії: 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.