1.2 - Laravel 5.6 - Extend 扩展机制
扩展机制也挺重要。先看下文档的解释
extend 方法可以修改解析的服务。例如,当一个服务被解析后,你可以添加额外的代码去修饰或配置这个服务。 extend 方法接受一个闭包,闭包的唯一参数和返回值都是一个服务:
$this->app->extend(Service::class, function($service) {
return new DecoratedService($service);
});
不明白?我也不明白。我用我的话讲一下。
extend主要的作用是在解析后,使用一个闭包函数产生的值(通常为当前实体类的子类)替换对应父实体类,从而对其产生扩展影响。这个闭包的参数和返回值都必须是对象。比如:
app()->extend('service', function ($service, $app) {
return new DecoratedService($service);
});
这里DecoratedService 是 service 的子类。
更简单来说,当我们从Container容器中取出一个实例后,用这个类的子类实例替换掉当前这个父类实例,达到扩展的作用。
当然这是一种用法。还有一些细节,我们去看下源代码。
我们看下源代码:
1.先获取Container中这个id($abstract)的别名
2.查看容器已有的实例数组instance里面有没有对应的实例,如果有直接执行我们extend的这个闭包方法的返回值,存入这个数组中。就是替换了原来的实例。
2.2.然后使用了rebound(),目的是看看有没有附带的回调函数,触发它,这个我们在回调函数中会提。
这说明extend的时候会触发回调函数。
3.如果instance中没有找到对应的实例,就把这个闭包函数存入extenders数组,做个记录,以后用。
并且如果这个id已经被resolved过了,还要触发rebound函数,触发一些对应的回调函数。
public function extend($abstract, Closure $closure)
{
$abstract = $this->getAlias($abstract);
if (isset($this->instances[$abstract])) {
$this->instances[$abstract] = $closure($this->instances[$abstract], $this);
$this->rebound($abstract);
} else {
$this->extenders[$abstract][] = $closure;
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
}
}
现在就很清楚了。
总结
extend的机制就是如果instance中存在实例,就用extend中的闭包执行结果替换掉。如果instances实例列表中不存在就存起来以后备用。
某个适用场景可以是:用子类实例替换父类实例达到扩展的作用。
但不管怎么样,都会触发当前存在的回调函数一次。下一章讲一下回调函数。
实测实例
实例1:替换依赖的实例对象
本来的Boss实例的依赖Money是一个Cheque对象,最后解析的时候,被替换成了Dollar对象。
public function testClosure(){
app()->bind('boss', Boss::class);
$this->app->when(Boss::class)
->needs(Money::class)
->give(Cheque::class);
app()->extend(Money::class, function() {
return new Dollar();
});
$boss= app()->make('boss');
$output = $boss->getA();
$this->assertEquals($output, 1);
}
//
interface Money
{
public function getAmount();
}
class Dollar implements Money
{
public function getAmount()
{
return 1;
}
}
class Cheque implements Money
{
public function getAmount()
{
return 100000;
}
}
class Worker
{
private $money;
public function __construct(Money $money)
{
$this->money = $money; // prints '1'
}
public function getA(){
return $this->money->getAmount();
}
}
class Boss
{
private $money;
public function __construct(Money $money)
{
$this->money = $money; // prints '100000'
}
public function getA(){
return $this->money->getAmount();
}
}
实例2:子类替换父类,向下拓展
public function testClosure()
{
app()->extend(Money::class, function() {
return new Dollar();
});
$money= app()->make(Money::class);
$output = $money->getAmount();
$this->assertEquals($output, 1);
}
class Money
{
public function getAmount(){
return 100;
}
}
class Dollar extends Money
{
public function getAmount()
{
return 1;
}
}
class Cheque extends Money
{
public function getAmount()
{
return 100000;
}
}
实例3:向其他地方拓展, extend第二个参数闭包的第一个参数就是 extend第一个参数的实例,在这里就是我们事先绑定的Money::class实例。
public function testClosure()
{
app()->bind('money', Money::class);
app()->extend('money', function($money) {
return new Currenty($money);
});
$boss= app()->make('money');
$output = $boss->getAmount();
$this->assertEquals($output, "harveynorman");
}
class Currenty{
protected $money;
public function __construct(Money $money)
{
$this->money = $money;
}
public function getAmount(){
return "harveynorman";
}
}
class Money
{
public function getAmount(){
return 100;
}
}
class Dollar extends Money
{
public function getAmount()
{
return 1;
}
}
class Cheque extends Money
{
public function getAmount()
{
return 100000;
}
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
大佬你这些文章get到点了 ,感谢下。