Skip to content

PHP Tutorial Modern PHP Features

Generators

Lesson 233 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

A normal function builds a whole result and returns it. A generator produces values one at a time, pausing between them. It uses the keyword yield.

index.php
PHP
<?php
function countTo(int $max): Generator
{
    for ($i = 1; $i <= $max; $i++) {
        yield $i;
    }
}

foreach (countTo(3) as $number) {
    echo $number . " ";
}
Output
OUTPUT
1 2 3

When the loop asks for the next value, PHP runs the function until the next yield, hands over the value and pauses. The state (the variable $i) is remembered until the next request.

Why generators matter: memory

Imagine reading a file with a million lines. Loading it into an array needs memory for every line. A generator gives one line at a time, so memory stays small.

index.php
PHP
<?php
function readLines(string $file): Generator
{
    $handle = fopen($file, "r");

    try {
        while (($line = fgets($handle)) !== false) {
            yield rtrim($line, "\r\n");
        }
    } finally {
        fclose($handle);
    }
}

file_put_contents("big.txt", "alpha\nbeta\ngamma\n");

foreach (readLines("big.txt") as $number => $line) {
    echo ($number + 1) . ": $line\n";
}
Output
OUTPUT
1: alpha
2: beta
3: gamma

The finally makes sure the file is closed even if the loop stops early.

Compare the memory

index.php
PHP
<?php
function rangeArray(int $n): array
{
    return range(1, $n);
}

function rangeGenerator(int $n): Generator
{
    for ($i = 1; $i <= $n; $i++) {
        yield $i;
    }
}

$before = memory_get_usage();
$sum = 0;
foreach (rangeGenerator(100000) as $i) {
    $sum += $i;
}
$used = memory_get_usage() - $before;

echo "Sum: $sum\n";
echo $used < 100000 ? "The generator used very little memory" : "More memory than expected";
Output
OUTPUT
Sum: 5000050000
The generator used very little memory

Keys

index.php
PHP
<?php
function pairs(): Generator
{
    yield "a" => 1;
    yield "b" => 2;
}

foreach (pairs() as $key => $value) {
    echo "$key=$value ";
}
Output
OUTPUT
a=1 b=2

yield from: delegate to another generator

index.php
PHP
<?php
function inner(): Generator
{
    yield 1;
    yield 2;
}

function outer(): Generator
{
    yield 0;
    yield from inner();
    yield 3;
}

echo implode(", ", iterator_to_array(outer(), false));
Output
OUTPUT
0, 1, 2, 3

The second argument false tells iterator_to_array() to ignore keys, which would otherwise repeat.

Pipelines

Generators can be chained, and each step handles one item at a time.

index.php
PHP
<?php
function numbers(): Generator
{
    foreach (range(1, 10) as $n) {
        yield $n;
    }
}

function onlyEven(iterable $source): Generator
{
    foreach ($source as $n) {
        if ($n % 2 === 0) {
            yield $n;
        }
    }
}

function squared(iterable $source): Generator
{
    foreach ($source as $n) {
        yield $n * $n;
    }
}

echo implode(", ", iterator_to_array(squared(onlyEven(numbers())), false));
Output
OUTPUT
4, 16, 36, 64, 100

Sending values in

A generator can also receive values with ->send(). It is used less often, in coroutines.

Limits

  • A generator can be looped over once. To loop again, call the function again.
  • You cannot count() a generator or jump to an item. It only moves forward.
  • You cannot rewind() after it has started.

When to use a generator

  • Large files or large database results.
  • Endless or very long sequences.
  • Building a lazy pipeline of steps.

For small data, a plain array is simpler and just as fast.

lightbulb
Return type Generator
Declare : Generator (or : iterable) on functions that use yield. A function with yield always returns a generator object, even if the yield is never reached.