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:

  • Large files or datasets;
  • Streams of data (APIs, logs, etc.);
  • Infinite sequences;

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