导语
类似于go语言的方法的接收者,js中的apply,bind,call改变函数对象的调用者,以及其它语言的上下文context。php也可以给函数指定this,即通俗意义上的改变其宿主。而bindTo就是这一作用,(注:php中函数都是closure实例,将函数绑定到实例对象上的用bindTo方法)
例1:
使用bindTo 给目标类添加执行初始条件(注册)
class A{
private $name='Hi';
public function test()
{
echo 'name: '.$this->name, 'age: '.$this->age,"\n";
}
}
// 1.准备匿名函数
$fn = function(){
return $this->age = 18;
};
// 2.准备被操作的实例
$o = new A;
// 3.将闭包绑定到实例
$new_fn = $fn->bindTo($o,$o);
// 4.执行闭包匿名函数,给对象实例动态添加属性(作初始化工作)
$new_fn();
// 执行实例方法
$o->test();
小结:很显然,如果缺少闭包添加某些生产脚本,实例的相关方法执行难以为继 。实质:将注册(register)与启动(boot)分离。注册仅仅只是一个映射,描述某种关系。而启动则是初始化。故通常顺序是 注册->启动->使用。
例2:
需求:给目标类动态添加方法
//
// 使用trait
trait MetaTrait{
private $methods = array();
// 对外接口
public function addMethod($methodName, $methodCallable)
{
if (!is_callable($methodCallable)) {
throw new InvalidArgumentException('第二个参数必须是一个可调用类型');
}
// 新的闭包绑定到当前对象(生产脚本,即改变this的指向,其方法可被动态访问,指定类型范围),
$this->methods[$methodName] = Closure::bind($methodCallable, $this, get_class());
}
public function __call($methodName, array $args)
{
//调用绑定的闭包
if (isset($this->methods[$methodName])) {
return call_user_func_array($this->methods[$methodName], $args);
}
throw RunTimeException('没有该方法');
}
}
// 实现
class Foo{
use MetaTrait;
private $dayOfWeek = 'Thursday';
}
//1.获取实例
$test = new Foo();
// 绑定方法
$test->addMethod('when', function () {
return $this->dayOfWeek;
});
//调用对象方法
echo $test->when(); //Thursday
小结:
1.依赖于被添加的类实例 类似于装饰器设计
2.绑定的this指向,Closure 形成新绑定闭包
3.闭包调用是函数式
laravel集合类源码分析
在类的调用时,比如集合
use Illuminate\Support\Str;
Collection::macro('toUpper', function () {
return $this->map(function ($value) {
return Str::upper($value);
});
});
$collection = collect(['first', 'second']);
$upper = $collection->toUpper();
// ['FIRST', 'SECOND']
::macro 宏添加指令 给集合类某个属性数组添加闭包,在后续实例调用->toUpper() ,会优先调用__call(内部检查该闭包数组)相应闭包方法。