What does yield mean in PHP?
What is yield
?
The yield
keyword returns data from a generator function:
The heart of a generator function is the yield keyword. In its simplest form, a yield statement looks much like a return statement, except that instead of stopping execution of the function and returning, yield instead provides a value to the code looping over the generator and pauses execution of the generator function.
What is a generator function?
A generator function is effectively a more compact and efficient way to write an Iterator. It allows you to define a function (your xrange
) that will calculate and return values while you are looping over it:
function xrange($min, $max) {
for ($i = $min; $i <= $max; $i++) {
yield $i;
}
}
[…]
foreach (xrange(1, 10) as $key => $value) {
echo "$key => $value", PHP_EOL;
}
This would create the following output:
0 => 1
1 => 2
…
9 => 10
You can also control the $key
in the foreach
by using
yield $someKey => $someValue;
In the generator function, $someKey
is whatever you want appear for $key
and $someValue
being the value in $val
. In the question's example that's $i
.
What's the difference to normal functions?
Now you might wonder why we are not simply using PHP's native range
function to achieve that output. And right you are. The output would be the same. The difference is how we got there.
When we use range
PHP, will execute it, create the entire array of numbers in memory and return
that entire array to the foreach
loop which will then go over it and output the values. In other words, the foreach
will operate on the array itself. The range
function and the foreach
only "talk" once. Think of it like getting a package in the mail. The delivery guy will hand you the package and leave. And then you unwrap the entire package, taking out whatever is in there.
When we use the generator function, PHP will step into the function and execute it until it either meets the end or a yield
keyword. When it meets a yield
, it will then return whatever is the value at that time to the outer loop. Then it goes back into the generator function and continues from where it yielded. Since your xrange
holds a for
loop, it will execute and yield until $max
was reached. Think of it like the foreach
and the generator playing ping pong.
Why do I need that?
Obviously, generators can be used to work around memory limits. Depending on your environment, doing a range(1, 1000000)
will fatal your script whereas the same with a generator will just work fine. Or as Wikipedia puts it:
Because generators compute their yielded values only on demand, they are useful for representing sequences that would be expensive or impossible to compute at once. These include e.g. infinite sequences and live data streams.
Generators are also supposed to be pretty fast. But keep in mind that when we are talking about fast, we are usually talking in very small numbers. So before you now run off and change all your code to use generators, do a benchmark to see where it makes sense.
Another Use Case for Generators is asynchronous coroutines. The yield
keyword does not only return values but it also accepts them. For details on this, see the two excellent blog posts linked below.
Since when can I use yield
?
Generators have been introduced in PHP 5.5. Trying to use yield
before that version will result in various parse errors, depending on the code that follows the keyword. So if you get a parse error from that code, update your PHP.
Sources and further reading:
- Official docs
- The original RFC
- kelunik's blog: An introduction to generators
- ircmaxell's blog: What generators can do for you
- NikiC's blog: Cooperative multitasking using coroutines in PHP
- Co-operative PHP Multitasking
- What is the difference between a generator and an array?
- Wikipedia on Generators in general
PHP yield without for?
It doesn't require a loop. This works:
function foo() {
yield 1;
yield 2;
yield 3;
}
One reason to use this is to use it as a similar feature as 'await' in Javascript.
It's possible for example to build an async framework that uses yield for this purpose:
function foo() {
$response = yield asyncHttpRequest('GET', 'http://blabla');
}
This specific example is fictional, but it's possible (and has been done).
I made such an async library. Docs here: https://sabre.io/event/coroutines/
There's better maintained/more popular libraries out there though.
Why does php return an empty Generator if function contains both return and yield?
The manual about the generator syntax says the following:
Any function containing
yield
is a generator function.
So by having the yield
keyword in there results in having a generator function. With the return
keyword you are quitting the function, which is used to build a generator function. In this case it's just empty because before the return
statement are no yield
lines. When you use the following source code:
function returnAndYield(): Generator
{
yield 1;
yield 2;
return;
yield 3;
yield 4;
}
$result = returnAndYield();
echo implode(',', iterator_to_array($result));
you will get the following output:
1,2
The values 3
and 4
are not in the generator because the function exits before these yield
statements.
PHP generator yield the first value, then iterate over the rest
The problem is that the foreach
try to reset (rewind) the Generator. But rewind()
throws an exception if the generator is currently after the first yield.
So you should avoid the foreach
and use a while
instead
$gen = generator();
$first = $gen->current();
echo $first . '<br/>';
$gen->next();
while ($gen->valid()) {
echo $gen->current() . '<br/>';
$gen->next();
}
Related Topics
MySQL Code Causes PHP Script to Crash at Popen/Exec
JavaScript Equivalent of PHP's List()
How to Implement Pagination in PHP
Include Constant in String Without Concatenating
Remove Duplicate from String in PHP
How to Convert JSON to Xml in PHP
PHP Script Not Working in HTML File
Allow Re-Sending New Order Notification in Woocommerce 5+
Best Way to Get Files from a Dir Filtered by Certain Extension in PHP
Cloudflare and Logging Visitor Ip Addresses via in PHP
Laravel Model with Two Primary Keys Update
Sum Values in Foreach Loop PHP