生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。
生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。
我们用伪代码来举个例子:
直接遍历:
<?php
//普通遍历
$User = new User(); //获取一个可以操作数据的模型
$list = $User->getAll(); //获取所有用户数据,返回一个大数组 ,数据越多,内存开销就越高,会严重影响性能。
foreach($list as $key => $v){
....//对键值进行操作,赋值等等,严重影响性能。
}
这时候,我们的生成器就可以派上用场了,我们看看Generator的实现:
//Generator 实现了Iterator 接口
final class Generator implements Iterator {
function rewind() {} //重置生成器,如果生成器当前在第一个yield之后,则抛出异常。
function valid() {} //检查,如果生成器已关闭,则返回false,否则返回true。
function current() {} //返回当前产生的值
function key() {} //返回当前产生的键
function next() {} //继续执行下一个
function send($value) {}//设置yield表达式的返回值并恢复生成器的执行(除非生成器已经关闭)。
function PS_UNRESERVE_PREFIX_throw(Throwable $exception) {} //在生成器的当前挂起点抛出异常。
function getReturn() {}//返回传递给return的任何值,如果没有,则返回null。如果生成器仍然有效,则抛出异常。
public function __wakeup(){} //序列化回调,在生成器不能被序列化时抛出异常。
}
生成器特性简述:
1、生成器最大的优点就是迭代数据,性能开销小,简单易用。
2、yield必须有函数包裹,包裹yield的函数称为”生成器函数”,该函数将返回一个可遍历的对象
3、生成器Generator类是Iterator 的实现,但是修改了内部的逻辑。
4、生成器是暂停循环执行逻辑,等到用的时候,才会触发循环再次执行。while 不会对它造成阻塞
<?php
for ($i = 0;$i<10000;$i++){
//此时循环只执行一次,除非有内容触发(需要$i)才会再次执行
yield $i;
}
写一个应用场景
<?php
$conn = @mysqli_connect('loca1host ' , ' root ' , 'root ' , ' db' , '3306') or die('数据库连接失败! ');
mysqli_set_charset($conn , 'utf8 ' ) or die('字符集设置失败! ');
function query($conn, $sq1){
$res = mysq1i_query(Sconn, $sq1);
while($row = mysqli_fetch_assoc( $res)){
yield $row;//这样就不需要数组保存大数据了,如果数据量够大会产生很大的消耗
}
}
//遍历输出
$list = query($conn,'SELECT * FROM user');
foreach($list as $v){
...操作数据
}
生成器可以返回表达式
此特性基于 PHP 5.5 版本中引入的生成器特性构建的。 它允许在生成器函数中通过使用 return
语法来返回一个表达式 (但是不允许返回引用值), 可以通过调用 Generator::getReturn()
方法来获取生成器的返回值, 但是这个方法只能在生成器完成产生工作以后调用一次
<?php
$gen = (function() {
yield 1;
yield 2;
return 3;
})();
foreach ($gen as $val) {
echo $val, PHP_EOL;
}
echo $gen->getReturn(), PHP_EOL;
//返回
1
2
3
在生成器中能够返回最终的值是一个非常便利的特性, 因为它使得调用生成器的客户端代码可以直接得到生成器(或者其他协同计算)的返回值, 相对于之前版本中客户端代码必须先检查生成器是否产生了最终的值然后再进行响应处理 来得方便多了。
Generator delegation
现在,只需在最外层生成其中使用 yield from
, 就可以把一个生成器自动委派给其他的生成器, Traversable 对象或者 array。
<?php
function gen()
{
yield 1;
yield 2;
yield from gen2();
}
function gen2()
{
yield 3;
yield 4;
}
foreach (gen() as $val)
{
echo $val, PHP_EOL;
}
?>
//返回
1
2
3
4