Generators & yield (PHP 5.5)
debt(d7/e3/b3/t5)
Closest to 'only careful code review or runtime testing' (d7), because the detection_hints note automated=no and the code_pattern targets file() or fetchAll() calls — phpstan can flag these patterns but won't reliably identify every missed generator opportunity. The absence of a generator is a sins-of-omission problem: no error is thrown, no warning fires by default, and memory exhaustion only surfaces at runtime with large datasets.
Closest to 'simple parameterised fix' (e3), because the quick_fix describes replacing array-returning functions with generator functions using yield — a contained refactor within one function or component. It's more than a one-line swap (callers must tolerate a Generator instead of an array, and foreach loops replace array operations), but it typically stays within a single file or small component.
Closest to 'localised tax' (b3), because the choice to use or not use generators is scoped to specific data-loading functions. Once adopted, callers need to use foreach/iterator patterns rather than array functions, imposing a small but localised tax. The rest of the codebase is largely unaffected unless the pattern is used pervasively.
Closest to 'notable trap' (t5), because the misconception field explicitly states that developers treat generators as 'syntax sugar for arrays' when they are fundamentally different (lazy evaluation, constant memory, forward-only, non-rewindable). The common mistakes confirm this: developers load large data into arrays instead of yielding, and are surprised that generators cannot be rewound. This is a documented gotcha that most PHP devs eventually learn.
TL;DR
Explanation
A generator function uses yield instead of return. Calling the function returns a Generator object (implements Iterator). Values are computed on demand. yield $value; pauses the function, returning the value. Execution resumes on next iteration. yield $key => $value; for key-value pairs. yield from iterable; delegates to another generator. send($value) passes a value back into the generator. Generators use O(1) memory regardless of dataset size — vs O(n) for arrays. Use cases: reading large files line by line, database cursors, infinite sequences, lazy pipelines.
Common Misconception
Why It Matters
Common Mistakes
- Not using generators for large file/database iteration — loading into array instead.
- Not knowing yield from can delegate to sub-generators.
- Trying to rewind a generator — they're forward-only by default.
Code Examples
// Memory issue — loads everything:
function readCsv(string $file): array {
return file($file); // Fatal on large files
}
// Constant memory — O(1):
function readCsvLines(string $file): Generator {
$handle = fopen($file, 'r');
while (($line = fgetcsv($handle)) !== false) {
yield $line;
}
fclose($handle);
}
foreach (readCsvLines('huge.csv') as $row) {
processRow($row);
}