Generators in PHP
Over times we're face with issues where PHP apps are found to consume quite a lot of memory and not memory efficient because of the way we write our apps or when we don't take advantage of some features provided PHP itself which helps memory management. An example of a feature that can be used as an advantage in reducing memory use is Generators. You say what? Yes - Generators! So, let's dive in...
What are Generators?
Generators are basically functions that returns many values that can be iterated over. Unlike iterating over an array, you have control over the process of iterating over the values returned by a generator. In other words we can control the iteration of the values and do things like returning values when we want or as much as we want or pause iteration or even rewind iteration and taking it to another level, we can send back values through generators.
Unlike normal function that directly returns
something a generator yields
something, even though PHP 7 now supports functions yielding values as well as returning values but such a function is still regarded as a generator. Once a yield
keyword appears in the function body, PHP will recognize such a function as a Generator.
Putting the explanation in code
We have our normal function with return
like this:
<?php
function foo($a, $b) { return $a + $b; }
But such a function like the one below is not regarded as function, but rather a Generator:
<?php
function bar($a, $b) { yield $a + $b; }
To proof this, I'll use this code:
<?php
function foo($a, $b) {
return $a + $b;
}
function bar($a, $b) {
yield $a + $b;
}
var_dump(foo(2, 3));
echo PHP_EOL;
var_dump(bar(2, 3));
When I run this, I got a result like this:
int(5) class Generator#1 (0) {
}
The function call foo(2, 3)
returns int(5)
as expected but the function call bar(2, 3)
returns an instance of theGenerator
class. It can be seen clearly that the only difference in the function body is the yield
keyword used in place of the return
keyword. So, to summarize this, anytime you use the yield
key word in a function, PHP automatically takes it as a Generator
and it returns a Generator object.
But how do we access the generator value?
The Generator class implements the IteratorInterface
which provides a handful of methods that can be used to access the values in the Generator. You can read about the IteratorInterface
here.
To proof this, we'll add this to our initial code:
<?php
function foo($a, $b) {
return $a + $b;
}
function bar($a, $b) {
yield $a + $b;
}
var_dump(foo(2, 3));
echo PHP_EOL;
var_dump(bar(2, 3));
echo PHP_EOL;
var_dump(bar(2, 3) instanceof Iterator);
And we get the result below:
int(5) class Generator#1 (0) {
}
bool(true)
We get a true
meaning that the Generator class implements the Iterator interface.
Getting result
Now if we call the ->current()
method on the bar(2, 3)
function call like this:
<?php
// ...
echo PHP_EOL;
var_dump(bar(2, 3)->current());
We'll get the expected int(5)
as result. We were able to use the ->current()
method because the Generator class implemented the Iterator
interface.
As seen from the Iterator documentation here, other methods that can be accessed are next()
, previous()
, key()
and valid()
, the documentation explains the workings of these methods as well as their return values. I recommend you read more on it.
Another example
In the initial example, we yielded
just a single result, but a a generator actually allows many results to be yielded, let's see an example:
<?php
function compute($x, $y) {
yield $x + $y;
yield $x * $y;
yield $x / $y;
yield $x - $y;
}
The function above computes the addition, subtraction, multiplication and division of two numbers. Now let's access the result by adding this to our code:
<?php
// ...
$computation = compute(2, 3);
$addition = $computation->current();
echo "Addition: $addition" . PHP_EOL;
$computation->next();
$multiplication = $computation->current();
echo "Multiplication: $multiplication" . PHP_EOL;
$computation->next();
$division = $computation->current();
echo "Division: $division" . PHP_EOL;
$computation->next();
$subtraction = $computation->current();
echo "Subtraction: $subtraction" . PHP_EOL;
// $computation->next(); - No need for this since we are already reached the end
This infrastructure is what provides Generators flexibility and power to be used in many cases that helps the program we write in many ways such as Speed, Memory management, streaming etc.
In this article, we'll only examine what generators are and how we can use them, in further articles, I'll show how Generators can help in saving Memory and in later articles, I'll be explaining how they can be used to improve speed as well as other applications.
You can read more about the generator syntax here.