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);
}
}
Links to use cases
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:
- co-routines as an alternative to state machines
- Cooperative multitasking using co-routines in PHP
- Python generators and co-routines
- What generators can do for you
- Coroutines in C
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.