1.9 - Laravel - 5.6 - tag 解析机制

先说作用,给某一类的绑定分配一个标记来表示他们是一类的,可以通过这个标记取出同一类下面的所有的绑定。
看看怎么用就知道了:

//定义两个实体类, 都标记名字为currency
//然后使用tagged取出currency就是可以取出abstruct的实体对象。
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);

}

注意这里:我们绑定的时候还是一个abstruct,就是说容器中还没有对象,但是我们tagged取出的时候都变成对象了。因为make可以直接解析类路径,而tagged使用了make方法,我们看下面源代码。


1.tag 方法源代码

1.0 还是先说参数,传入的$abstracts可以是类路径,也可以是一个绑定的别名或者字符串。如果是后者,需要事先使用bind方法绑定对应的concrete。如果是类路径则可以直接使用,参见前面make方法。

源代码:

public function tag($abstracts, $tags)
    {
        $tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1);

        foreach ($tags as $tag) {
            if (! isset($this->tags[$tag])) {
                $this->tags[$tag] = [];
            }

            foreach ((array) $abstracts as $abstract) {
                $this->tags[$tag][] = $abstract;
            }
        }
    }

1.1 判断第二个参数$tags是不是一个数组,如果不是数组,他可以是一个参数。
这里可以看到,这里也可以传入多个参数。array_slice会吧第二个参数,以及后面所有的参数都转换成一个数组返回。

$tags = is_array($tags) ? $tags : array_slice(func_get_args(), 1);

什么意思呢,举例:

//可以这样
$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,他会强行转换成一个数组,说明$abstruct可以是一个字符串,也可以是一个数组。把当前的abstruct对应的值存储到上面我们创建的子数组tag中。
这里我们同时知道了tags数组作用以及存储格式。

foreach ($tags as $tag) {
    if (! isset($this->tags[$tag])) {
        $this->tags[$tag] = [];
    }

    foreach ((array) $abstracts as $abstract) {
        $this->tags[$tag][] = $abstract;
    }
}

这是tags的存储过程。


2.tagged方法源代码,看看以tag标记的一类绑定如何获取

public function tagged($tag)
    {
        $results = [];

        if (isset($this->tags[$tag])) {
            foreach ($this->tags[$tag] as $abstract) {
                $results[] = $this->make($abstract);
            }
        }

        return $results;
    }

2.1 其实很简单,也是两个逻辑:
a.先判断当前容器的 tags 数组中有没有对应的值,如果没有直接返回空数组
b.如果有,遍历这个子tag数组,分别使用make函数解析这个$abstruct

这里我们就能明白,为什么我们可以不用事先绑定类路径,而直接使用就能绑定成功,因为我们知道make解析的时候,如果在binding数组中找不到对应的值,他会使用build函数直接解析。
但是我们,也看到,这个make解析的时候是没有第二个参数parameters的,说明什么呢,如果这个类路径有自定义的依赖并且这个依赖没有默认值,它是无法实例化的。因为make他需要我们传入第二个参数才能实例化

实例测试:

  1. 测试提供类:
    class 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.测试多个参数情况,以及直接使用类路径

public function testTags()
{
$this->app->bind(‘rmb’, Rmb::class);

//多个参数
$this->app->tag('rmb', 'currency', 'money');
//直接使用类路径
$this->app->tag(Dollar::class, 'currency', 'money');

$currencyArray = $this->app->tagged('money');
$this->assertTrue($currencyArray[0] instanceof Rmb);
$this->assertTrue($currencyArray[1] instanceof Dollar);

}


2.测试使用tag直接绑定有依赖的类路径,会报错

public function testTagsWithDependency()
{
$this->app->tag(AusDollars::class, ‘currency’, ‘money’);
try{
$this->app->tagged(‘money’);
}catch(\Exception $e){
$this->assertContains(“Unresolvable dependency resolving”,$e->getMessage());
}
}

会报这样的错
`Unresolvable dependency resolving [Parameter #0 [ <required> $amount ]] in class ...`
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 1

能不分析源码诶。。。

3年前 评论
HarveyNorman (楼主) 3年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!