首页 » Web前端 » yield感化php技巧_PHP yield 分析以及协程的实现超具体版

yield感化php技巧_PHP yield 分析以及协程的实现超具体版

访客 2024-12-08 0

扫一扫用手机浏览

文章目录 [+]

PHP的 yield 关键字是php5.5版本推出的一个特性,算是比较古老的了,其他很多措辞中也有类似的特性存在。
但是在实际的项目中,目前用到还比较少。
网上干系的文章最出名的便是鸟哥的那篇了,但是都不足细致理解起来较为困难,本日我来给大家超详细的先容一下这个特性。

function gen(){  while(true){    yield \公众gen\n\"大众;  }}$gen = gen();var_dump($gen instanceof Iterator);echo \公众hello, world!\"大众;

如果事先没理解过yield,可能会以为这段代码一定会进入去世循环。
但是我们将这段代码直接运行会创造,输出hello, world!,预想的去世循环没涌现。

yield感化php技巧_PHP yield 分析以及协程的实现超具体版

究竟是什么样的力量,征服了while(true)呢,接下来就带大家一起来领略一下yield关键字的魅力。

yield感化php技巧_PHP yield 分析以及协程的实现超具体版
(图片来自网络侵删)

首先要从foreach提及,我们都知道工具,数组和工具可以被foreach语法遍历,数字和字符串缺弗成。
实在除了数组和工具之外PHP内部还供应了一个 Iterator 接口,实现了Iterator接口的工具,也是可以被foreach语句遍历,当然跟普通工具的遍历就很不一样了。

以下面的代码为例:

class Number implements Iterator{  protected $key;  protected $val;  protected $count;  public function __construct(int $count){    $this->count = $count;  }  public function rewind(){    $this->key = 0;    $this->val = 0;  }  public function next(){  $this->key += 1;  $this->val += 2;  }  public function current(){    return $this->val;  }  public function key(){  return $this->key + 1;  }  public function valid(){    return $this->key < $this->count;  }}foreach (new Number(5) as $key => $value){  echo \"大众{$key} - {$value}\n\"大众;}

这个例子将输出

1 - 0

2 - 2

3 - 4

4 - 6

5 - 8

关于上面的number工具,被遍历的过程。
如果是初学者,可能会涌现有点懵的情形。
为了深入的理解Number工具被遍历的时候内部是怎么事情的,我将代码改了一下,将接口内的每个方法都尽心输出,借此来窥伺一下遍历时工具内部方法的的实行情形。

  class Number implements Iterator{ protected $i = 1; protected $key; protected $val; protected $count; public function __construct(int $count){ $this->count = $count; echo \"大众第{$this->i}步:工具初始化.\n\公众; $this->i++; } public function rewind(){ $this->key = 0; $this->val = 0; echo \"大众第{$this->i}步:rewind()被调用.\n\"大众; $this->i++; } public function next(){ $this->key += 1; $this->val += 2; echo \"大众第{$this->i}步:next()被调用.\n\公众; $this->i++; } public function current(){ echo \"大众第{$this->i}步:current()被调用.\n\"大众; $this->i++; return $this->val; } public function key(){ echo \"大众第{$this->i}步:key()被调用.\n\"大众; $this->i++; return $this->key; } public function valid(){ echo \"大众第{$this->i}步:valid()被调用.\n\"大众; $this->i++; return $this->key < $this->count; } } $number = new Number(5); echo \公众start...\n\"大众; foreach ($number as $key => $value){ echo \"大众{$key} - {$value}\n\公众; } echo \公众...end...\n\公众;

以上代码输出如下

第1步:工具初始化.start...第2步:rewind()被调用.第3步:valid()被调用.第4步:current()被调用.第5步:key()被调用.0 - 0第6步:next()被调用.第7步:valid()被调用.第8步:current()被调用.第9步:key()被调用.1 - 2第10步:next()被调用.第11步:valid()被调用.第12步:current()被调用.第13步:key()被调用.2 - 4第14步:next()被调用.第15步:valid()被调用.第16步:current()被调用.第17步:key()被调用.3 - 6第18步:next()被调用.第19步:valid()被调用.第20步:current()被调用.第21步:key()被调用.4 - 8第22步:next()被调用.第23步:valid()被调用....end...

看到这里,我相信大家对Iterator接口已经有一定认识了。
会创造当工具被foreach的时候,内部的valid,current,key方法会依次被调用,其返回值便是foreach语句的key和value。
循环的终止条件则根据valid方法的返回而定。
如果返回的是true则连续循环,如果是false则终止全体循环,结束遍历。
当一次循环体结束之后,将调用next进行下一次的循环直到valid返回false。
而rewind方法则是在全体循环开始前被调用,这样担保了我们多次遍历得到的结果都是同等的。

那么这个跟yield有什么关系呢,这便是我们接下来要说的重点了。
首先给大家先容一下我总结出来的 yield 的特性,包含以下几点。

1.yield只能用于函数内部,在非函数内部利用会抛出错误。

2.如果函数包含了yield关键字的,那么函数实行后的返回值永久都是一个Generator工具。

3.如果函数内部同事包含yield和return 该函数的返回值依然是Generator工具,但是在天生Generator工具时,return语句后的代码被忽略。

4.Generator类实现了Iterator接口。

5.可以通过返回的Generator工具内部的方法,获取到函数内部yield后面表达式的值。

6.可以通过Generator的send方法给yield 关键字赋一个值。

7.一旦返回的Generator工具被遍历完成,便不能调用他的rewind方法来重置

8.Generator工具不能被clone关键字克隆

首先看第1点,可以明白我们文章开头的gen函数实行后返回的是一个Generatory工具,以是代码可以连续实行下去输出hello, world!,因此$gen是一个Generator工具,由于实在现了Iterator,以是这个工具可以被foreach语句遍历。
下面我们来看看对其进行遍历,会是什么样的效果。
为了防止被去世循环,我加多了一个break语句只进行十次循环,方便我们理解yield的一些特性。

代码如下:

$i = 0; foreach ($gen as $key => $value) { echo \公众{$key} - {$value}\公众; if(++$i >= 10){ break; } }

以上代码输出为

0 - gen

1 - gen

2 - gen

3 - gen

4 - gen

5 - gen

6 - gen

7 - gen

8 - gen

9 - gen

通过不雅观察不难创造个中的规律。
在包含yield的函数返回的工具被foreach遍历时, 函数体内部的代码会被对应的实行。
PHP 会剖析其内部的代码从而天生对应的Iterator接口的方法。

个中key方法实现是返回的是yield涌现的次序,从0开始递增。

current方法则是yield后面表达式的值。

而valid方法则在当前yield语句存在的时候返回true, 如果当前不在yield语句的时候返回false。

next方法则实行从当前到下一个yield、或者return、或者函数结束之间的代码。

网上也有文章让大家把yield理解为暂时停滞函数的实行,等待外部的激活从而再次实行。
虽然看起来确实像那么回事,但我不建议大家这么理解,由于他本身是返回一个迭代器工具,其返回值是可以被用于迭代的。
我们理解了他被foreach迭代时,其内部是如运作的之后更易于理解yield关键字的实质。

下面我们再做一个大略的测试,以便更直不雅观的展示他的特性。

function gen1(){ yield 1; echo \"大众i\n\"大众; yield 2; yield 3+1; } $gen = gen1(); foreach ($gen as $key => $value) { echo \"大众{$key} - {$value}\n\"大众; }

以上的代码输出

0 - 1

i

1 - 2

2 - 4

我们来剖析一下输出的结果,首先当遍历开始时rewind被实行由于第一个yield之前无任何语句,无任何输出。

key的值为yield涌现的次序为0,current为yield表达式后的值也便是1。

foreach开始,valid由于当前为第一个yield,以是返回true。
正常输出0 - 1

此时next方法被实行,跳转到了第二个yield,第一个到第二个之间的代码被实行输出了i。

再次进入循环 实行vaild,由于当前在第二个yield上面,以是依然是true

由于next实行了,以是key的值也有刚刚的0变为了1,current的值为2,正常输出 1 - 2。

这时候连续实行next(),进入循环vaild()实行,由于此时到了第三个yield返回依然是true。
key的值为2, yield为4。
正常输出 2 - 4

再次实行next(),由于后续没有yield了vaild()返回为false, 以是循环到此便终止了。

下面我们用代码来验证一下

$gen = gen1(); var_dump($gen->valid()); echo $gen->key().' - '.$gen->current().\"大众\n\"大众; $gen->next(); var_dump($gen->valid()); echo $gen->key().' - '.$gen->current().\"大众\n\"大众; $gen->next(); var_dump($gen->valid()); echo $gen->key().' - '.$gen->current().\"大众\n\"大众; $gen->next(); var_dump($gen->valid());

输出值如下

bool(true)

0 - 1

i

bool(true)

1 - 2

bool(true)

2 - 4

bool(false)

跟我们的剖析完备同等,至此我们理解了Iterator接口在遍历时内部的运作办法,也理解了包含yield关键字的函数所天生的工具内部是如何实现Iterator接口的方法的。
对付yild的特性理解一半了,但是如果我们仅仅将其用于天生可以被遍历的工具的话,yield目前对我们来说,彷佛无太大的用途。
当然我们可以利用他来天生一些凑集工具,节约一些内存知道数据真正被用到的时候在天生。
例如:

我们可以写一个方法

function gen2(){ yield getUserData(); yield getBannerList(); yield getContext(); } #中间其他操作 #然后在view中得到数据 $data = gen2(); foreach ($data as $key => $value) { handleView($key, $value); }

通过以上的代码,我们将几个获取数据的操作都延迟到了数据被渲染的时候实行。
节省了中间进行其他操作时获取回来的数据占用的内存空间。
然而实际开放项目的过程中,这些数据每每被多处利用。
而且这样的构造让我们单独掌握数据变得困难,以此带来的性能提升相对付便利性来说,好处微乎其微。
不过还好的是,我们对yield的理解才刚刚到一半,已经有这样的功效了。
相信我们在理解完其余一半之后,它的功效将大大提升。

接下来我们来连续理解yield, 由于yield返回的是一个Generator类的工具,这个工具除了实现了Iterator接口之外,内部还有一个相称主要的方法便是send方法,即我们提到的第6点特性,通过send方法我们可以给yield发送一个值作为yield语句的值。

首先大家考虑一下下面的代码

function gen3(){ echo \"大众test\n\"大众; echo (yield 1).\公众I\n\"大众; echo (yield 2).\公众II\n\"大众; echo (yield 3 + 1).\公众III\n\"大众; } $gen = gen3(); foreach ($gen as $key => $value) { echo \"大众{$key} - {$value}\n\公众; }

实行往后输出

test

0 - 1

I

1 - 2

II

2 - 4

III

可能这段输出比较难明得,我们接下来,一步一步剖析一下为什么得出这样的输出。
由于我们知道了foreach的时候gen内部是如何操作的,那么我们便用代码来实现一次。

$gen = gen3(); $gen->rewind(); echo $gen->key().' - '.$gen->current().\公众\n\"大众; $gen->next();

实行后输出

0 - 1

I

通过这两句我们创造,当前的key为0,current则为1也便是yield后面表达式的值。
由于yield 1被括号括起来了,以是yield后面表达式的值是1,如果没有括号则为1.\"大众I\n\公众.当然由于1.\公众I\n\"大众是一个缺点语法。
如果想要测试的朋友须要给1加上双引号。

当实行next时,第1个yield到第二个yieldz之间的的语法被实行。
也便是echo (yield 1).\公众I\n\"大众被实行了,由于我们利用的是next(),以是yield当前是无值的。
以是输出了I。
须要把稳的是在第一个yield之后的语法将不会被实行,而 echo (yield 2). \"大众II\n\"大众;属于下一个yield块的语句,以是不会被实行。

到这里,是时候让我们本日末了的主角send方法来表现一下了。

public mixed Generator::send ( mixed $value )

这个是手册里send方法的描述,可以看出来他可以接管一个mixed类型的参数,也会返回一个mixed类型的值。

传入的参数会被做 yield 关键字在语句中的值,而他的返回值则是next之后,$gen->current()的值。

下面我们来考试测验一下

$gen = gen3(); $gen->rewind(); echo $gen->key().' - '.$gen->current().\"大众\n\公众; echo $gen->send(\公众send value - \"大众);

实行后输出

0 - 1

send value - I

2

这时候我们创造,我们通过send方法成功的将一个值通报给了一个函数的内部,并且当做yield关键字的值给输出了,由于下一个yield的值为2,以是我们调用send返回的值为2,同样被输出。

虽然我们知道了send可以完成内部对函数内部的yield表达式传值,也知道了可以通过$gen->current()获得当前yield表达式之后的值,但是这个有什么用呢。
可以看一下这个函数

function gen4(){ $id = 2; $id = yield $id; echo $id; } $gen = gen4(); $gen->send($gen->current() + 3);

根据上面对yield代码的理解,我们不难创造这个函数会输出5,由于current()为2,而当我们send之后 yield的值为 2 + 3,也便是5.同时yield到函数结束之间的代码被实行。
也便是$id = 5; echo $id;

通过这样一个大略的例子,我们创造。
我们不但从函数内部得到了返回值,并且将他的返回值再次发送给了函数内部参与后续的打算。

关于yield的先容就到此为止了,本文至此也告一段落。
后续将会给大家带来,关于yield的下篇,实现一个调度器使得我们只须要将gen()函数返回的gen工具通报给调度器,其内部的代码就能自动的实行。
并且让利用yield来实现并行(伪),以及在多个$gen工具实行之间建立联系和掌握其实行顺序,请大家多多关注。
其余由于本人才疏学浅,yield特性较多也较为繁琐。
文章内容难免有出错或者不全面的地方,如果大家创造有缺点的地方,也希望大家留言奉告, 祝大家五一愉快~

标签:

相关文章

php权限上传文件技巧_php的文件上传

这里首先声明一下这一章的内容比较多,比较难,你要抱着和自己去世磕的态度。细微之处不放过,多敲多练是王道。 学习就像爬山,得一步一步...

Web前端 2024-12-10 阅读0 评论0

租片女郎php技巧_想昔时|本能相爱相杀

小编阿树树这里是一个怀旧戏院在互联网还没涌现的年代,要找一部大尺度电影并不是一件大略的事情。首先须要一台专属设备——最早是录像机,...

Web前端 2024-12-10 阅读0 评论0