Generators in PHP

Syed Sirajul Islam Anik
4 min readAug 15, 2019

--

Image from: http://bit.ly/2OVz879

This noon, while I was reading, all of a sudden I came to know that PHP has generators too as Python does. I was astonished. What? I didn’t know! 😲

Well, then I started reading about the generators and here is what I came to know about it.

Generators

Generators are like the functions but the key difference between them is function returns value (or may not) but generators yields values. Yes, generators can return value as well. We’ll get into that later on. The function that contains *yield* keyword is a generator. That’s it.

When you intentionally or unintentionally use the keyword yield within a function the function becomes a generator. It’s impossible unintentionally, cause you may not know the word. How will you use?

Let’s have a look into the code…

function g1 () {
yield 1;
yield 2;
yield 3;
}

The above code is a generator. When you iterate over the function, each time you’ll get a value until it reaches the end.

$gen = g1();foreach($gen as $value) {
echo $value . PHP_EOL;
}

While iterating the generator, you’ll also get a key for those values. So the previous iteration can be rewritten like the following.

$gen = g1();foreach($gen as $key => $value) {
echo $key . ' => ' . $value . PHP_EOL;
}

Generators can be used as an associative array as well. You can just do the following to achieve so,

function g2 () {
yield 'a' => 1;
yield 'b' => 2;
yield 'c' => 3;
}
$gen = g2();foreach($gen as $key => $value) {
echo $key . ' => ' . $value . PHP_EOL;
}

If you’re using PHP5 and you want to use the yield as an expression and use that variable afterward, you should wrap the yield statement within a parenthesis. Like $data = (yield 1);. In PHP7+ it’s all good if you don’t use parenthesis.

You can also yield NULL from a generator either by yield null; or yield;

function gnull () {
yield null;
yield;
}
$gen = gnull();foreach($gen as $key => $value) {
echo $key . ' => ' . $value . PHP_EOL;
}

Generator Delegation

You can only delegate generator values only in PHP7+. Which can be delegated from another generator, traversable object or an array. To achieve so, you can do like below

function gDelegate () {
yield from g2();
yield from [3, 4];
yield from new ArrayIterator([5, 6]);
yield 'd' => 4;
yield 'e' => 5;
yield 'f' => 6;
}
$gen = gDelegate();
foreach($gen as $key => $value) {
echo $key . ' => ' . $value . PHP_EOL;
}

Return value

Only in PHP7+, you are allowed to return a value from a generator. Doing so in earlier version would raise an error.

In PHP 5, a generator could not return a value: doing so would result in a compile error. — php.net

To get the return value from a generator we’ll use the Generator::getReturn() method.

function greturn () {
yield 'a' => 1;
yield 'b' => 2;
return 'greturn';
}
$gen = greturn();
foreach($gen as $key => $value) {
echo $key . ' => ' . $value . PHP_EOL;
}
var_dump($gen->getReturn());

The getReturn method on a generator will return the value that’s been returned from the generator.

Convert to an array

Generators are an iterable item. To convert a generator to an array we will use the iterator_to_array method.

var_dump(iterator_to_array(gDelegate()));

But the problem with this is that each generator generates a key for its values. If the key is not specified, it’ll start from 0. Now while converting to an array, if you want to delegate values from other iterable objects, the keys will be overwritten if there is a same key for multiple vales. Check the following code …

function gDelegated () {
yield 'a' => 1;
yield 'b' => 2;
yield 'c' => 3;
}
function gDelegatable () {
yield from gDelegated();
yield 'd' => 4;
yield 'e' => 5;
yield 'f' => 6;
yield 'a' => 7; // this will overwrite the gDelegated::a's value
}
var_dump(iterator_to_array(gDelegatable()));
iterate_to_array output
iterate_to_array output

As you can see above, the value of the key a got overwritten here. If you’re using an int based key and the index doesn’t matter to you then you can pass the second parameter false to the iterator_to_array which will reindex the array and keep all the values. Doing so for Key Value generators will reindex with integer keys.

Cons

  • You cannot rewind the generator once the iteration has started. You’ll always need to call the generator get the value from the start.
  • getReturn will only work if you’ve processed all the values which were yielded. Otherwise, it’ll throw an exception.

Happy coding ❤.

--

--

Syed Sirajul Islam Anik

software engineer with "Senior" tag | procrastinator | programmer | !polyglot | What else 🙄 — Open to Remote