思考并回答以下问题:
- $new_closure = Closure::bind($closure, new ClassName(), ClassName::class);和$new_closure = $closure->bindTo(new ClassName(), ClassName::class);
PHP闭包中的绑定bindTo和bind
1 | final class Closure { |
fromCallable等价于
1 | $reflexion = new ReflectionFunction('addDiscount'); |
bindTo和bind函数的功能相同,一个是静态调用,一个实例调用,请看下面的示例:
创建一个匿名函数
1 | $clo = function ($name) { |
结果为:1
2
3
4
5
6
7object(Closure)#1 (1) {
["parameter"]=>
array(1) {
["$name"]=>
string(10) "<required>"
}
}
由此可见$clo为Closure类的实例对象。所以Closure类是不能使用通过构造函数来实例化,也没这个必要,所以构造函数被定义为私有的。
所以上面说的实例调用是指$clo->bindTo($newthis, $newscope)
所以上面说的静态调用是指Closure::bind($clo, $newthis, $newscope)都能够得到一个新的闭包。
那么,它的使用场景是什么?
有时候我们的在闭包内部想要访问某个类或者对象的属性和方法,但是它们是由访问修饰符控制的,也就存在作用于的问题。
并且想要使用$this来访问的话,必须是在对象的内部,鉴于这些问题,我们有必要来操作一下闭包,使其具有这些能力。
- 1、具有“本类内部”同等效果的作用域
- 2、将 $this 传递到闭包里面,绑定到具体的实例,可使用$this访问。
看例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class 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();
打印:
1 | object(Closure)#2 (1) { |
由于$fun是定义在类的内部,于是它已经具备了这两个能力,这叫自动绑定。如果不想被自动绑定$this,可以使用静态闭包
1 | $fun = static function (){ |
但是”本类内部“的功能还在。
但是对于一个外来的闭包,是不具备这些能力的。
$fun2不在类里面定义。
1 | $fun2 = function (){ |
好,我们来使用bind()方法。
1 | $fun22 = Closure::bind($fun2, new Test(), Test::class); |
打印:
1 | object(Closure)#3 (1) { |
如果只想绑定$this的话,PHP7.0以后可以直接使用call,绑定并调用。
1 | $fun2->call(new Test()); |
和上面的结果一模一样,这就是bind和bindTo的作用。当然根据需要也可以只赋予某一种能力。
newthis
需要绑定到匿名函数的对象,或者NULL创建未绑定的闭包。
newscope
想要绑定给闭包的类作用域,或者‘static’表示不改变。如果传入一个对象,则使用这个对象的类型名。 类作用域用来决定在闭包中$this对象的 私有、保护方法的可见性。
这种操作一般用在框架里面,来扩展类或者实例的功能。
比如Laravel框架中的Illuminate\Support\Traits\Macroable宏。
Laravel提供的Macroable可以在不改变类结构的情况为其扩展功能
Macroable
我们以Illuminate\Support\Traits\Macroable为例分析。
通过trait可以很方便的在任何类中使用。
Laravel提供的Macroable可以在不改变类结构的情况下为其扩展功能。也就是为一个类动态注入一些方法,并且和该类本身的方法拥有同样的作用域和调用方式。
Macroable的核心是基于匿名函数的绑定功能
1 | trait Macroable |
我们可以看到执行绑定是在__callStatic和__call里面,如果一个类想要借助Macroable来提升能力,基本操作是:
1、use Macroable;
2、调用macro添加一个方法到$macros
macro可以添加匿名函数和对象,之所能通过调用匿名函数的方式调用对象,前提是该对象要实现__invoke()方法,也就是说,调用这个对象的入口是__invoke() 方法。
例如:
1 |
|
或者
1 | Foo::macro('join', function(...$string){ |
3、或者调用mixin将一个对象包含的全部方法都注册到当前类中。
使用了反射类获取到public和protected的方法,并且将protected方法设置为可访问,最后调用了这些方法,将返回值注入到 $macros;我们知道注册进去的都是可以被当作闭包调用的,因此我们反射的这个对象里面的方法的返回值应该是闭包,例如:
1 | final class Str |
为了更便捷的扩展一个类的功能,macro,mixin,hasMacro三个方法都被设计为静态调用,可以通过类直接调用来扩展方法。