2.2 - Laravel - 5.6 - Facade - 關於 facade 載入

HarveyNorman發表於2020-07-15

larave在啟動的時候會預先載入facade 已經配置好的對映。以便在後面facade呼叫的時候使用。

啟動流程是這樣的

1.起始頁面 index.php 載入了
$app = require_once __DIR__.'/../bootstrap/app.php';

2.在bootstrap/app.php中載入

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    App\Http\Kernel::class
);

3.然後在 App\Http\Kernel::class父類中載入了

protected $bootstrappers = [
        ...
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        ...
    ];

4.這個RegisterFacades::class 就是facade預載入的核心類,只有一個bootstrap方法:

public function bootstrap(Application $app)
    {
        //1 第一步
        Facade::clearResolvedInstances();
        //2 第二步
        Facade::setFacadeApplication($app);
        //3 第三步
        AliasLoader::getInstance(array_merge(
            $app->make('config')->get('app.aliases', []),
            $app->make(PackageManifest::class)->aliases()
        ))->register();
    }

bootstrap方法做了如下的事情:
1.清空物件類陣列例項
1.1 清空處理就是置空。

public static function clearResolvedInstances()
    {
        static::$resolvedInstance = [];
    }

2.把容器放入facade物件,就是facade中的set方法
在2.1 上一章的abstract class Facade中,我們使用setFacadeApplication方法來注入容器物件(app)給facade類。

public static function setFacadeApplication($app)
{
    static::$app = $app;
}

3.把facade物件從config中讀取後註冊(這個註冊就是建立關係)
3.1 獲取所有配置的facade對應關係

$app->make('config')->get('app.aliases', []),
            $app->make(PackageManifest::class)->aliases()

預設的別名配置是從 app 配置檔案下的 aliases陣列 讀取的,PackageManifest 是 laravel 5.5 新增的 包自動發現規則,這裡我們暫時不考慮,還沒研究。

3.2 使用AliasLoader的getInstance方法註冊這些對應關係。

public static function getInstance(array $aliases = [])
    {
        if (is_null(static::$instance)) {
            return static::$instance = new static($aliases);
        }

        $aliases = array_merge(static::$instance->getAliases(), $aliases);

        static::$instance->setAliases($aliases);

        return static::$instance;
    }

3.2.1 第一步 如果當前這個例項為空,重新生成一個
3.2.2 把當前已經有的alias(別名)和傳入的alias合併
3.2.3 返回當前這個AliasLoader例項,包含了所有該有的別名對映。

3.3 使用 register()方法註冊

public function register()
    {
        if (! $this->registered) {
            $this->prependToLoaderStack();

            $this->registered = true;
        }
    }

3.3.1 prependToLoaderStack函式通過使用php自動載入方法spl_autoload_register 把當前物件(當前AliasLoader類中有個load方法)的load方法 放到SPL __autoload函式佇列的頭部。如果php執行中觸發了沒有宣告的類,會自動執行load這個函式。load函式通常都要引入(require 或者include)需要的類,如果沒有,就會在佇列中繼續尋找需要的類。(詳細部分放在下一章 2.3)

protected function prependToLoaderStack()
{
    spl_autoload_register([$this, 'load'], true, true);
}

3.3.1.1 然後看下這個load函式,主要存在兩種情況。

public function load($alias)
    {
        if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) {
            $this->loadFacade($alias);
            return true;
        }
        if (isset($this->aliases[$alias])) {
            return class_alias($this->aliases[$alias], $alias);
        }
    }

a.判斷facade的別名中是否存在啟始位置為Facades\\ 的欄位, 如果有就要呼叫loadFacade()去載入。
b.如果沒有,這裡是核心方法,使用了php的class_alias方法把別名和真實的類建立關係

比如我們直接使用 Route 時, 我們其實是呼叫的Illuminate\Support\Facades\Route類。

說到底,這個整個第三步其實就是使用了class_alias給配置項產生一個別名。然後存起來

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章