docs: English JsonRpcBundle docs, REST and MCP Bundle sections, package cards
uk/articles/from-tg/series/php-can-do/generators.md
новий файл +129
−0
1
+
# Генератори
2
+
3
+
*Старий інструмент, який більшість досі не використовує*
4
+
5
+
- **Додано у версії:** PHP 5.5
6
+
- **Що дає:** функції з `yield`, які повертають елементи поступово — замість того, щоб збирати все в памʼяті й віддавати одразу.
7
+
8
+
## Реальність така
9
+
10
+
Хоча генератори існують у PHP з 5.5, на співбесідах більшість розробників або не можуть пояснити, де саме вони реально економлять памʼять, або згадують їх лише теоретично — на кшталт "це щось для великих даних". А по практичним кейсам — нуль ідей.
11
+
12
+
Тому давайте я накину вам трохи ідей як і де їх можна використовувати.
13
+
14
+
## Як вони працюють насправді
15
+
16
+
На відміну від звичайних ітераторів чи масивів:
17
+
18
+
- генератор не знає, скільки буде елементів;
19
+
- не знає, чи є наступний;
20
+
- не пам’ятає, чи була попередня ітерація;
21
+
- однопрохідний — пройшов, і все, повторно не повернешся.
22
+
23
+
А попри всі ці “обмеження” — він має одну суттєву перевагу: він може призупиняти власне виконання, що дозволяє не тримати в памʼяті всі стани, а тільки поточний.
24
+
25
+
Це не “масив”, а скоріше потік — і саме тому він безцінний у правильному місці.
26
+
27
+
## Реальні кейси, де yield справді потрібен
28
+
29
+
### 1. Посторінкове читання з БД
30
+
31
+
```php
32
+
function fetchUsers(PDO $db, int $limit = 500): Generator
33
+
{
34
+
$offset = 0;
35
+
while (true) {
36
+
$rows = $db->query("SELECT * FROM users LIMIT $limit OFFSET $offset")->fetchAll();
37
+
if (!$rows) break;
38
+
yield from $rows;
39
+
$offset += $limit;
40
+
}
41
+
}
42
+
```
43
+
44
+
Економія памʼяті колосальна — працює навіть на таблицях у сотні тисяч рядків.
45
+
46
+
### 2. Потоковий парсинг великих файлів
47
+
48
+
```php
49
+
function readCsv(string $path): Generator
50
+
{
51
+
$h = fopen($path, 'r');
52
+
while (($row = fgetcsv($h)) !== false) {
53
+
yield $row;
54
+
}
55
+
fclose($h);
56
+
}
57
+
```
58
+
59
+
Читає рядок за рядком — не вантажить весь CSV в памʼять.
60
+
61
+
### 3. Потоковий запис результату в файл
62
+
63
+
```php
64
+
function streamCsvRows(iterable $data): Generator
65
+
{
66
+
foreach ($data as $row) {
67
+
yield implode(',', $row) . "\n";
68
+
}
69
+
}
70
+
71
+
header('Content-Type: text/csv');
72
+
foreach (streamCsvRows($repository->getLargeDataset()) as $line) {
73
+
echo $line;
74
+
flush();
75
+
}
76
+
```
77
+
78
+
Відправка великих звітів чи експорту — без буферизації, у реальному часі.
79
+
80
+
### 4. Поступове завантаження з API
81
+
82
+
```php
83
+
function fetchPages(ApiClient $client): Generator
84
+
{
85
+
$page = 1;
86
+
do {
87
+
$data = $client->getPage($page++);
88
+
yield from $data['items'];
89
+
} while ($data['has_more']);
90
+
}
91
+
```
92
+
93
+
Ідеально для стрімінгової обробки даних без буферизації.
94
+
95
+
### 5. Обхід великої файлової структури
96
+
97
+
```php
98
+
function scanDirRecursive(string $path): Generator
99
+
{
100
+
foreach (scandir($path) as $item) {
101
+
if ($item === '.' || $item === '..') continue;
102
+
$full = "$path/$item";
103
+
if (is_dir($full)) {
104
+
yield from scanDirRecursive($full);
105
+
} else {
106
+
yield $full;
107
+
}
108
+
}
109
+
}
110
+
111
+
foreach (scanDirRecursive('/var/log') as $file) {
112
+
echo $file . PHP_EOL;
113
+
}
114
+
```
115
+
116
+
Ідеально для обходу каталогів, навіть якщо їх тисячі.
117
+
118
+
## Висновок
119
+
120
+
> Генератори — це інструмент не для зручності, а для ефективності.
121
+
> Вони не дають можливості перемотати чи дізнатись розмір, але саме тому й дозволяють працювати з необмеженими потоками даних.
122
+
123
+
Якщо ти колись писав скрипт, який падав через *out of memory*, — `yield` твій новий друг.
124
+
125
+
← [Попередня: Стрілкові функції](/articles/from-tg/series/php-can-do/arrow-functions) · [Уся серія](/articles/from-tg/series/php-can-do) · [Наступна: DateTimeImmutable →](/articles/from-tg/series/php-can-do/datetime-immutable)
126
+
127
+
---
128
+
129
+
*Вперше опубліковано в Telegram-каналі [«Створений щоб помирати»](https://t.me/php_while_true/78) 18.10.2025.*