PHP has a great mechanism to create iterators in a compact fashion. This type of iterator comes with some severe limitations; they are forward only and cannot be rewound. Indeed, even to simply start an iterator from the start, you must rebuild the generator. In essence, this is a forward-only iterator.
A function that uses the yield
keyword instead of the return
keyword. This will act in the same way as a return
statement, but it will not stop the execution of that function. A generator function can yield
data as many times as you please.
When you populate an array with values, those values must be stored in memory which can cause you to exceed your PHP memory limit or require a significant amount of processing time for the generator. When you put the logic in a generator function, that overhead does not exist. The generator function may merely yield as many results as it needs; there's no need to prepopulate an array first.
Here is a simple generator that will var_dump
a string stating, the generator has started. The function will then generate the first five square numbers while also outputting their place in the series with var_dump
. It will then finally indicate the generator has ended:
<?php function squaredNumbers() { var_dump("Generator starts."); for ($i = 0; $i < 5; ++$i) { var_dump($i . " in series."); yield pow($i, 2); } var_dump("Generator ends."); } foreach (squaredNumbers() as $number) { var_dump($number); }
The second part of this script loops through this function and runs a var_dump
string on each number. The output of this is as follows:
Let's amend this function slightly.
It is very important to note that if you add a return type to the variable, you can only declare a return type of Generator
, Iterator
or Traversable
, integer
.
Here is the code:
<?php function squaredNumbers(int $start, int $end): Generator { for ($i = $start; $i <= $end; ++$i) { yield pow($i, 2); } } foreach (squaredNumbers(1, 5) as $number) { var_dump($number); }
The result of this is as follows:
What if we want to yield a key as well as a value? Well, this is fairly easy.
There's something else to mention about generators to those who used them in PHP 5: in PHP 5, when you want to simultaneously yield a variable while setting it to a variable, you must wrap the yield statement in brackets. This restriction does not exist in PHP 7.
This works in PHP 5 and 7:
$data = (yield $value);
This only works in PHP 7:
$data = yield $value;
Let's suppose we want to amend our generator so that it yields a key-value result. Here's what the code looks like:
<?php function squaredNumbers(int $start, int $end): Generator { for ($i = $start; $i <= $end; ++$i) { yield $i => pow($i, 2); } } foreach (squaredNumbers(1, 5) as $key => $number) { var_dump([$key, $number]); }
When we test this, we will var_dump
a two-dimensional array containing a key-value store of whatever the generator has yielded in a given iteration.
Here is the output:
Just a few other tips, a yield statement with no variable (like the one shown in the succeding command) will simply yield null
:
yield;
You may also use yield from
which will yield the inner values of any given generator.
Let's suppose we have an array of two values:
[1, 2]
When we use yield from
to yield an array of two values we get the inner values of the array. Let me demonstrate this:
<?php function innerGenerator() { yield from [1, 2]; } foreach (innerGenerator() as $number) { var_dump($number); }
This will display the following output:
However, now let's alter this script so that it uses yield
instead of yield from
:
<?php function innerGenerator() { yield [1, 2]; } foreach (innerGenerator() as $number) { var_dump($number); }
We will now see that instead of merely just the inner values of the array, we get the outer container too:
3.128.200.71