Loïc Faugeron Technical Blog

PHP generators and coroutines 30/04/2014

TL;DR: jump to the conclusion.

In PHP ~5.5, a function containing yield becomes an instance of the Generator class, which acts like an iterator.

Each yield occurence marks a position. Calling next will move from the current yield to the next one.

Thanks to the send method, the Generator class acts also as a corroutine: you can send the returned value of the yield occurence.

A Generator function cannot return a value.

Generator API

class Generator
{
    public function current(); // The yielded value.
    public function key();

    public function valid(); // Has the iterator been closed?

    public function next(); // Resumes execution.
    public function rewind();

    public function send($value); // Sends the value to be returned by the current yield, and resumes execution.
    public function throw(Exception $e); // Replaces the current yield with `throw $e;`
}

Examples

Simple example

function direct_generator()
{
    yield 1;
    yield 'index' => 2;
}

$generator = direct_generator();
echo $generator->current(); // outputs 1
$generator->next();
echo $generator->current(); // outputs 2
echo $generator->key(); // outputs index

Range example

The foreach loop internally calls current, key, next and valid methods:

function xrange($start, $end)
{
    $value = $start;
    while ($value <= $end) {
        yield $value;
        $value++;
    }
}

foreach(xrange(1, 2) as $value) {
    echo $value; // outputs 1, then 2.
}

Co-routine example

Calling the send method is like replacing the current yield with the given value.

function holy_grail_countdown($number)
{
    while (0 < $number) {
        $reset = (yield $number);
        $number = $reset ?: $number - 1;
    }
}

$c = holy_grail_countdown(5);
foreach ($c as $number) {
    echo "$number\n"; // outputs 5, then 2 and finally 1.
    if (5 === $number) {
        $c->send(3);
    }
}

I've first encountered generators and couritines in Python a long time ago, but never really grasped its utility.

It's only recently that I've found some articles describing some usefull use cases, so I'm just going to share them with you:

Another helpful resource would be the PHP's RFC, which I find more explicative than the actual documentation.

Conclusion

Generators allow you to pause the execution of a function and resume back to where it stopped. Coroutines allow you to send a value while resuming back to it!

If you have any comments or questions, feel free to notify me on Twitter.