思考并回答以下问题:
简介
extend方法可以修改解析的服务。例如,当一个服务被解析后,你可以添加额外的代码去修饰或配置这个服务。extend方法接受一个闭包,闭包的唯一参数和返回值都是一个服务:
1 | $this->app->extend(Service::class, function($service) { |
extend主要的作用是在解析后,使用一个闭包函数产生的值(通常为当前实体类的子类)替换对应父实体类,从而对其产生扩展影响。这个闭包的参数和返回值都必须是对象。比如:1
2
3app()->extend('Service', function ($service, $app) {
return new DecoratedService($service);
});
这里DecoratedService是Service的子类。
更简单来说,当我们从Container容器中取出一个实例后,用这个类的子类实例替换掉当前这个父类实例,达到扩展的作用。
当然这是一种用法。还有一些细节,我们去看下源代码。
实测实例
实例1:替换依赖的实例对象
本来的Boss实例的依赖Money是一个Cheque对象,最后解析的时候,被替换成了Dollar对象。
1 | interface Money |
实例2:子类替换父类,向下拓展1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33class Money
{
public function getAmount(){
return 100;
}
}
class Dollar extends Money
{
public function getAmount()
{
return 1;
}
}
class Cheque extends Money
{
public function getAmount()
{
return 100000;
}
}
public function testClosure()
{
app()->extend(Money::class, function() {
return new Dollar();
});
$money= app()->make(Money::class);
$output = $money->getAmount();
$this->assertEquals($output, 1);
}
实例3:向其他地方拓展,extend的第二个参数是闭包,闭包的第一个参数就是extend第一个参数的实例,在这里就是我们事先绑定的Money::class实例。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50class Money
{
public function getAmount(){
return 100;
}
}
class Dollar extends Money
{
public function getAmount()
{
return 1;
}
}
class Cheque extends Money
{
public function getAmount()
{
return 100000;
}
}
class Currenty
{
protected $money;
public function __construct(Money $money)
{
$this->money = $money;
}
public function getAmount()
{
return "harveynorman";
}
}
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");
}
源码
1 | /** |
1、先获取Container中这个id($abstract)的别名。
2、查看容器已有的实例数组instance里面有没有对应的实例,如果有,直接执行我们extend的这个闭包方法,返回值存入这个数组中,就是替换了原来的实例。
2.2.然后使用了rebound(),目的是看看有没有附带的回调函数,触发它,这个我们在回调函数中会提。
这说明extend的时候会触发回调函数。
3.如果instance中没有找到对应的实例,就把这个闭包函数存入extenders数组,做个记录,以后用。
并且如果这个id已经被resolved过了,还要触发rebound函数,触发一些对应的回调函数。
总结
extend的机制就是如果instance中存在实例,就用extend中的闭包执行结果替换掉。如果instances实例列表中不存在就存起来以后备用。
某个适用场景可以是:用子类实例替换父类实例达到扩展的作用。
但不管怎么样,都会触发当前存在的回调函数一次。下一章讲一下回调函数。