What Does Yield Mean in PHP

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



Leave a reply



Submit