PhpRiot
News Archive
PhpRiot Newsletter
Your Email Address:

More information

Cooperative multitasking using coroutines (in PHP!)

Note: This article was originally published at Planet PHP on 22 December 2012.
Planet PHP

One of the large new features in PHP 5.5 will be support for generators and coroutines. Generators are already sufficiently covered by the documentation and various other blog posts (like this one or this one. Coroutines on the other hand have received relatively little attention. The reason is that coroutines are both a lot more powerful and a lot harder to understand and explain.

In this article I'd like to guide you through an implementation of a task scheduler using coroutines, so you can get a feeling for the stuff that they allow you to do. I'll start off with a few introductory sections. If you feel like you already got a good grasp of the basics behind generators and coroutines, then you can jump straight to the aoCooperative multitaskinga section.

Generators

The basic idea behind generators is that a function doesn't return a single value, but returns a sequence of values instead, where every value is emitted one by one. Or in other words, generators allow you to implement iterators more easily. A very simple example of this concept is the xrange() function:

function xrange($start, $end, $step = 1) { for ($i = $start; $i $end; $i += $step) { yield $i; } } foreach (xrange(1, 1000000) as $num) { echo $num, "\n"; }

The xrange() function shown above provides the same functionality as the built-in range() function. The only difference is that range() will return an array with one million numbers in the above case, whereas xrange() returns an iterator that will emit these numbers, but never actually compute an array with all of them.

The advantages of this approach should be evident. It allows you to work with large datasets without loading them into memory all at once. You can even work with infinite data-streams.

All this can also be done without generators, by manually implementing the Iterator interface. Generators only make it (a lot) more convenient, because you no longer have to implement five different methods for every iterator.

Generators as interruptible functions

To go from generators to coroutines it's important to understand how they work internally: Generators are interruptible functions, where the yield statements constitute the interruption points.

Sticking to the above example, if you call xrange(1, 1000000) no code in the xrange() function is actually run. Instead PHP just returns an instance of the Generator class which implements the Iterator interface:

$range = xrange(1, 1000000); var_dump($range); // object(Generator)#1 var_dump($range instanceof Iterator);

Truncated by Planet PHP, read more at the original (another 89312 bytes)