A Generator is a special type of function in PHP that allows you to iterate over a sequence of values without building the entire dataset in memory at once.
Instead of returning all results in one go (like an array), a generator "yields" values one by one, only when requested by the consumer (e.g., a foreach loop).
This makes generators perfect for handling:
The yield Keyword
The magic behind generators is the yield keyword.
Instead of using return, you use yield to emit a value from the generator function. The function pauses execution after yield and resumes from the same point the next time a value is requested.
function numbersUpTo($limit) { for ($i = 1; $i <= $limit; $i++) { yield $i; } } foreach (numbersUpTo(5) as $number) { echo $number . PHP_EOL; } // 12345
Notice how we didn't need to store all numbers in an array. Each value is generated only when needed.
Yielding Keys and Values
Generators can yield both keys and values, just like arrays.
function squares($limit) { for ($i = 1; $i <= $limit; $i++) { yield $i => $i * $i; } } foreach (squares(5) as $number => $square) { echo "$number squared is $square"; } // 1 squared is 1 // 2 squared is 4 // 3 squared is 9 // 4 squared is 16 // 5 squared is 25
Returning Values from Generators
Starting with PHP 7.0, you can also use return inside a generator to provide a final return value. This doesn't affect iteration but can be retrieved via the getReturn() method on the generator object.
function processItems($items) { foreach ($items as $item) { yield strtoupper($item); } return "Done!"; } $gen = processItems(["apple", "banana"]); foreach ($gen as $value) { echo $value; } echo "Generator returned: " . $gen->getReturn(); // APPLE // BANANA // Generator returned: Done!
Comparing Generators and Iterator Objects
Before generators, if you wanted custom iteration, you typically implemented the Iterator interface. Let's compare the two approaches.
- Traditional Iterator Object
class NumberIterator implements Iterator { private $current = 1; private $limit; public function __construct($limit) { $this->limit = $limit; } public function current() { return $this->current; } public function key() { return $this->current; } public function next() { $this->current++; } public function rewind() { $this->current = 1; } public function valid() { return $this->current <= $this->limit; } } foreach (new NumberIterator(5) as $number) { echo $number; }
That's a lot of boilerplate code for a simple sequence of numbers!
- Generator Equivalent
function numbersUpTo($limit) { for ($i = 1; $i <= $limit; $i++) { yield $i; } } foreach (numbersUpTo(5) as $number) { echo $number; }
Much cleaner, right? With generators, PHP handles the iterator mechanics behind the scenes.
Real-World Example: Reading a Large File
Using arrays:
$lines = file('largefile.txt'); // Loads entire file into memory
Using generators:
function readFileLines($file) { $handle = fopen($file, 'r'); while (($line = fgets($handle)) !== false) { yield $line; } fclose($handle); } foreach (readFileLines('largefile.txt') as $line) { echo $line; }
With generators, you can process gigabyte-sized files without running out of memory.
Source: Orkhan Alishov's notes