1.5 - Laravel 5.6 - Alias 別名

HarveyNorman發表於2020-03-06

服務別名也相對重要,在很多地方使用。
通過服務繫結的別名,在解析服務的時候,跟不使用別名的效果一致。別名的作用也是為了同時支援全型別的服務繫結名稱以及簡短的服務繫結名稱考慮的。

通俗的講,假如我們想要建立 auth 服務,我們既可以這樣寫:

$this->app->make('auth')
又可以寫成:

$this->app->make('\Illuminate\Auth\AuthManager::class')
還可以寫成

$this->app->make('\Illuminate\Contracts\Auth\Factory::class')
後面兩個服務的名字都是 auth 的別名,使用別名和使用 auth 的效果是相同的。

這裡特別注意,誰是誰的別名


服務別名的獲取
我們看下原始碼:
1.看看有沒有對應的$abstruct的別名。如果沒有就返回當前$abstruct.
2.如果發現別名和$abstruct一樣,丟擲異常,別名不能和自己一樣。
3.進一步遞迴,檢視這個別名是否還有別名的別名。

public function getAlias($abstract)
{
    if (! isset($this->aliases[$abstract])) {
        return $abstract;
    }

    if ($this->aliases[$abstract] === $abstract) {
        throw new LogicException("[{$abstract}] is aliased to itself.");
    }

    return $this->getAlias($this->aliases[$abstract]);
}

服務別名的實現
那麼這些別名是如何載入到服務容器裡面的呢?
兩種方法:

  1. 通過呼叫Container中的alias方法,我們看下原始碼
    這裡有兩個陣列:aliases和abstractAliases,他們儲存一樣的資料,但是鍵值對相反。簡單說就是aliases的key值是abstractAliases的value值。下面有例項。
    把key和value分別儲存到對應的陣列中。

    public function alias($abstract, $alias)
    {
     $this->aliases[$alias] = $abstract;
     $this->abstractAliases[$abstract][] = $alias;
    }
  2. 外部配置引入。
    其實原理也是一樣,使用alias()方法,但是laravel在初始化的時候自己讀取配置檔案去存入對應陣列中。
    有兩個地方,一個是app.php檔案下的alias陣列中,還有一個是在laravel/framwork/src/Illuminate/Foundation/Application.php下的registerCoreContainerAliases方法,這個方法在Application初始化的時候回載入。Application是Container的子類,應用容器。後面再討論。
    看部分程式碼例項:

$aliases = [
  'app' => [\Illuminate\Foundation\Application::class, \Illuminate\Contracts\Container\Container::class, \Illuminate\Contracts\Foundation\Application::class],
  'auth' => [\Illuminate\Auth\AuthManager::class, \Illuminate\Contracts\Auth\Factory::class],
  'auth.driver' => [\Illuminate\Contracts\Auth\Guard::class],
  'blade.compiler' => [\Illuminate\View\Compilers\BladeCompiler::class],
  'cache' => [\Illuminate\Cache\CacheManager::class, \Illuminate\Contracts\Cache\Factory::class],
...
]

執行的registerCoreContainerAliases方法。我們可以看到還是呼叫了alias()方法

public function registerCoreContainerAliases()
{
  foreach ($aliases as $key => $aliases) {
    foreach ($aliases as $alias) {
      $this->alias($key, $alias);
    }
  }
}

載入後,服務容器的 aliases 和 abstractAliases 陣列例項:

$aliases = [
  'Illuminate\Foundation\Application' = "app"
  'Illuminate\Contracts\Container\Container' = "app"
  'Illuminate\Contracts\Foundation\Application' = "app"
  'Illuminate\Auth\AuthManager' = "auth"
  'Illuminate\Contracts\Auth\Factory' = "auth"
  'Illuminate\Contracts\Auth\Guard' = "auth.driver"
  'Illuminate\View\Compilers\BladeCompiler' = "blade.compiler"
  'Illuminate\Cache\CacheManager' = "cache"
  'Illuminate\Contracts\Cache\Factory' = "cache"
  ...
]
$abstractAliases = [
  app = {array} [3]
  0 = "Illuminate\Foundation\Application"
  1 = "Illuminate\Contracts\Container\Container"
  2 = "Illuminate\Contracts\Foundation\Application"
  auth = {array} [2]
  0 = "Illuminate\Auth\AuthManager"
  1 = "Illuminate\Contracts\Auth\Factory"
  auth.driver = {array} [1]
  0 = "Illuminate\Contracts\Auth\Guard"
  blade.compiler = {array} [1]
  0 = "Illuminate\View\Compilers\BladeCompiler"
  cache = {array} [2]
  0 = "Illuminate\Cache\CacheManager"
  1 = "Illuminate\Contracts\Cache\Factory"
  ...
]

我們在這裡還是要特別強調:
陣列$abstractAliases中,app是abstruct,後面跟著的三個陣列元素才是‘app’的別名(alias)

app = {array} [3]
  0 = "Illuminate\Foundation\Application"
  1 = "Illuminate\Contracts\Container\Container"
  2 = "Illuminate\Contracts\Foundation\Application"

這裡的這個結構需要指出的是,不管這個app別名有多少個,這些別名都指向一個abstruct,這個abstruct又會指向陣列binding,binding陣列是建立abstruct和concrete關係的陣列,而這個$abstractAliases和$aliases則是建立abstruc和alias之間關係的陣列。


總結
別名機制就是用一個別名來命名具體物件,類或者閉包函式。他在Facade中有運用到,以及在容器繫結和解析都有用到。我們只要記住,他儲存的兩個陣列名字:
1.abstractAliases
2.abstructs
我覺得他主要的作用可能就是讓程式碼簡介吧。


已測示例:
我們用別名alias_money重新命名了Money::class
別名先找到‘money’再去找到Money::class`

class ExampleTest extends TestCase
    public function testClosure()
    {
        app()->bind('money', Money::class);
        app()->alias('money','alias_money');

        $boss= app()->make('alias_money');

        $output = $boss->getAmount();
        $this->assertEquals($output, 100);
    }
}

class Money
{
    public function getAmount(){
        return 100;
    }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章