PHP Generators

Just like PHP traits that I wrote about a couple of weeks ago, generators have been in PHP for a while (since 5.5 in fact). But, also like traits, I never got around to trying them out. So, decided to look into them for this week's blog post.

What are Generators?

According the PHP manual, generators "provide an easy way to implement simple iterators without the overhead or complexity of implementing a class that implements the Iterator interface".

What is an iterator?

An iterator is a special type of object that has defined a way that it can be used in place of a list or an array, for example in a foreach statement.

The problem with this method is that it is a little overkill for simple situations. It causes you to have to create an entire class for the sake of a list that is slightly more complex than a list.

How do I use it?

A generator is written exactly the same as a function but uses the yield keyword. Here is an example:

function multipleRange($start, $end, $multiple = 1) {
    if ($limit < $start) throw new \LogicException('End must be greated than start.');
    for ($i = $start; $i <= $end; $i++) {
        if ($i % $multiple == 0) {
            yield $i;
        }
    }     
}

So I have this function called multipleRange. It returns a list of numbers that are multiples of a given number. It takes 3 parameters, a minimum number, a maximum number and a number to be a multiple of.

It simply goes through each number in the range checks if it is a multiple and yields it if it is.

I could then use the function in a foreach loop like so:

foreach (multipleRange(0, 30, 3) as $number) {
    print $number." ";
}

Which outputs:

0 3 6 9 12 15 18 21 24 27 30 

Easy!

Now a question that might come to mind is, "Why can't the function just generate an array and return it?"

In this very simple example, it could be done like that without much of a problem. But the benefit of using the yield keyword is that it isn't holding up the memory that you would usually use for an array.

The function is only getting the next item when it is called upon. It isn't storing previous ones anywhere or the ones to come. So if you had to iterate over giant objects, you can see that this would clearly use less memory which is a good thing.

Summary

It is quite a simple concept, but I think use cases can get complex very quickly. I have always had an issue with wrapping my head around iterators. But I hope this gives a good starting point for investigating how you can use generators in the future.


© 2012-2017