← Generators

Ashterix

docs: English JsonRpcBundle docs, REST and MCP Bundle sections, package cards

en/articles/from-tg/series/php-can-do/generators.md new file +129 βˆ’0

1 + # Generators
2 +
3 + *An old tool most people still don't use*
4 +
5 + - **Added in:** PHP 5.5
6 + - **What it gives you:** functions with `yield` that return elements one at a time β€” instead of collecting everything in memory and handing it over at once.
7 +
8 + ## The reality
9 +
10 + Even though generators have existed in PHP since 5.5, at interviews most developers either can't explain where exactly they save memory, or mention them only in theory β€” something like "it's a thing for big data". As for practical cases β€” zero ideas.
11 +
12 + So let me throw you a few ideas on how and where they can be used.
13 +
14 + ## How they actually work
15 +
16 + Unlike regular iterators or arrays:
17 +
18 + - a generator doesn't know how many elements there will be;
19 + - it doesn't know whether there's a next one;
20 + - it doesn't remember whether there was a previous iteration;
21 + - it's single-pass β€” once you've gone through it, that's it, you can't go back.
22 +
23 + And despite all these "limitations" it has one major advantage: it can pause its own execution, which means you don't keep all the states in memory, only the current one.
24 +
25 + It's not an "array" but rather a stream β€” and that's exactly why it's priceless in the right place.
26 +
27 + ## Real cases where yield is truly needed
28 +
29 + ### 1. Reading from the database page by page
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 + The memory savings are enormous β€” it works even on tables with hundreds of thousands of rows.
45 +
46 + ### 2. Streaming parsing of large files
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 + It reads line by line β€” the whole CSV never gets loaded into memory.
60 +
61 + ### 3. Streaming the result into a file
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 + Sending large reports or exports β€” without buffering, in real time.
79 +
80 + ### 4. Loading from an API gradually
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 + Perfect for streaming data processing without buffering.
94 +
95 + ### 5. Walking a large file tree
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 + Perfect for walking directories, even if there are thousands of them.
117 +
118 + ## Conclusion
119 +
120 + > Generators are a tool not for convenience, but for efficiency.
121 + > They don't let you rewind or find out the size, but that's exactly why they can handle unbounded streams of data.
122 +
123 + If you've ever written a script that crashed with *out of memory*, `yield` is your new friend.
124 +
125 + ← [Previous: Arrow functions](/articles/from-tg/series/php-can-do/arrow-functions) Β· [Whole series](/articles/from-tg/series/php-can-do) Β· [Next: DateTimeImmutable β†’](/articles/from-tg/series/php-can-do/datetime-immutable)
126 +
127 + ---
128 +
129 + *First published in Ukrainian in the Telegram channel [β€œCreated to die”](https://t.me/php_while_true/78) on October 18, 2025.*