生成器是PHP 5.5.0才引入的功能, 生成器函数看上去就像一个普通函数,除了不是返回一个值之外,
生成器会根据需求产生更多的值.
function createRange($number){
$data = [];
for($i=0;$i<$number;$i++){
$data[] = time();
}
return $data;
}
$result = createRange(10); // 这里调用上面我们创建的函数
foreach($result as $value){
sleep(1);//这里停顿1秒,我们后续有用
echo $value.'<br />';
}
function createRange($number){
for($i=0;$i<$number;$i++){
yield time();
}
}
$result = createRange(10); // 这里调用上面我们创建的函数
foreach($result as $value){
sleep(1);
echo $value.'<br />';
}
会发现当对象被foreach的时候, 内部的valid,current,key方法会依次被调用,
其返回值便是foreach语句的key和value.循环的终止条件则根据valid方法的返回而定.
如果返回的是true则继续循环, 如果是false则终止整个循环, 结束遍历.当一次循环体结束之后,
将调用next进行下一次的循环直到valid返回false.而rewind方法则是在整个循环开始前被调用,
这样保证了多次遍历得到的结果都是一致的
Iterator extends Traversable {
/* Methods */
abstract public mixed current ( void ) //返回当前位置的元素
abstract public scalar key ( void ) //返回当前元素对应的key
abstract public void next ( void ) //移到指向下一个元素的位置
abstract public void rewind ( void ) //倒回到指向第一个元素的位置
abstract public boolean valid ( void ) //判断当前位置是否有效
}
class Number implements Iterator{
protected $i = 1;
protected $key;
protected $val;
protected $count;
public function __construct(int $count){
$this->count = $count;
echo "第{$this->i}步:对象初始化.\n";
$this->i++;
}
public function rewind(){
$this->key = 0;
$this->val = 0;
echo "第{$this->i}步:rewind()被调用.\n";
$this->i++;
}
public function next(){
$this->key += 1;
$this->val += 2;
echo "第{$this->i}步:next()被调用.\n";
$this->i++;
}
public function current(){
echo "第{$this->i}步:current()被调用.\n";
$this->i++;
return $this->val;
}
public function key(){
echo "第{$this->i}步:key()被调用.\n";
$this->i++;
return $this->key;
}
public function valid(){
echo "第{$this->i}步:valid()被调用.\n";
$this->i++;
return $this->key < $this->count;
}
}
$number = new Number(5);
echo "start...\n";
foreach ($number as $key => $value){
echo "{$key} - {$value}\n";
}
echo "...end...\n";
Generator implements Iterator {
/* 方法 */
// 返回当前产生的值
public current ( void ) : mixed
// 返回当前产生的键
public key ( void ) : mixed
// 生成器继续执行
public next ( void ) : void
// 重置迭代器
public rewind ( void ) : void
// 向生成器中传入一个值
public send ( mixed $value ) : mixed
// 向生成器中抛入一个异常
public throw ( Exception $exception ) : void
// 检查迭代器是否被关闭
public valid ( void ) : bool
// 序列化回调
public __wakeup ( void ) : void
}
传统解决方法: 内存溢出时报 Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) 错误,以往通过php.ini或者ini_set() 来设置一个进程的更大内存,虽然能解决大内存问题但不利于接口的性能。
yield原理:假如有100条数据 用foreach就是一次性把数据放到一个数组,内存长度占100。 用yield是一条一条放进去,而且每次放完一条就“销毁”,然后总内存是一条,内存长度只占1。可以想象yield有点类似 return
常见使用场景
用法1,处理一个返回值或一个键值对
// yield
function createRange($number){
for($i=1;$i<$number;$i++){
yield $i => time();
}
}
$result = createRange(10); // 这里返回生成器对象(里面是我们要的数组data,但内存只占1)
foreach($result as $k=>$value){
echo $value.$k;
}
用法2,处理sql查询出来的数组
public static function yieldData($data){
foreach ($data as $datum){
yield $datum;
}
}
$res = Db()->select();
$list = self::yieldData($res);
// 逻辑处理
foreach($list as $k => &$v){}
// 导出等等
Common::csv($list)
用法3,文件读取/导入功能
function readTxt($path)
{
$handle = fopen($path, 'rb');
while (feof($handle)===false) {
yield fgets($handle);
}
fclose($handle);
}
foreach (readTxt() as $key => $value) {
// 逻辑处理
}
总结
yield生成器允许你 在 foreach 代码块中写代码来迭代一组数据,而不需要在内存中创建一个数组,极大提高了进程的内存使用率