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 可以在不改变类结构的情况为其扩展功能,