PHP yield 协程 生成器用途的理解
官网讲解
生成器同意你在 foreach
代码块中写代码来迭代一组数据而不需要在内存中创立一个数组, 那会使你的内存到达上限,或者会占据可不雅的处置时间。相反,你可以写一个生成器函数,就像一个一般的自定义函数一样, 和一般函数只返回一次不一样的是, 生成器可以按照需要 yield
屡次,以便生成需要迭代的值。
看了下官网对他讲解:php.net 生成器语法 . 每个字都认识,但好像还是体味到它讲的内在。官网我们主要看两部分内容:
yield
的语法。代码例子。
先说语法, yield 的左边是一个赋值语句,右侧可以是值(也可是表达式) 。而yield 会先施行右侧的表达式,并把值$value送到生成器外面。当生成器收到值后,会施行yield左边的语句,赋值给$data.
<?phpfunction func(){ $data = (yield [$express]);}
语法就这样,估量大家还是有些懵,那就看看官网下面代码例子吧,我看里面例子参差不齐。
留意yield 外面包的这一层括号,假如是在php5.5,右侧$express的优先级是推断,大概会比左侧$data的赋值语句低的。所以在php5用yield,yield 右侧是可运转表达式,左侧需要接受返回并赋值,那么这个括号是有必要的。在php7不会有这个问题。
通过例子来理解它
不管是学 人类说话,运算机说话,都是仿照开端
关于一个用人类说话来描写,都不那么明晰时,所以那就通过例子告诉你它能做什么,不克不及做什么。
相关代码,我放到gitee了,但愿你能复制到你当地运转下,亲自运转感受下,有助于了懂得接下来的内容。
git clone gitee.com/xupaul/PHP-generator-yie...
怎样才能发生 Generator
先定义一个函数,在函数内 写个 yield 关键词,将这个函数调取赋值给一个变量。一个生成器就发生了。
代码 /php-yield-test/yieldFunctions.php 是生成器依照不一样语法组合定义了多个生成器。
测试代码 /php-yield-test/whatIsGenerator.php,用来检查哪些函数能构成生成器,哪些不克不及。运转结果如下
- 函数内必需有
yield
关键词,函数可以是全剧函数,或者类的办法。 - 哪怕
yield
必定不会被施行,也会发生生成器。见:yield_func4 - 光秃秃 的
yield
关键词就行(不向外送出,不处置外面的输入)。见: yield_func2 - 函数内使用 生成器 并不克不及让本人也成为生成器,见:yield_func5
- eval函数中直接运转
yield
会报错, 见:yield_func11
是的,函数内有没有foreach,while,for 语句都不是关键,关键是 yield. 生成器的类型推断用
$gen instanceof Generator
生成器的函数
Generator 对象是从 generators返回的.
Generator 对象不克不及通过 new 实例化.
- Generator::current — 返回当前发生的值
- Generator::key — 返回当前发生的键
- Generator::next — 生成器连续施行
- Generator::rewind — 重置迭代器
- Generator::send — 向生成器中传入一个值
- Generator::throw — 向生成器中抛入一个非常
- Generator::valid — 检查迭代器可否被关闭
- Generator::__wakeup — 序列化回调
- Gengerator::getReturn - Get the return value of a generator
摘自 php.net generator
看着以上办法,是不想起了
Iterator
, 他们确实很像。同时留意,官网zh说话版本的文档没有索引办法getReturn
,拜访也是404。文档以en版为准,ch做参照 。
以上就是生成器所有的办法,我们一个个来看。
测试办法代码 /php-yield-test/generatorMothod.php, 这里面临每个办法都有使用举例,运转结果如下。
好接下来对举例做个一一讲解。
Generator::current
- 返回当前发生的值
<?phpfunction yield_func(){ yield 12; return 'a';}$gen = yield_func();$re = $gen->current();echo 'current return : ' . $re;
输出:
current return : 12
看到 php-yield-test/generatorMothod.php
代码。
通过第一个代码事例,可得,对一个generator调取current办法,才算真正开端施行。施行到yield为止。假如不克不及命中yield,则施行到函数完毕。
非generoator会立马施行并得到结果,而非一个生成器对象。
通过例子2,调取current一次,两次呢,第一次可以看到代码施行日志,第二次,只是把上一次的结果返回给我们罢了,并不是让该生成重视新施行。
通过例子1,调取该函数还会猎取到返回值,返回的内容就是 yield 表达式左边的内容。假如表达式无内容,则是NULL.
Generator::send
- 向生成器
yield
点中传入一个值,并返回下一次current
值。
<?phpfunction yield_func(){ $data = yield 12; echo 'get yield data: ' . $data; return 'a';}$gen = yield_func();$re = $gen->current();$gen->send(32);
输出:
get yield data: 32
例子3,是一个current,send的常规调取。调取current代码运转yield比及会员send输入参数。接收到输入后,连续运转。current能够接收到yield弹出的值,send返回值为空。
例子4,直接调取send,相当于调取current,send。不外current的返回值,并不会通过send传给会员。
例子21中,可以看到直接调取send(1),会运转生成器,并向第一个yield处输入1,连续运转至下一个yield的返回值value
。所以,$gen->send(2)
,和 $gen->current()
结果都是统一个值。
也就是说:跳过current,直接调取send,会丧失第一次yield的弹出值。
Generator::next
- 跳过中止,并让生成器连续施行
<?phpfunction yield_func(){ echo 'run to code line: ' . __LINE__ . PHP_EOL; yield; echo 'run to code line: ' . __LINE__ . PHP_EOL; return $result;}$gen = yield_func();$gen->current();echo 'current called' . PHP_EOL;$gen->next();
输出:
run to code line: 4current called run to code line: 6
例子5,这是一个较为常规的调取,调取current
代码运转yield
比及会员输入,这是调取next跳过,让代码连续运转。
例子6,直接调取next
,相当于调取current
,next
。并且通过最后打印$result
, 我们发明如何有点像在调取 $gen->send(NULL);
。
Generator::rewind
- 重置迭代器
<?phpfunction yield_func(){ echo 'run to code line: ' . __LINE__ . PHP_EOL; $result = yield 12; echo 'run to code line: ' . __LINE__ . PHP_EOL;}$gen = yield_func();echo 'call yield_func rewind ' . PHP_EOL;$gen->rewind();
输出:
call yield_func rewind run to code line: 4
例子7,8 中,发明调取该办法,会致使隐式调取current
。
例子9 中,发明在施行过一个yield代码段后,再次调取该办法,会致使报错(哪怕该 生成器已完毕)。
Generator::throw
- 向生成器中抛入一个非常
<?phpfunction yield_func(){ try { $re = yield 'exception'; } catch (Exception $e) { echo 'catched exception msg: ' .$e->getMessage(); }}$gen = yield_func();$gen->throw(new \Exception('new yield exception'));
输出:
catched exception msg: new yield exception
通过以上简便的例子可得,throw 就是让yield这行代码发生非常,让外面的try catch 捕捉我们生成的阿谁非常。
例子11中,结构生成器,并调取current办法,运转到yield处,再调取throw,就能捕捉到非常。
例子12中,当调取send办法,跳过函数内yield代码时,再调取throw传入非常,就没法捕捉了。
Generator::valid
- 检查迭代器可否被关闭
<?phpfunction yield_func(){ yield 12; return 'a';}$gen = yield_func();$gen->send(1);$check = $gen->valid();echo 'the generator valid ? ' . intval($check);
输出:
the generator valid ? 0
例子12中,发明current被隐式调取。
例子13中,可得,当生成器运转到yield代码段时,用valid
函数检查,都会返回true
。
所以,别问我可否已运转,问就是运转。该办法用来猎取可否关闭状态,不是 可否运转状态!运转到底,运转到return就是 关闭状态。
Generator::key
- 返回当前发生的键
<?phpfunction yield_func(){ yield 1 => 'abc';}$gen = yield_func();echo 'value is :' . $gen->current() . PHP_EOL;echo 'key is: ' . $gen->key() . PHP_EOL;
输出:
value is :abc key is: 1
从以上例子中,可得yield可显示设定返回的key.
例子15 中,发明key的分发纪律和PHP数组键值发放战略是差不多的,默许从0开端,未指定则是以上一个数字key+1
作为当前的key
.
例子16 中,我们又发明current
被隐式调取。
Generator::__wakeup
- Generator::__wakeup — 序列化回调
<?phpfunction yield_func(){ yield 1 => 'abc';}$gen = yield_func();try {$ser = serialize($gen);} catch (\Exception $e) { print_r($e->getMessage());}
输出:
Serialization of 'Generator' is not allowed
这是一个魔术办法,见 PHP 魔术办法,也就是说 生成器 不克不及被序列化成一个字符串。
例子17就不消说了,看下例子18,看模样序列化成功了。也就是说一个生成器做为一个办法可以被序列化,当函数变成生成器时,就不克不及被序列化了。
Generator::getReturn
<?phpfunction yield_func(){ yield 1 => 'abc'; return 32;}$gen = yield_func();$gen->send(0);echo 'call yield_func return, and get: ' . $gen->getReturn();
输出:
call yield_func return, and get: 32
该函数就是猎取生成器最后的返回值。假如没有return语句,或者没有施行到return语句,调取该函数得到的就是NULL。
例子19 可得,getReturn 能够猎取到生成器最后的返回值。
例子19、20 可得,当生成器没有施行到return语句,或者没有施行到最后时,调取getReturn是会致使报错。
综上所述
到这里,我们就发明rewind
,next
和 __wakeup
这两个函数感受没啥叼用呢,为啥还存在呢,由于Generator
继承Iterator
,天然就有了rewind
, next
办法,PHP
虽然支撑办法覆盖,但子类的拜访润饰符
不克不及缩紧,所以Generator
只能重写这两个办法。 __wakeup
继承自 stdClass
。
状态转换
看图:
PHP yield 生命周期图
画了两个状态转换图,上面的要详细,繁复一点。下面的精简版,便于快速懂得。
总结
以上就是关于 PHP 生成器的根基内容,但愿你看了后对它有更进一步认识。下一讲,我们手把手一起来做一个任务调度器,实战一下。
有问题欢迎发问,感谢大家!
以上就是PHP yield 协程 生成器用途的理解的具体内容,更多请关注百分百源码网其它相关文章!