我们身边的现代PHP(三)

生成器

PHP生成器(generator)是PHP5.5.0引入的功能,我估计很多小伙伴都没用过,甚至不知道生成器这个特性的存在,因为生成器的作用不是很明显。生成器是简单点迭代器,仅此而已。

与标准的PHP迭代器不同,PHP生成器不要求类实现Itereaor(不知道这个是什么?看补充迭代器部分)接口,从而减轻了类的负担。假如标准的PHP迭代器经常在内存中执行迭代操作,这要预先计算出数据集,性能低下;如果使用特定的方式计算大量数据,对性能的影响更甚。此时我们可以使用生成器,及时计算并产出后续值,不占用宝贵的内存资源。

 

简单补充迭代器

很多小伙伴不知道迭代器是什么,也就是Itereaor接口是什么。

PHP官方简介:可在内部迭代自己的外部迭代器或类的接口。(太抽象,看不懂?)

个人理解:高级foreach循环,能够让对象像数组一样循环。

其实foreach也能循环对象,区别在于:如果你的类并实现了Iterator接口,那么你的这个类对象就是ZEND_ITER_OBJECT,否则就是ZEND_ITER_PLAIN_OBJECT。对于ZEND_ITER_PLAIN_OBJECT的类,foreach会通过HASH_OF获取该对象的默认属性数组,然后对该数组进行foreach,而对于ZEND_ITER_OBJECT的类对象,则会通过调用对象实现的Iterator接口相关函数来进行foreach。

看不懂没关系,基本你也不会去用,哈哈哈,这里只是想说明一个问题,不管你使用foreach还是自己创建的迭代器,你要循环的数据是预先生成的,这个数据会加载到内存,提供给迭代器循环输出,那么这个数据如果超过最大内容限制,会提示内存不足,当然你可以使用 ini_set('memory_limit', '内存大小');来临时改变内存,但是如果还是不够怎么办,例如:我现在有个日志文件,大小是5G,服务器的内存2G,你全开都不够啊,哈哈哈哈哈,怎么办呢?生成器就能给我解决这个问题,哈哈哈,大部分我使用生成器也是读文件在用。

注意:PHP生成器不能满足所有迭代操作的需求,因为如果不查询,生成器永远不知道下一个要迭代的值是什么,这里提到一个开发中常遇到的问题来举例:数据库操作,有时候对大批量数据进行处理,如果把数据全部查询出来,很多小伙伴会遇到内存溢出,那么多数据保存在内存中,肯定不足啊,这里的解决方案就是分块拿,每次拿1000,用完再拿1000,这个解决方案在各个框架都有,例如laravel中等chunk(),tp5中也是chunk(),分块拿,小伙伴可以定位到这个chunk(),里面是do{}while{}循环在处理,那么问题来了,能否用今天我们探究的生成器来解决呢?答案是no,至于为什么,因为生成器永远不知道下一个要迭代的值是什么,这里对数据库的操作,我们只能采取数据拿出才能解决,难道你打算每次读取一条,那么这样数据库资源更大,不建议。

创建生成器

生成器的创建很简单,只是一个PHP函数,使用yield关键字。与普通的PHP函数不同的是,生成器从不返回值,只产出值,和return是有区别的。

<?php

function myGenerator() {

    yield 'test1';

}

定义生成器是很容易的,就和定义函数一样,我们函数使用return 返回,换成yield 直接产值就好,简单吧,最主要我们来看看生成器常见的使用吧。

使用生成器

下面的代码是我常使用生成器的案例,欢迎在其他地方也能使用生成器的小伙伴反馈,共同成长。

<?php

// 定义一个生成器
function readeFile($file)
{
    $handle = fopen($file, 'r');// 只读打开文件得到文件资源
    if ($handle == false) { // 打开失败抛出异常
        throw new Exception();
    }

    while (feof($handle) === false) {// 判断是否到文件最后
        yield fgets($handle); // 读取一行
    }
    fclose($handle);// 关闭文件资源文件
}

$file = 'text.txt';

// 逐行读取
foreach (readeFile($file) as $row) {
    print_r($row);
}

当text.txt文件大于你的内存时 也不会出问题哟!

 

闭包

