Generator::send

(PHP 5 >= 5.5.0, PHP 7, PHP 8)

Generator::send値をジェネレータに送る

説明

public Generator::send(mixed $value): mixed

指定した値を yield の結果としてジェネレータに送り、ジェネレータを続行します。

ジェネレータが yield 式を指していないときにこのメソッドが呼ばれると、 まずは最初の yield 式まで進めてから値を送信します。そのため、 最初のジェネレータを指すためにわざわざ Generator::next() を呼ぶ必要はありません (Python と同じです)。

パラメータ

value

ジェネレータに送り込む値。この値が、ジェネレータが現在指している yield 式の戻り値になります。

戻り値

yield した値を返します。

例1 Generator::send() による値の注入

<?php
function printer() {
echo
"I'm printer!".PHP_EOL;
while (
true) {
$string = yield;
echo
$string.PHP_EOL;
}
}

$printer = printer();
$printer->send('Hello world!');
$printer->send('Bye world!');
?>

上の例の出力は以下となります。

I'm printer!
Hello world!
Bye world!

add a note

User Contributed Notes 6 notes

up
72
sfroelich01 at sp dot gm dot ail dot am dot com
12 years ago
Reading the example, it is a bit difficult to understand what exactly to do with this. The example below is a simple example of what you can do this.<?phpfunction nums() {    for ($i = 0; $i < 5; ++$i) {                //get a value from the caller        $cmd = (yield $i);                if($cmd == 'stop')            return;//exit the function        }     }$gen = nums();foreach($gen as $v){    if($v == 3)//we are satisfied        $gen->send('stop');        echo "{$v}\n";}//Output0123?>
up
10
php at didatus dot de
4 years ago
If you want to use generator::send() within a foreach loop, you will most likely get an unexpected result. The Generator::send() method resumes the generator, which means the pointer within the generator is moved to the next element in the generator list.Here is an example:<?phpclass ApiDummy{    private static $apiDummyData = ['a', 'b', 'c', 'd', 'e'];    public static function getAll(): Generator {        foreach (self::$apiDummyData as $entry) {            echo 'yielding $elem' . PHP_EOL;            $newElem = (yield $entry);            echo 'yield return: ' . $newElem . PHP_EOL;        }    }}$generator = ApiDummy::getAll();// example iteration one with unexpected resultforeach ($generator as $elem) {    echo 'value from generator: ' . $elem . PHP_EOL;    $generator->send($elem . '+');}// example iteration two with the expected resultwhile ($generator->valid()) {    $elem = $generator->current();    echo 'value from generator: ' . $elem . PHP_EOL;    $generator->send($elem . '+');}?>The result of example iteration one:yielding $elemvalue from generator: ayield return: a+yielding $elemyield return:yielding $elemvalue from generator: cyield return: c+yielding $elemyield return:yielding $elemvalue from generator: eyield return: e+As you can see, the values b and d are not printed out and also not extended by the + sign.The foreach loop receives the first yield and the send call causes a second yield within the first loop. Therefor the second loop already receives the third yield and so on.To avoid this, one solution could be to use a while loop and the Generator::send() method to move the generator cursor forward and the Generator::current() method to retrieve the current value. The loop can be controlled with the Generator::valid() method which returns false, if the generator has finished. See example iterator two. The expected result of example iteration two:yielding $elemvalue from generator: ayield return: a+yielding $elemvalue from generator: byield return: b+yielding $elemvalue from generator: cyield return: c+yielding $elemvalue from generator: dyield return: d+yielding $elemvalue from generator: eyield return: e+
up
4
anonymous at example dot com
6 years ago
As of 7.3, the behavior of a generator in a foreach loop depends on whether or not it expects to receive data. Relevant if you are experiencing "skips".<?phpclass X implements IteratorAggregate {    public function getIterator(){        yield from [1,2,3,4,5];    }    public function getGenerator(){        foreach ($this as $j => $each){            echo "getGenerator(): yielding: {$j} => {$each}\n";            $val = (yield $j => $each);            yield; // ignore foreach's next()            echo "getGenerator(): received: {$j} => {$val}\n";        }    }}$x = new X;foreach ($x as $i => $val){    echo "getIterator(): {$i} => {$val}\n";}echo "\n";$gen = $x->getGenerator();foreach ($gen as $j => $val){    echo "getGenerator(): sending:  {$j} => {$val}\n";    $gen->send($val);}?>getIterator(): 0 => 1getIterator(): 1 => 2getIterator(): 2 => 3getIterator(): 3 => 4getIterator(): 4 => 5getGenerator(): yielding: 0 => 1getGenerator(): sending:  0 => 1getGenerator(): received: 0 => 1getGenerator(): yielding: 1 => 2getGenerator(): sending:  1 => 2getGenerator(): received: 1 => 2getGenerator(): yielding: 2 => 3getGenerator(): sending:  2 => 3getGenerator(): received: 2 => 3getGenerator(): yielding: 3 => 4getGenerator(): sending:  3 => 4getGenerator(): received: 3 => 4getGenerator(): yielding: 4 => 5getGenerator(): sending:  4 => 5getGenerator(): received: 4 => 5
up
11
sergei dot solomonov at gmail dot com
11 years ago
<?phpfunction foo() {    $string = yield;    echo $string;    for ($i = 1; $i <= 3; $i++) {        yield $i;    }}$generator = foo();$generator->send('Hello world!');foreach ($generator as $value) echo "$value\n";?>This code falls with the error:PHP Fatal error:  Uncaught exception 'Exception' with message 'Cannot rewind a generator that was already run'.foreach internally calls rewind, you should remember this!
up
1
baohx2000 at gmail dot com
5 years ago
I have found that inverse generators (using $x = yield) is a great way to handle chunked batch processing. As data is being iterated, once a specific count has been fed to the generator, it processes and resets the data. For example, you could do a batch mysql insert every 500 records.Example (note the handling of null, which you would send to the generator to handle stragglers after the previous batch)function importer(){  $max = 500;  $items = [];  while (true) {    $item = yield;    if ($item !== null) {      $items[] = yield;    }    if ($item === null || count($items) >= $max) {       // do batch operations       $items = [];    }  }}
up
0
booleantype1990 at gmail dot com
3 months ago
I will allow myself to edit the code baohx2000 at gmail dot com. I suppose, it should be `$items[] = $item;` instead of `$items[] = yield;`:<?phpfunction importer(){  $max = 500;  $items = [];  while (true) {    $item = yield;    if ($item !== null) {      $items[] = $item;    }    if ($item === null || count($items) >= $max) {       // do batch operations       $items = [];    }  }}?>
To Top