这篇文章,要和你们探讨的是 PHP yield 在 生成器用法,不带 foreach
,for
, while
循环的那种。就讨论 yield
将一个函数变成为生成器的用法。php
关于yield
特性,是在开发PHP5
时被提上日程,PHP5.5
版本正式加入。
关于yield
的使用,我看到大部分文章都停留在,使用yield
如何在foreach
中传出数据,今天想给你们讲讲 生成器 全部语法。git
生成器容许你在 foreach
代码块中写代码来迭代一组数据而不须要在内存中建立一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你能够写一个生成器函数,就像一个普通的自定义函数同样, 和普通函数只返回一次不一样的是, 生成器能够根据须要 yield
屡次,以便生成须要迭代的值。express
看了下官网对他讲解:php.net 生成器语法 . 每一个字都认识,但彷佛仍是体会到它讲的内涵。官网咱们主要看两部份内容:segmentfault
yield
的语法。先说语法, yield 的左边是一个赋值语句,右边能够是值(也但是表达式) 。而yield 会先执行右边的表达式,并把值$value送到生成器外面。当生成器收到值后,会执行yield左边的语句,赋值给$data.数组
<?php $data = (yield $express);
语法讲完了,估计你们仍是有些懵,那就看看官网下面代码例子吧,我看里面例子良莠不齐。函数
不管是学 人类语言,计算机语言,都是模仿开始
对于一个用人类语言来描述,都不那么明晰时,因此那就经过例子告诉你它能作什么,不能作什么。oop
相关代码,我放到gitee了,但愿你能复制到你本地运行下,亲自运行感觉下,有助于了理解接下来的内容。测试
先定义一个函数,在函数内 写个 yield 关键词,将这个函数调用赋值给一个变量。一个生成器就产生了。spa
因为例子代码不少,我把例子放到 gitee 了。同时如下的文章中提到"例子"就是在 gitee
中的代码。.net
代码 /php-yield-test/yieldFunctions.php 是生成器按照不一样语法组合定义了多个生成器。
测试代码 /php-yield-test/whatIsGenerator.php,用来检查哪些函数能构成生成器,哪些不能。运行结果以下
yield
关键词,函数能够是全局函数,或者类的方法。yield
确定不会被执行,也会产生生成器。见:yield_func4yield
关键词就行(不向外送出,不处理外面的输入)。见: yield_func2yield
会报错, 见:yield_func11
是的,函数内有没有foreach,while,for 语句都不是关键,关键是 yield. 生成器的类型判断用
$gen instanceof Generator
Generator 对象是从 generators返回的.
Generator 对象不能经过 new 实例化.
摘自 php.net generator看着以上方法,是不想起了
Iterator
, 他们的确很像。同时注意,官网zh语言版本的文档没有索引方法getReturn
,访问也是404。文档以en版为准,ch作参考。
以上就是生成器全部的方法,咱们一个个来看。
测试方法代码 /php-yield-test/generatorMothod.php, 这里面对每一个方法都有使用举例,运行结果以下。
好接下来对举例作个一一讲解。
<?php function 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.
<?php function 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传给用户。
也就是说:跳过current,直接调用send,会丢失yield的弹出值。
转载著名出处 sifou
<?php function 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: 4 current called run to code line: 6
例子5,这是一个较为常规的调用,调用current
代码运行yield
等到用户输入,这是调用next跳过,让代码继续运行。
例子6,直接调用next
,至关于调用current
,next
。并且经过最后打印$result
, 咱们发现怎么有点像在调用 $gen->send(NULL);
。
<?php function 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代码段后,再次调用该方法,会致使报错(哪怕该 生成器已结束)。
<?php function 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传入异常,就无法捕获了。
<?php function 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就是 关闭状态。
<?php function 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
被隐式调用。
<?php function 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,看样子序列化成功了。也就是说一个生成器作为一个方法能够被序列化,当函数变成生成器时,就不能被序列化了。
<?php function 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 可以获取到生成器最后的返回值。
例子1九、20 可得,当生成器没有执行到return语句,或者没有执行到最后时,调用getReturn是会致使报错。
到这里,咱们就发现rewind
,next
和 __wakeup
这两个函数感受没啥叼用呢,为啥还存在呢,由于Generator
继承Iterator
,天然就有了rewind
, next
方法,PHP
虽然支持方法覆盖,但子类的访问修饰符
不能缩紧,因此Generator
只能重写这两个方法。 __wakeup
继承自 stdClass
。
看图:
花了两个状态图,上面的要细致一点,下面的精简一些,便于理解。
以上就是关于 PHP 生成器全部内容,但愿你能学会掌握这门强大的语法,下一讲,咱们手把手一块儿来作一个任务调度器,实战一下。
有问题欢迎提问,谢谢你们!