闭包和匿名函数是PHP5.3.0中引入,这两个特性是仅次于命名空间使用最多的吧,或多或少,你在开发中肯定使用了的,这两个特性很有必要去掌握(好用+使用频率超高,上面提到的生成器不用也没事儿,但是这两个特性不能错过)。

闭包是指在创建时封装周围状态的函数。

匿名函数是没有名称的函数。

理论上闭包和匿名函数两个不同的函数,但是在PHP中其实一样,虽然闭包和匿名函数的使用和普通函数相同,但是闭包和匿名函数是对象,你没有看错,他们是Closure类的实例,伪装成函数的对象,哈哈哈哈哈,吓到了没

 

创建闭包

先来看简单的闭包使用:

<?php

$closure = function ($name) {
    return "hello " . $name;
};

echo $closure('phper');

输出结果是:hello phper

 

我们使用闭包最常见的是结合PHP自带的一些函数,会用到回调函数,比如array_map(),preg_replace_callback(),直接看写法,比较简单:

<?php

// 写法一
// 匿名函数直接放在参数位置
$res1 = array_map(function ($value) {
    return $value + 1;
}, [1, 2, 3]);

var_dump($res1);

// 写法二
// 定义函数
function doMap($value)
{
    return $value + 2;
}
// 函数名当参数
$res2 = array_map('doMap', [1, 2, 3]);
var_dump($res2); 


输出结果分别是:

array(3) {
  [0]=>
  int(2)
  [1]=>
  int(3)
  [2]=>
  int(4)
}

array(3) {
  [0]=>
  int(3)
  [1]=>
  int(4)
  [2]=>
  int(5)
}


还有一些PHP自带函数使用回调函数,欢迎小伙伴们留言整理,哪些函数会用到匿名函数,我先举例几个吧:

array_map(),preg_replace_callback(),array_diff_uassoc(),array_intersect_uassoc() 我只选了几个数组相关的函数,还有很多,欢迎小伙伴们留言,请顺带解释下函数的用法,方便大家使用嘛,哈哈哈

那么问题又来了,上面讲的都是PHP自带的函数使用匿名函数来处理,如何自己来写或者说开发中我们怎么自己来写,那就让我们来提升下吧,用上面提及到的 laravel中等chunk() 这个方法来举例,如何使用,只是简单的模拟,开发中使用根据自身场景!

<?php


class Test
{
    // 模拟数据库只有4页数据
    private $page = 4;

    // 模拟数据库中4页数据
    private $dataBase = [
        1 => [['name' => 'test1', 'age' => 12], ['name' => 'test2', 'age' => 8]],
        2 => [['name' => 'test3', 'age' => 13], ['name' => 'test4', 'age' => 15]],
        3 => [['name' => 'test5', 'age' => 4], ['name' => 'test6', 'age' => 23]],
        4 => [['name' => 'test7', 'age' => 5], ['name' => 'test8', 'age' => 12]],
    ];


    // 获取结果
    public function getResult($page)
    {
        $dataBase = $this->dataBase;
        return $dataBase[$page] ?? [];
    }

    // 模拟分块处理数据
    public function chunk($page, callable $callback)
    {
        do {
            $result = $this->getResult($page);

            if (!$result) break;

            $callback($result);

            $page++;
        } while ($page <= $this->page);

        return true;
    }
}

$test = new Test();

// 从第几页开始取
$test->chunk(1, function ($data) {

    foreach ($data as $k => $v) {
        $str = 'name: ' . $v['name'] . '-' . 'age: ' . $v['age'] . "\n";
        // 直接输出
        echo $str;
        // 或者写入文件
        //file_put_contents('text.txt', $str, FILE_APPEND);
        // 想怎么处理 你自己看着办吧 哈哈哈哈哈
    }
});

使用相当简单吧,合理使用闭包的写法,会提升代码质量

因为闭包的东西比较多,下篇文章我们将继续深入学习哟!

附上代码demo: https://github.com/lirui310/demo-generator

 

后续会继续更新,感兴趣的小伙伴可以收藏下哟!

欢迎有问题或者项目开发有问题的小伙伴(妹子优先)添加微信:wxmm686800,共同成长!不好意思可以邮箱联系:lirui310@aliyun.com

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值