PHP

PHP-闭包

1.定义

1). 闭包和匿名函数在 PHP5.3 中被引入。
2). 闭包是指在创建时封装函数周围状态的函数,即使闭包所在的环境不存在了,闭包封装的状态依然存在,这一点和 Javascript 的闭包特性很相似。可以看我之前写的彻底弄懂 Javascript 闭包
3). 匿名函数就是没有名称的函数,匿名函数可以赋值给变量,还可以像其他任何 PHP 对象一样传递。可以将匿名函数和闭包视作相同的概念。
4). 需要注意的是闭包使用的语法和普通函数相同,但是他其实是伪装成函数的对象,是 Closure 类的实例。闭包和字符串或整数一样,是一等值类型。
定义一个闭包函数,其实就是实例化一个闭包类(Closure)对象:

$func = function()
{
    echo 'hello world' . PHP_EOL;
};
var_dump($func);

输出:
object(Closure)#1 (0) {
}

类摘要:

Closure {
     __construct ( void )
     public static Closure bind ( Closure $closure , object $newthis [, mixed $newscope = 'static' ] )
     public Closure bindTo ( object $newthis [, mixed $newscope = 'static' ] )
}

2.应用

(1)创建闭包

$closure = function ($name) {
  return sprintf('hello %s', $name);
};
echo $closure('Josh');

(2)使用闭包

__invoke()魔术方法

PHP 自从 5.3 版以来就新增了一个叫做__invoke() 的魔术方法,使用该方法就可以在创建实例后,直接调用对象,我们之所以可以调用 $closure 变量,是因为这个变量的值是一个闭包,而且闭包对象实现了__invoke() 魔术方法,只要后面跟着 (),PHP 就会查找__invoke() 方法。这里简单解释下这个魔术方法:

class testClass
{
    public function __invoke
    {
        print "hello world";
    }
}
$n = new testClass;
$n();
作为回调函数

我们通常把 PHP 闭包当做函数和方法的回调使用。很多 PHP 函数都会用到回调函数,例如 array_map(),

$numbersPlusOne = array_map(function($number) {
    return $number + 1;
}, [1, 2, 3]);
bindTo()方法

在 PHP 中通过调用闭包对象的 bindTo 方法或者使用 use 关键字,把状态附加到 PHP 闭包上。来看一个例子

function enclosePerson($name)
{
    return function ($doCommand) use ($name) {
        return sprintf('%s , %s', $name, $doCommand);
   };
}
//把字符串“Clay”封装在闭包中
$clay = enclosePerson('Clay');
//传入参数,调用闭包
echo $clay('get me sweat tea!'); // Clay, get me sweat tea!

在这个例子中,函数 enclosePerson() 有一个 $name 参数,这个函数返回一个闭包对象,这个闭包封装了 $name 参数,即便返回的对象跳出了 enclosePerson() 函数的作用域,它也会记住 $name 参数的值,因为 $name 变量仍然在闭包中。
2). 使用 use 关键字可以把多个关键字传入闭包,此时要想像 PHP 函数或方法的参数一样,使用逗号分割多个参数。
3).PHP 闭包仍然是对象,可以使用 $this 关键字获取闭包的内部状态。闭包的默认状态里面有一个__invoke() 魔术方法和 bindTo() 方法。
4).bindTo() 方法为闭包增加了一些有趣的东西。我们可以使用这个方法把 Closure 对象内部状态绑定到其他对象上。bindTo() 方法的第二个参数可以指定绑定闭包的那个对象所属的 PHP 类,这样我们就可以访问这个类的受保护和私有的成员变量。看下面的代码示例:

class App
{
    protected $route = 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;
    }
}

我们把路由回调绑定到了当前的 App 实例上,这样就可以在回调函数中处理 App 实例的状态了,这里我们需要重点关注addRoute方法,这个方法的参数分别是一个路由路径和一个路由回调,dispatch方法的参数是当前HTTP请求的路径,它会调用匹配的路由回调。第9行是重点所在,我们将路由回调绑定到了当前的App实例上。这么做能够在回调函数中处理App实例的状态:

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

匿名函数可以从父作用域继承变量,而这个父作用域是定义该闭包的函数(不一定是调用它的函数)。利用这个特性,我们可以实现一个简单的控制反转IoC容器:

class Container
{
    protected static $bindings;
 
    public static function bind($abstract, Closure $concrete)
    {
        static::$bindings[$abstract] = $concrete;
    }
 
    public static function make($abstract)
    {
        return call_user_func(static::$bindings[$abstract]);
    }
}
 
class talk
{
    public function greet($target)
    {
        echo 'Hello ' . $target->getName();
    }
}

class A
{
    public function getName()
    {
        return 'World';
    }
}
 
// 创建一个talk类的实例
$talk = new talk();
 
// 将A类绑定至容器,命名为foo
Container::bind('foo', function() {
    return new A;
});
 
// 通过容器取出实例
$talk->greet(Container::make('foo')); // Hello World

上述例子中,只有在通过make方法获取实例的时候,实例才被创建,这样使得我们可以实现容器。在Laravel框架底层也大量使用了闭包以及bindTo方法,利用好闭包可以实现更多的高级特性如事件触发等。

转自:
IOC https://www.cnblogs.com/DebugLZQ/archive/2013/06/05/3107957.html
闭包 https://juejin.im/post/5aac88ab51882555627d0d37
php官方的闭包解释

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值