思考并回答以下问题:
简介
先说作用,给某一类的绑定分配一个标记来表示他们是一类的,可以通过这个标记取出同一类下面的所有的绑定。
看看怎么用就知道了:1
2
3
4
5
6
7
8
9
10
11
12// 定义两个实体类,都标记名字为currency
// 然后使用tagged取出currency就是可以取出abstract的实体对象。
public function testTags()
{
$this->app->tag(Rmb::class, 'currency');
$this->app->tag(Dollar::class, 'currency');
$currencyArray = $this->app->tagged('currency');
$this->assertTrue($currencyArray[0] instanceof Rmb);
$this->assertTrue($currencyArray[1] instanceof Dollar);
}
注意这里:我们绑定的时候还是一个abstract,就是说容器中还没有对象,但是我们tagged取出的时候都变成对象了。因为make可以直接解析类路径,而tagged使用了make方法。
实例测试
测试提供类: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
41class AusDollars
{
private $amount;
public function __construct($amount)
{
$this->amount = $amount;
}
}
class Rmb
{
}
class Dollar
{
public function __construct()
{
}
public function getAmount()
{
return 1;
}
}
Class Currency
{
private $dollar;
public function __construct(Dollar $dollar)
{
$this->dollar = $dollar;
}
public function getAmount()
{
return $this->dollar->getAmount();
}
}
1.测试多个参数情况,以及直接使用类路径。
1 | public function testTags() |
2.测试使用tag直接绑定有依赖的类路径,会报错。
1 | public function testTagsWithDependency() |
会报这样的错:1
`Unresolvable dependency resolving [Parameter #0 [ <required> $amount ]] in class ...`
源码
1.tag方法源代码
1 | /** |
1.0 还是先说参数,传入的$abstracts可以是类路径,也可以是一个绑定的别名或者字符串。如果是后者,需要事先使用bind方法绑定对应的concrete。如果是类路径则可以直接使用,参见前面make方法。
1.1 判断第二个参数$tags是不是一个数组,如果不是数组,他可以是一个参数。
这里可以看到,这里也可以传入多个参数。array_slice会把第二个参数,以及后面所有的参数都转换成一个数组返回。1
$tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1);
什么意思呢,举例:1
2
3
4
5
6// 可以这样
$this->app->tag(Rmb::class, 'currency');
// 也可以传入数组
$this->app->tag(Rmb::class, ['currency', 'money']);
// 也可以这样
$this->app->tag(Rmb::class, 'currency',‘money’);
最后一种,我们看源码就知道可以这样使用,虽然我感觉可能不是一个好的写法。
1.2 下面就简单了,主要分两步:
- a.遍历这个tags数组,如果在tags数组(protected $tags = [];)中不存在这个tag那么创建一个空的子数组。
- b.遍历前面的第一个参数$abstracts,他会强行转换成一个数组,说明$abstract可以是一个字符串,也可以是一个数组。把当前的abstract对应的值存储到上面我们创建的子数组tag中。
这里我们同时知道了tags数组作用以及存储格式。1
2
3
4
5
6
7
8
9
10
11
12foreach ($tags as $tag)
{
if (! isset($this->tags[$tag]))
{
$this->tags[$tag] = [];
}
foreach ((array) $abstracts as $abstract)
{
$this->tags[$tag][] = $abstract;
}
}
这是tags的存储过程。
2.tagged方法源代码,看看以tag标记的一类绑定如何获取。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20/**
* Resolve all of the bindings for a given tag.
*
* @param string $tag
* @return iterable
*/
public function tagged($tag)
{
if (! isset($this->tags[$tag]))
{
return [];
}
return new RewindableGenerator(function () use ($tag) {
foreach ($this->tags[$tag] as $abstract)
{
yield $this->make($abstract);
}
}, count($this->tags[$tag]));
}
2.1 其实很简单,也是两个逻辑:
- a.先判断当前容器的tags数组中有没有对应的值,如果没有直接返回空数组。
- b.如果有,遍历这个子tag数组,分别使用make函数解析这个$abstract。
这里我们就能明白,为什么我们可以不用事先绑定类路径,而直接使用就能绑定成功,因为我们知道make解析的时候,如果在binding数组中找不到对应的值,他会使用build函数直接解析。
但是我们,也看到,这个make解析的时候是没有第二个参数parameters的,说明什么呢,如果这个类路径有自定义的依赖并且这个依赖没有默认值,它是无法实例化的。因为make他需要我们传入第二个参数才能实例化。