PHP闭包中的绑定bindTo和bind

final class Closure {
    // 用于禁止实例化
    private function __construct() { }

    // 复制一个闭包,绑定指定的$this对象和类作用域。这个方法是 Closure::bindTo() 的静态版本
    static function bind ( Closure $closure , object $newthis [, mixed $newscope = 'static' ] ) : Closure

    // 复制当前闭包对象,绑定指定的$this对象和类作用域。
    public function bindTo ( object $newthis [, mixed $newscope = 'static' ] ) : Closure

    // 当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
    public function __invoke ([ $... ] ) : mixed
	
	// Temporarily binds the closure to newthis, and calls it with any given parameters.
	// 一次性的,绑定$this,并可以传入参数,直接发起调用
	// @since 7.0
    function call ($newThis, ...$parameters) {}
	
	// 将给定的函数转化为匿名函数,可传入函数名
    public static function fromCallable (callable $callable) {}
}

fromCallable 等价于

$reflexion = new ReflectionFunction('addDiscount');
$closure = $reflexion->getClosure();

使用参考:https://www.php.net/manual/zh/closure.bind.php

bindTo和bind函数的功能相同,一个是静态调用,一个实例调用,请看下面的示例:

创建一个匿名函数

$clo = function ($name) {
    echo $name;
};

var_dump($clo);

结果为:

object(Closure)#1 (1) {
  ["parameter"]=>
  array(1) {
    ["$name"]=>
    string(10) "<required>"
  }
}

由此可见 $clo 为 Closure类 的实例对象。所以Closure类是不能使用通过构造函数来实例化,也没这个必要,所以构造函数被定义为私有的。

所以上面说的实例调用是指 $clo->bindTo($newthis, $newscope)
所以上面说的静态调用是指 Closure::bind($clo, $newthis, $newscope)
都能够得到一个新的闭包。

那么,它的使用场景是什么?

有时候我们的在闭包内部想要访问某个类或者对象的属性和方法,但是它们是由访问修饰符控制的,也就存在作用于的问题。关于修饰符的范围
可以参考:https://blog.csdn.net/raoxiaoya/article/details/103892440

并且想要使用$this来访问的话,必须是在对象的内部,鉴于这些问题,我们有必要来操作一下闭包,使其具有这些能力。
1、具有“本类内部”同等效果的作用域
2、将 $this 传递到闭包里面,绑定到具体的实例,可使用 $this 访问。

看例子:

class Test {
    public static $name = "rao";
    protected static $color = "red";
    private static $height = "188";

    public $age = 12;
    protected $sex = 1;
    private $weight = 100;

    function a(){
        $fun = function (){
            var_dump(Test::$name);
            var_dump(Test::$color);
            var_dump(Test::$height);
            var_dump($this->age);
            var_dump($this->sex);
            var_dump($this->weight);
        };

        var_dump($fun);
        $fun();
    }
}

(new Test())->a();

打印:

object(Closure)#2 (1) {
  ["this"]=>
  object(Test)#1 (3) {
    ["age"]=>
    int(12)
    ["sex":protected]=>
    int(1)
    ["weight":"Test":private]=>
    int(100)
  }
}
string(3) "rao"
string(3) "red"
string(3) "188"
int(12)
int(1)
int(100)

由于 $fun 是定义在类的内部,于是它已经具备了这两个能力,这叫自动绑定。如果不想被自动绑定 $this,
可以使用静态闭包

$fun = static function (){
    var_dump(Test::$name);
    var_dump(Test::$color);
    var_dump(Test::$height);
};

但是”本类内部“的功能还在。

但是对于一个外来的闭包,是不具备这些能力的。

$fun2 不在类里面定义。

$fun2 = function (){
    var_dump(Test::$name); // 可以访问
    var_dump(Test::$color); // 无法访问
    var_dump(Test::$height); // 无法访问
    var_dump($this->age); // 无法访问
    var_dump($this->sex); // 无法访问
    var_dump($this->weight); // 无法访问
};

好,我们来使用bind()方法。

$fun22 = Closure::bind($fun2, new Test(), Test::class);
// 或者 $fun22 = $fun2->bindTo(new Test(), Test::class);

var_dump($fun22);
$fun22();

打印:

object(Closure)#3 (1) {
  ["this"]=>
  object(Test)#2 (3) {
    ["age"]=>
    int(12)
    ["sex":protected]=>
    int(1)
    ["weight":"Test":private]=>
    int(100)
  }
}
string(3) "rao"
string(3) "red"
string(3) "188"
int(12)
int(1)
int(100)

如果只想绑定 $this 的话,PHP7.0 以后可以直接使用 call,绑定并调用。

$fun2->call(new Test());

和上面的结果一模一样,这就是 bind 和 bindTo 的作用。当然根据需要也可以只赋予某一种能力。
newthis
需要绑定到匿名函数的对象,或者 NULL 创建未绑定的闭包。

newscope
想要绑定给闭包的类作用域,或者 ‘static’ 表示不改变。如果传入一个对象,则使用这个对象的类型名。 类作用域用来决定在闭包中 $this 对象的 私有、保护方法 的可见性。

这种操作一般用在框架里面,来扩展类或者实例的功能。

比如 Laravel 框架中的 Illuminate\Support\Traits\Macroable 宏。
Laravel 提供的 Macroable 可以在不改变类结构的情况为其扩展功能,

发布了412 篇原创文章 · 获赞 25 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/raoxiaoya/article/details/103894397