Generators

PHP 5.5 was the first version of PHP with support for generator functions, or generators for short. The xrange() function shown in the example below is the “Hello world” example for generators:

function xrange(int $start, int $end, int $step = 1) {
    for ($i = $start; $i <= $end; $i += $step) {
        yield $i;
    }
}

foreach (xrange(1, 1000000) as $number) {
    print $number . \PHP_EOL;
}

Unlike the range() function that is built into PHP, the xrange() generator function shown above does not compute an array with all numbers at once. Instead, it returns an iterator that will calculate and then emit one number per iteration step. This approach is more memory-efficient than an iterator, which needs to keep all values in memory the whole time.

A generator is an interruptible function that returns a sequence of values (using the yield keyword) instead of a single value (using the return keyword). Two things happen when the yield statement of a generator function is executed: the argument of the yield statement is yielded, and the execution of the generator function is suspended. The execution of the generator function is resumed when the next value is requested.

In PHP, invoking a generator function creates an object (of the built-in Generator class) that implements the Iterator interface and can, therefore, be used with foreach. Generators can be thought of as a convenient way to implement iterators. Using them does not require the manual implementation of the five methods of the built-in Iterator interface (current(), next(), key(), valid(), rewind()).

Generator Return Expressions

Since their execution can be suspended and then later resumed, generator functions can be used to implement cooperative multitasking in the form of so-called coroutines. Before PHP 7, however, such a coroutine was able to process concurrent tasks but had no standard way to access the results of those computations because a generator function could only yield a sequence of values but not return a result.

PHP 7 allows the usage of return statements in generator functions:

function g()
{
    yield 1;
    yield 2;

    return true;
}

$g = g();

foreach ($g as $element) {
    // ...
}

var_dump($g->getReturn());

Executing the code shown above will print the output shown below:

bool(true)

In the example shown above we use the getReturn() method on the generator object to retrieve the return value of a generator.

Generator Delegation

PHP 7 introduces the yield from <expression> syntax that allows the implementation of generators that delegate operations to Traversable objects and arrays. It is now possible to compose yield statements from smaller conceptual units.

function delegating_generator()
{
    yield from subgenerator();
}

function subgenerator()
{
    yield 'value';
}

foreach (delegating_generator() as $value) {
    print $value . "\n";
}

Executing the code shown above will print the following:

value

A delegating generator is a generator that uses the new yield from <expression> syntax. A subgenerator is a generator that is used in the <expression> part of a delegating generator’s yield from <expression> statement.

yield in Expression Context

We already discussed the concept of operator precedence in the “Uniform Variable Syntax” chapter. This comes into play once again, as the yield language construct is now a right-associative operator with precedence between the print and => operators:

Expression PHP 5 interpretation PHP 7 interpretation
yield -1 (yield) - 1 yield (-1)
yield $foo or die yield ($foo or die) (yield $foo) or die

This change was implemented to eliminate the need to use parentheses when using yield in an expression context.