说到php中的Generator(生成器),有些人可能会想到协程。这里先不说php是如何实现协程的。下面我们来探究一下Generator的执行过程。Generator是通过yield实现的,yield关键字是php5.5版本引入的特性。首先看下面的代码:functiongen(){ while(true){ yield"gen\n"; }}$gen=gen();echo"发电机";如果通过yield,你会认为上面代码执行的结果是:死循环。但实际上,它会回显生成器。说到这里,你可能会觉得奇怪,yield怎么会结束循环呢?下面给大家解释一下:Generator提供的方法:Generator::current——返回当前生成的值Generator::key——返回当前生成的keyGenerator::next——生成器继续执行Generator::rewind——重置iteratorGenerator::send—将值传递给生成器Generator::throw—向生成器抛出异常Generator::valid—检查迭代器是否关闭Generator::__wakeup—序列化回调生成器提供了一种更简单的方法来实现简单对象迭代(iterators),相比定义一个实现了Iterator接口的类,性能开销和复杂度大大降低。示例:functiongen(){for($i=0;$i<5;$i++){echo(yield$i).$i.'
';}}$gen=gen();foreach($genas$k=>$v){echo"{$k}---{$v}".'
';}结果是:从上面的结果,我们可以分析出以下几点:1foreachedGenerator对象时,会依次调用内部的valid、current、key方法,返回值为的value和keyforeach语句。2循环的终止条件取决于有效方法的返回。如果返回true,则继续循环,如果为false,则终止整个循环,结束遍历。3循环体结束后,会调用next进行下一次循环,直到valid返回false。rewind方法在整个循环开始前(即生成Generator对象时)被调用,保证了我们多次遍历得到的结果是一致的。我们来证明一下这个过程:$gen=gen();echo$gen->key();//结果为0,生成Generator对象时,已经执行了rewind。echo$gen->key().'----'.$gen->current();//0----0var_dump($gen->next());//var_dump值为null,但是echo中会多一个0;这个0是怎么来的?原因是:next()执行后,执行了第一个yield和第二个yieldz之间的语法,即:echo(yield$i).$i.'
';由于next()没有返回值,即表达式(yield$i)没有值,$i的值为0;echo$gen->key().'----'.$gen->current();//1----1当前为第二个yield上面的例子可以证明Generator的内部过程支付特别注意对next()的理解。最后说一下send():官方分析:传递一个值给生成器,作为yield表达式的结果,然后继续执行生成器。如果调用此方法时生成器不在yield表达式中,它将在传入值之前运行到第一个yield表达式。翻译下的结论是:send()方法主要用于向当前yield发送数据,即把yield表达式替换为一个值,继续执行下一个yield,即next()证明例子:$gen=gen();$gen->send(666);//66606660结果分析:先将666替换为当前yield表达式的值,然后执行next(),即运行echo(yield$i).$i.'
',当前yield为666,所以最终结果为:6660。注意与next()的区别!!!总结:1.yield只能在函数内部使用,在非函数内部使用会报错。2.如果函数中包含yield关键字,则函数执行后的返回值永远是一个Generator对象。3.如果函数中包含yield和return,函数的返回值仍然是一个Generator对象,但是在生成Generator对象时,return语句之后的代码会被忽略。4.Generator类实现了Iterator接口。5.函数内部yield后表达式的值可以通过返回的Generator对象内部的方法获取。6、可以通过Generator的send方法给yield关键字赋值。7、返回的Generator对象一旦被遍历过,就不能通过调用它的rewind方法来重置。8.Generator对象不能通过clone关键字进行克隆。实际应用:1.Coroutine2.Genenrator返回一个迭代器,在处理大数据时不需要一次性加载到内存,见http://php.net/manual/zh/lang....
