Generator::send
(PHP 5 >= 5.5.0, PHP 7, PHP 8)
Generator::send — 向生成器中传入一个值
说明
   向生成器中传入一个值,并且当做 yield 表达式的结果,然后继续执行生成器。
  
   如果当这个方法被调用时,生成器不在 yield 表达式,那么在传入值之前,它会先运行到第一个 yield 表达式。
   因此没有必要调用 Generator::next() 让 PHP 生成器 “准备”(就像是 Python 那样做)。
  
参数
value- 
     
传入生成器的值。这个值将会被作为生成器当前所在的
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!
  +添加备注
  
用户贡献的备注 5 notes
  
  
  sfroelich01 at sp dot gm dot ail dot am dot com ¶
  
 
  11 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.
<?php
function 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";
}
//Output
0
1
2
3
?>  
  
  php at didatus dot de ¶
  
 
  3 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:
<?php
class 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 result
foreach ($generator as $elem) {
    echo 'value from generator: ' . $elem . PHP_EOL;
$generator->send($elem . '+');
}
// example iteration two with the expected result
while ($generator->valid()) {
$elem = $generator->current();
    echo 'value from generator: ' . $elem . PHP_EOL;
$generator->send($elem . '+');
}
?>
The result of example iteration one:
yielding $elem
value from generator: a
yield return: a+
yielding $elem
yield return:
yielding $elem
value from generator: c
yield return: c+
yielding $elem
yield return:
yielding $elem
value from generator: e
yield 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 $elem
value from generator: a
yield return: a+
yielding $elem
value from generator: b
yield return: b+
yielding $elem
value from generator: c
yield return: c+
yielding $elem
value from generator: d
yield return: d+
yielding $elem
value from generator: e
yield return: e+  
  
  anonymous at example dot com ¶
  
 
  5 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".
<?php
class 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 => 1
getIterator(): 1 => 2
getIterator(): 2 => 3
getIterator(): 3 => 4
getIterator(): 4 => 5
getGenerator(): yielding: 0 => 1
getGenerator(): sending:  0 => 1
getGenerator(): received: 0 => 1
getGenerator(): yielding: 1 => 2
getGenerator(): sending:  1 => 2
getGenerator(): received: 1 => 2
getGenerator(): yielding: 2 => 3
getGenerator(): sending:  2 => 3
getGenerator(): received: 2 => 3
getGenerator(): yielding: 3 => 4
getGenerator(): sending:  3 => 4
getGenerator(): received: 3 => 4
getGenerator(): yielding: 4 => 5
getGenerator(): sending:  4 => 5
getGenerator(): received: 4 => 5  
  
  sergei dot solomonov at gmail dot com ¶
  
 
  11 years ago
  <?php
function 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!  
  
  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 = [];
    }
  }
}