Generators let you stream big files and query results without loading everything into memory.
A function containing yield becomes a generator: calling it returns a Generator object and runs nothing yet. Each foreach step resumes the function until the next yield, receives that value, and pauses again.
function lines(string $path): Generator {
$h = fopen($path, 'r');
try {
while (($line = fgets($h)) !== false) yield rtrim($line, "\n");
} finally { fclose($h); }
}This reads a 10 GB file with constant memory, because only one line exists at a time. Generators compose: one generator can filter or map another, building lazy pipelines.
yield $key => $valueyields keys too.yield from $otherdelegates to another generator or array.- A generator can
returna final value, read with$gen->getReturn()after it finishes. $gen->send($x)resumes it and makes the pausedyieldexpression evaluate to$x, enabling two-way coroutines.
A generator runs once; it can't be rewound after it has started. To make an object iterable many times, implement IteratorAggregate and return a fresh generator from getIterator().
Watch out with iterator_to_array($gen): it uses keys by default, and yield from restarts keys at 0 for each inner source, so values overwrite each other. Pass preserve_keys: false.
Example
<?php
function numbers(int $max): Generator {
for ($i = 1; $i <= $max; $i++) {
echo "[make $i]";
yield $i;
}
return 'done';
}
function evens(iterable $xs): Generator {
foreach ($xs as $x) if ($x % 2 === 0) yield $x;
}
$gen = numbers(4);
foreach (evens($gen) as $n) echo " got $n\n";
// [make 1][make 2] got 2
// [make 3][make 4] got 4
echo $gen->getReturn(), "\n"; // done
function both(): Generator { yield from [1, 2]; yield from [3]; }
print_r(iterator_to_array(both())); // [0 => 3, 1 => 2]: keys collided!
print_r(iterator_to_array(both(), preserve_keys: false)); // [1, 2, 3]Remember
- Calling a generator function runs nothing until you iterate
- Generators stream data in constant memory and compose into pipelines
- Use preserve_keys: false with iterator_to_array after yield from
Watch out for
- Trying to iterate the same generator twice; it throws once it has run.