在PHP中 yield意味着什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (91)

我最近被这个代码难住了:

function xrange($min, $max) 
{
    for ($i = $min; $i <= $max; $i++) {
        yield $i;
    }
}

我从没见过这个yield关键

那这是什么yield关键词?它是否有效的PHP?如果是的话,我该怎么用呢?

提问于
用户回答回答于

1 生成器 yield关键字

yield的中文文档在这里:http://php.net/manual/zh/language.generators.overview.php

查看文档,能知道yield的一个功能就是能有效的降低迭代的内存开销。比如官网的这个xrange例子:

<?php
function xrange($start, $limit, $step = 1) {
    for ($i = $start; $i <= $limit; $i += $step) {
        yield $i;
    }
}
 
echo 'Single digit odd numbers: ';
 
/*
 * Note that an array is never created or returned,
 * which saves memory.
 */
foreach (xrange(1, 9, 2) as $number) {
    echo "$number ";
}
 
echo "\n";
?>

这里的xrange是一个迭代,功能和range是一样的,如果使用range函数的话,那么函数内部实现会储存每个迭代的中间过程,即每个中间变量都有个内存空间,那么首先程序使用的内存空间就大了,而且分配内存,回收内存都会导致程序的运行时间加长。但是如果使用上yield实现的xrange函数的话,里面所有的中间变量都只使用一个内存$i,这样节省的时间和空间都会变小。

那么为什么yield会有这样的效果呢?联想到lua中的yield,这里就算是协程的概念了。在lua语言中,当程序运行到yield的时候,使用协程将上下文环境记录住,然后将程序操作权归还到主函数,当主函数调用resume的时候,会重新唤起协程,读取yield记录的上下文。这样形成了程序语言级别的多协程操作。php 5.5这里的yield也是同样的道理,当程序运行到yield的时候,当前程序就唤起协程记录上下文,然后主函数继续操作,只是php中没有使用如resume一样的关键字,而是“在使用的时候唤起”协程。比如上例中的foreach迭代器就能唤起yield。所以上面的这个例子就能理解了。

其实照着引用yield来说,好多内部函数,特别是迭代有关的函数应该都有可能进行优化。或许后续会有yield版本和非yield版本的实现同一功能的函数把。

2 finally关键字

这个和java中的finally一样,经典的try ... catch ... finally 三段式异常处理。

3 foreach 支持list()

对于“数组的数组”进行迭代,之前需要使用两个foreach,现在只需要使用foreach + list了,但是这个数组的数组中的每个数组的个数需要一样。看文档的例子一看就明白了。

<?php
$array = [
    [1, 2],
    [3, 4],
];
 
foreach ($array as list($a, $b)) {
    echo "A: $a; B: $b\n";
}
?>

4 empty() 支持自定义函数了

之前empty()中的参数是不能为函数的。现在可以了

<?php
function foo(){
    return false;
}
 
if(empty(foo())){
    echo 11;
} else {
    echo 12;
}

5 非变量array和string也能支持下标获取了

<?php
  
echo array(1, 2, 3)[0];
echo [1, 2, 3][0];
  
echo "foobar"[2];
  
?>

6 类名通过::class可以获取

<?php
namespace Name\Space;
class ClassName {}
 
echo ClassName::class;
 
echo "\n";
?>

7 增加了opcache扩展

使用opcache会提高php的性能,你可以和其他扩展一样静态编译(--enable-opcache)或者动态扩展(zend_extension)加入这个优化项。

用户回答回答于

这是php实现协程的方式。

要理解协程,首先要理解:代码是代码,函数是函数。函数包裹的代码赋予了这段代码附加的意义:有参数,有返回值,当函数调用另个函数的时候,必须等这个函数返回,当前函数才能返回,这就构成了后进先出,也就是stack。

而协程包裹的代码,不是函数,不完全遵守函数的这些附加的意义,协程执行到某个点,他yield,而不是return,再次调用协程的时候,会在上次yeild的点继续执行。

所以携程违背了通常操作系统和x86的cpu认定的代码执行方式,也就是stack的这种执行方式,需要运行环境(比如php,python的yield和golang的goroutine)自己调度,来实现你所要求的这种代码执行的语义。

具体来说,一个包含yeild的php函数,就是协程,他有阶段性的结算值 yield $var, 但是代码并不返回,php的调度者接到这个值后,喂给一个generator,generator是个实现了iterator接口的+和协程通讯接口(比如send方法)的实例,所以可以用在for循环里(另个接口负责和协程通讯)。那么gnenerator收到了这个协程的阶段性的值后,他喂给for循环,等for循环下一次循环的时候,他又启动这个协程,协程从上次中断的点继续执行,继续计算,继续yeild值给generator,generator喂for循环,继续循环,直到协程执行完毕。

扫码关注云+社区

领取腾讯云代金券