写在上一篇文章中。我想和大家讨论的是phpyield在generator中的用法,不用foreach,for,while循环。仅讨论使用yield将函数转换为生成器。关于yield特性,在PHP5开发的时候就提上了日程,PHP5.5版本正式加入。关于yield的使用,我看到大部分文章都卡在foreach中如何使用yield传输数据。今天我想告诉你关于生成器的所有语法。Trilogyyieldsyntaxexplorationyieldfromsyntaxexplorationyield实用“多线程”编码官网解释生成器允许你在foreach代码块中编写代码来迭代一组数据,而无需在内存中创建数组,这将使你的内存达到最大值,否则会占用相当长的处理时间。相反,您可以编写一个生成器函数,就像普通的自定义函数一样,而不是只返回一次,生成器可以根据需要多次生成需要迭代的值。我看了官网给他解释:php.netgeneratorgrammar。每个字我都认识,但我似乎还是明白它说的内涵。我们主要看官网上的两部分:yield的语法。使用示例。先说语法,yield左边是赋值语句,右边可以是一个值(或表达式)。而yield会先执行右边的表达式,并将值$value送出生成器。生成器接收到值后,会执行yield左边的语句,将值赋值给$data.current();echo'getre:'.$re.PHP_EOL;$gen->send('cc');$re2=$gen->current();echo'getre2:'。$re2.PHP_EOL;$gen->send('hello');$re3=$gen->getReturn();echo'getreturn:'。$re3。PHP_EOL;图中看到yield_func()函数作为生成器后,按yield分成了一段段代码。一段没有执行完就跳出来,等待外部程序的调度。这就像多线程一样,在执行过程中,随时可能被打断让出CPU,但是协程是手动调用yield让出,程序运行顺序是可以预见的。调用current()开始执行。当运行yield代码并释放cpu时,将调用send()以允许生成器继续运行。最后getReturn()获取生成器的返回值。好了,我们先大概了解一下,现在我们来详细了解一下各个功能。Generator::current返回当前生成的值current();echo'currentreturn:'。$重新;输出:当前返回:12参见php-yield-test/generatorMothod.php代码。通过第一个代码示例可以看出调用生成器上的当前方法才是真正开始执行。执行直到屈服。如果yield无法命中,它将一直执行到函数结束。非生成器将立即执行并获得结果,而不是生成器对象。通过例子2,调用current一次,两次,第一次可以看到代码执行日志,第二次就把上次的结果返回给我们,不让generator再次执行。通过例子1,调用这个函数也会得到返回值,返回的内容就是yield表达式左边的内容。如果表达式没有内容,则为NULL.Generator::send给生成器传一个值current();$gen->send(32);输出:getyielddata:32示例3是current和send的常规调用。调用当前代码运行yield并等待用户发送输入参数。接收到输入后,继续运行。current可以接收yield弹出的值,send的返回值为空。例4,直接调用send相当于调用current和send。但是current的返回值不会通过send传递给用户。也就是说:跳过current直接调用send会丢失yield的弹出值。转载著名源码sifouGenerator::next让生成器继续执行current();echo'currentcalled'。PHP_EOL;$gen->next();output:runtocodeline:4当前调用的runtocodeline:6示例5,这是一个更常规的调用。调用当前代码运行yield并等待用户输入。这是调用next跳过,让代码继续运行。例6,直接调用next相当于调用current和next。通过在最后打印$result,我们发现它有点像调用$gen->send(NULL);。Generator::rewindresetiteratorrewind();Output:callyield_funcrewindruntocodeline:4在示例7和8中,发现调用此方法会导致对current的隐式调用。在Example9中,发现执行完一个yield代码段后,再次调用该方法会报错(即使generator已经结束)。Generator::throw向生成器中抛出异常getMessage();}}$gen=yield_func();$gen->throw(new\Exception('newyieldexception'));output:caughtexceptionmsg:newyieldexception可以通过上面的简单例子得到,throw是让代码的yield行产生异常,让外面的trycatch捕获我们产生的异常。Example11,构造一个generator,调用current方法,run到yield,然后调用throw捕获异常。例12调用send方法时,跳过了函数中的yield代码,然后调用throw传入异常,无法捕获。Generator::valid检查迭代器是否关闭send(1);$check=$gen->valid();echo'生成器有效吗?'。间隔($检查);输出:生成器有效?0在例子12中,发现隐式调用了current。在Example13中可以看出,当生成器运行到yield代码段时,用valid函数检查时会返回true。所以,不要问我是否有效,只要问。这个方法是用来获取是否关闭的,不是用来获取是否运行的!运行到结束,运行返回是关闭状态。Generator::key返回当前生成的键'abc';}$gen=yield_func();echo'valueis:'。$gen->current().PHP_EOL;echo'keyis:'.$gen->key()。PHP_EOL;Output:valueis:abckeyis:1从上面的例子,yield可以显示设置返回的key。在例15中,我们发现key的分配规则和PHP数组key值的分配策略是类似的,默认从0开始,如果不指定,会使用前一个数字key+1作为当前key。在示例16中,我们发现current被隐式调用。Generator::__wakeupGenerator::__wakeup—序列化回调'abc';}$gen=yield_func();try{$ser=serialize($gen);}catch(\Exception$e){print_r($e->getMessage());}Output:Serializationof'Generator'isnotallowed这是一个魔术方法,参见PHP魔术方法,这意味着生成器不能被序列化为字符串。例子17不用说了,看例子18,好像序列化成功了。也就是说,一个生成器可以被序列化为一个方法,但是当一个函数成为一个生成器时,它就不能被序列化了。生成器::getReturn'abc';return32;}$gen=yield_func();$gen->send(0);echo'callyield_funcreturn,andget:'.$gen->getReturn();输出:调用yield_funcreturn,得到:32该函数是获取生成器最终的返回值。如果没有return语句,或者没有执行return语句,调用该函数的结果为NULL。例19getReturn可以得到生成器的最终返回值。从例子19和20可以看出,当generator没有执行return语句,或者没有执行到最后,调用getReturn会报错。综上所述,我们发现rewind、next和__wakeup这两个函数是没用的。为什么它们仍然存在?因为Generator继承了Iterator,自然有rewind和next方法。PHP虽然支持Method覆盖,但是子类的访问修饰符不能收紧,所以Generator只能重写这两个方法。__wakeup继承自stdClass。看状态转移图:画两张状态转移图,上面要详细复杂。下面的浓缩版是为了快速理解。综上所述,以上都是关于PHP生成器的内容。我希望你能学会掌握这个强大的语法。下一讲,我们一起来搭建一个任务调度器,试试看。欢迎提问,谢谢!
