A generator is a function that contains yield. Calling it does not run the body; it returns a Generator object that foreach can loop over. Each yield $value hands one value to the loop and pauses the function until the loop asks for the next one, so values are produced one at a time instead of being collected in an array first.
The output alternates between the generator and the loop: each value is made only when the loop asks for it.
Generators save memory
range(1, 1000000) builds an array of a million integers before the loop starts. A generator that yields the same numbers never holds more than one of them. Run it and compare the memory each version needs:
Both loops print the same sum, 500000500000, but the array version reports extra memory 16.0 MB and the generator extra memory 0.0 MB. The gap grows with the data. The same idea is what makes generators useful for large files, database results and API pages: process one item, drop it, take the next.
Yield keys and values
yield $key => $value sets the key that foreach ($gen as $key => $value) receives. Without a key, a generator numbers its values from 0, like a list. A common use is reading a file line by line with line numbers:
Only one line is in memory at a time, so the same function handles a 2 GB log file. fopen() and fgets() cover the file functions used here.
Infinite sequences and lazy pipelines
A generator can loop forever, because it only runs as far as the consumer asks. Stop with break, or wrap it in another generator that takes the first few values. Chaining small generators gives a pipeline where every step is lazy:
fibonacci() never ends on its own, yet the program finishes: take() stops asking after the requested count, and nothing beyond that is ever computed.
yield from: delegate to another generator
yield from passes through every value of an array, an iterator or another generator. It is how you split a generator into smaller ones, or walk a nested structure recursively:
yield from keeps the inner keys, so both arrays yield keys 0 and 1. iterator_to_array() then overwrites a and b. Pass false as the second argument whenever only the values matter.
send() and getReturn()
A generator can also receive values. $x = yield; pauses and waits; $gen->send($value) resumes the generator with $value as the result of that yield. And a generator can return a final value, which you read with getReturn() once it has finished.
Most code never needs send(); reading lazily with foreach is the everyday use. It is the building block behind coroutine libraries.
A generator can only be looped once
Generators run forward and cannot be rewound. Once a foreach has consumed one, a second loop over the same object throws:
The fix is to call the generator function again for each pass, foreach (colors() as $c), or to collect the values once with iterator_to_array(colors(), false) when you really need them twice. If you find yourself doing that often, a plain array was the right choice for that data.
Frequently Asked Questions
What does yield do in PHP?
yield $value hands one value to the code looping over the function and pauses the function right there. The next loop iteration resumes it after the yield. Any function containing yield returns a Generator object instead of running when you call it.
Why use a generator instead of an array?
An array holds every element in memory at once; a generator produces one element at a time. Looping over range(1, 1000000) allocates all million integers, while a generator that yields them keeps memory flat. Generators also allow infinite sequences and stop work early when the loop breaks.
What is yield from in PHP?
yield from $source passes along every value of another generator, an array or a Traversable, as if the current generator had yielded them itself. It also evaluates to the inner generator's return value.
Can I loop over a generator twice?
No. A generator runs forward only. After a foreach has finished, looping again throws Exception: Cannot traverse an already closed generator. Call the generator function again to get a new generator, or store the values with iterator_to_array() if you need them twice.
How do I convert a generator to an array?
iterator_to_array($gen) collects all values, keeping the generator's keys. If the keys repeat, for example after yield from, later values overwrite earlier ones, so pass false as the second argument to get a plain list: iterator_to_array($gen, false).