1.9 - Laravel - 5.6 - tag 解析機制

HarveyNorman發表於2020-03-28

先說作用,給某一類的繫結分配一個標記來表示他們是一類的,可以通過這個標記取出同一類下面的所有的繫結。
看看怎麼用就知道了:

//定義兩個實體類, 都標記名字為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 協議》,轉載必須註明作者和本文連結

相關文章