Programster's Blog

Tutorials focusing on Linux, programming, and open-source

PHP - Generators

PHP

Generators have been available since PHP 5.5 and the currently minimum supported (security) version of PHP is 5.6 so you should definitely have access to them.

Generators are a simple way to implement iterators which can be used in foreach statements. You may be familiar with the range statement which can be used to generate a list of numbers, such as the code below that will generate an array of numbers as shown below:

print_r( range(0,10) ); // array of numbers 0 -> 10
print_r( range(0,10, 1) ); // array of numbers 0 -> 10
print_r( range(0,10, 3) ); // array of numbers 0, 3, 6, 9 

You could use this instead of a for loop like so:

// print numbers 0 to 100 each on their own line.
foreach (range(0,10) as $number)
{
    print $number . PHP_EOL;
}

// same as above
for ($i=0; $i < 11; $i++)
{
    print $i . PHP_EOL;
}

It is only one step further with a generator. Here is a generator that does the same thing:

function myGenerator($start, $stop) 
{
    for ($i=$start; $i <= $stop; $i++)
    {
        yield $i;
    }
}

foreach (myGenerator(0,10) as $number)
{
    print "myGenerator: " . $number . PHP_EOL;
}

That doesn't look pretty or more concise than a for loop but something like this may be useful in certain situations:

// This may feel more "natural"
$myGenerator = function(){
    for ($i=0; $i <= 10; $i++)
    {
        yield $i;
    }
};


foreach ($myGenerator() as $number)
{
    print '$myGenerator: ' . $number . PHP_EOL;
}

Not Just Numbers!

$fruitGenerator = function(){
    yield "apple";
    yield "pear";
    yeild "pineapple";
};


foreach ($fruitGenerator() as $fruit)
{
    print $fruit . PHP_EOL;
}

Nested Generators

Generators can call other generators like so which will output the times tables. e.g. 0->10, then 0,2,4->20, then 0,3,6->30, etc.

function timesTableGenerator($start, $stop)
{
    for ($i=$start; $i <= $stop; $i++)
    {
        foreach(multiplier(0, 10, $i) as $num)
        {
            yield $num;
        }
    }
}

function multiplier($start, $stop, $factor)
{
    for ($i=$start; $i <= $stop; $i++)
    {
        yield $i * $factor;
    }
}

foreach (timesTableGenerator(1,10) as $number)
{
    print $number . PHP_EOL;
}

The code above is more confusing than if one had just used a nested for loop, but perhaps your codebase wants to use the multiplier generator on its own as well.

Not an Iterator or Traversable Object.

Unfortunately, $myGenerator in the examples above is a closure that does not implement the Iterator or Traversable interfaces and won't work as you might expect without the () as shown below:

$myGenerator = function() {
    for ($i=0; $i <= 5; $i++)
    {
        yield $i;
    }
};

// This won't print anything due to no "()"
foreach ($myGenerator as $number)
{
    print 'number is: ' . $number . PHP_EOL;
}

// This also won't work
class MyClass
{

    public function __construct(Traversable $generator)
    {
        foreach ($generator as $number)
        {
             print $number . PHP_EOL;
        }
    }
}

$obj = new MyClass($myGenerator);

Also, generators can't run "business logic" as well, and are only for yielding a result. For example, the following won't work.

$myGenerator = function(){
    for ($i=0; $i <= 10; $i++)
    {
        print "yielding $i" . PHP_EOL;  // this breaks it
        yield $i;
    }
};


foreach ($myGenerator() as $number)
{
    print '$myGenerator: ' . $number . PHP_EOL;
}

What's The Benefit Of Generators?

So what's the point of generators then? Why not just use arrays and the the range function? In a word: memory. Sometimes you may need to loop over a massive set of numbers, in which case storing them in an array in memory may not be practical. You could just skip the pre-population step and go straight to the iterating.

Last updated: 16th September 2021
First published: 16th August 2018

This blog is created by Stuart Page

I'm a freelance web developer and technology consultant based in Surrey, UK, with over 10 years experience in web development, DevOps, Linux Administration, and IT solutions.

Need support with your infrastructure or web services?

Get in touch