php特性之闭包

闭包

闭包和匿名函数在PHP 5.3.0中引入。

闭包是指在创建时封装周围状态的函数。即便闭包所在的环境不存在了,闭包中封装的状态依然存在。

匿名函数其实就是没有名称的函数。匿名函数可以赋值给变量,还能像其他任何php对象那样传递。不过匿名函数仍然是函数,因此可以调用,还可以传入参数。匿名函数特别适合作为函数或方法的回调。

注意: 理论上讲,闭包和匿名函数是不同的概念。不过,php将其视作相同概念。所以,提到闭包时,指的也是匿名函数;反之亦然。

PHP闭包和匿名函数使用的句法与普通函数相同,不过别被这一点迷惑了,闭包和匿名函数其实是伪装成函数的对象。如果审查PHP闭包和匿名函数,会发现它们是Closure类的实例。闭包和字符串或整数一样,也是一等值类型。

创建闭包

<?php
$closure = function ($name) {
    return sprintf('Hello %s', $name);
};
echo $closure("Josh");
// 输出--> Hello Josh

闭包和普通的PHP函数很像:使用的句法相同,也接受参数,而且能返回值。不过,匿名函数没有名称。
建议: 我们之所以能调用$closure变量,是因为这个变量的值是一个闭包,而且闭包对象实现了__invoke()魔术方法。只要变量名后有(),PHP就会查找并调用__invoke()方法。

很多PHP函数都会用到回调函数,例如array_map()和preg_replace_callback()。这是使用PHP匿名函数的绝佳时机!记住,闭包和其他值一样,可以作为参数传入其他PHP函数。

<?php
$numbersPlusOne = array_map(function ($number) {
    return $number + 1;
}, [1,2,3]);
print_r($numbersPlusOne); // [2,3,4]

在闭包出现之前,PHP开发者别无选择,只能单独创建具名函数,然后使用名称引用那个函数。这么做,代码执行得稍微慢一点,而且把回调的实现和使用场所隔离开了。

<?php
// 实现具名函数
function incrementNumber ($number) {
    return $number + 1;
}

// 使用具名函数
$numbersPlusOne = array_map('incrementNumber', [1,2,3]);
print_r($numbersPlusOne);

这样的代码虽然可用,但是如果只需使用一次回调,没必要单独定义具名函数。把闭包当成回调使用,写出的代码更简洁、更清晰。

附加状态

前面如何把匿名函数当成回调使用,下面讨论如何为PHP闭包附加并封装状态。JavaScript开发者可能对PHP的闭包感到奇怪,因为PHP闭包不会像真正的JavaScript闭包那样自动封装应用的状态。在PHP中必须手动调用闭包对象的bindTo()方法或者使用use关键字,把状态附加到PHP闭包上。

<?php
function  enclosePerson ($name) {
    return function ($doCommand) use ($name) {
        return sprintf('%s, %s', $name, $doCommand);
    };
}

// 把字符串"Clay"封装在闭包中
$clay = enclosePerson('Clay');

// 传入参数,调用闭包
echo $clay('get me sweet tea!');
// 输出 --> "Clay, get me sweet tea!"

具名函数enclosePerson()有个名$name的参数,这个函数返回一个闭包对象,而且这个闭包封装了$name参数。即便返回的闭包对象跳出了enclosePerson()函数的作用域,它也会记住$name参数的值,因为$name参数的值,因为$name变量仍在闭包中。

建议: 使用use关键字可以把多个参数传入闭包,此时要像PHP函数或者方法的参数一样,使用逗号分隔多个参数

别忘了,PHP闭包是对象。与任何其他PHP对象类似,每个闭包实例都可以使用$this关键字获取闭包内部状态。闭包对象的默认状态没什么用,不过有一个__invoke()魔术方法和bindTo()方法。仅此而已。

但是,bindTo()方法为闭包增加了一些有趣的潜力。我们可以使用这个方法把Closure对象的内部状态绑定到其他对象。bindTo()方法的第二个参数很重要,其作用是指定绑定闭包的那个对象所属的PHP类。因此,闭包可以访问绑定闭包的对象中受保护和私有的成员变量。

你会发现,PHP框架经常使用bindTo()方法把路由URL映射到匿名回调函数上。框架会把匿名函数绑定到应用对象上,这么做可以在这个匿名函数中使用$this关键字引用重要的应用对象

示例:

<?php
class App
{
    protected $routes = array();
    protected $responseStatus = '200 OK';
    protected $responseContentType = 'text/html';
    protected $responseBody = 'Hello world';

    public function addRoute($routePath,$routeCallback){
        $this->routes[$routePath] = $routeCallback->bindTo($this,__CLASS__);
    }

    public function dispatch($currentPath){
        foreach($this->routes as $routePath => $callback){
            if($routePath == $currentPath){
                $callback();
            }
        }
        header('HTTP/1.1 ' . $this->responseStatus);
        header('Content-type: ' . $this->responseContentType);
        header('Content-length: ' . mb_strlen($this->responseBody));
        echo $this->responseBody;
    }


}
// 我们要特别注意addRoute()方法。这个方法的参数分别是一个路由路径(例如/users/josh)和一个路由回调。
dispatch()方法的参数是当前HTTP请求的路径,它会调用匹配的路由回调。
第11行是重点所在,我们把路由回调绑定到了当前的App实例上。
这么做能在回调函数中处理App实例的状态

$app = new App();
$app->addRoute('/users/josh',function (){
    $this->responseContentType = 'application/json;charset=utf8';
    $this->responseBody = '{"name": "Josh"}';
});
$app->dispatch('/users/josh');

本篇博文主要摘自《Modern PHP》,便于以后查看

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值