Laravel源碼入門-啓動引導過程(十)RegisterProviders

上篇:Laravel源碼入門-啓動引導過程(九)RegisterFacades

上文介紹了 HandleExceptions,在 《Laravel源碼入門-啓動引導過程(五)$kernel->handle($request)》中第五個要載入的是 RegisterProviders,也就是 Foundation\Http\Kernel::bootstrapers[] 的第五個

\Illuminate\Foundation\Bootstrap\RegisterProviders::class, 如下:

// Illuminate\Foundation\Http\Kernel.php 片段

   /**
     * The bootstrap classes for the application.
     * 引導類,起引導作用的類
     *
     * @var array
     */
    protected $bootstrappers = [
        // 載入服務器環境變量(.env 文件)
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        // 載入配置信息(config 目錄)
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        // 配置如何處理異常
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        // 註冊 Facades
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        // 註冊 Providers
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        // 啓動 Providers
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];

我們再直接貼出 RegisterProviders 類的代碼,進行分析,非常直觀,如下

<?php

namespace Illuminate\Foundation\Bootstrap;

use Illuminate\Contracts\Foundation\Application;

class RegisterProviders
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        // 非常簡單一句話,註冊配置的 Providers。
        $app->registerConfiguredProviders();
    }
}

本來至此,RegisterProviders 就結束了,但再深入一步,我們看看 Application::registerConfiguredProviders() 的源代碼:

//來自: Illuminate\Foundation\Application.php

   /**
     * Register all of the configured providers.
     *
     * @return void
     */
    public function registerConfiguredProviders()
    {
        // 下面是 註冊配置好的 Providers 方法代碼,我們註釋掉改寫一下。
        //(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))
        //            ->load($this->config['app.providers']);

        // 第一步:創建 Provider 倉庫對象
        $repository = new ProviderRepository(
            $this, 
            new Filesystem, 
            $this->getCachedServicesPath()
        );
        // dump($repository);

        // 第二步:獲取配置文件 config/app.php 中的 providers 數組中指定的 Providers
        // 打印出 $this($app),可以看到 $app 中的 instances 包含 config 實例
        // $this->config 是 LoadConfiguration 時 綁定的實例,原來的綁定代碼如下:

        // Illuminate\Foundation\Bootstrap\LoadConfiguraton.php
        // $app->instance('config', $config = new Repository($items));

        $providers = $this->config['app.providers'];
        // dump($this);
        // dump($providers);

        // 第三步:Provider 倉庫對象載入 Providers
        $repository->load($providers);
    }

==分析==

1. 第一步,從緩存服務路徑(bootstrap/cache/services.php))創建倉庫,爲什麼?深入 ProviderRepository.php 源代碼可以看到,services.php 中的 服務提供者是基本的,在載入過程中,需要與 config/app.php 中的 privoders 做對比的,如果不一致,需要重新載入。La讓用戶添加的自己的 providers 放在 app.php 中。

2. 第二步,就是獲取了所有的 config/app.php 中的 providers。

3. 第三步,載入,具體載入時,做了幾件事,請看 $reponsitory->load() 源代碼。

-- 比對 services.php,判定是否需要重新編譯;

-- 註冊事件,實際是處理 services.php 中的 when 數組,這個 when 很形象,意思是 什麼時候要做什麼事(件);

-- 繼續註冊 eager 數組中的 providers,這裏 eager 急切的、熱切的,是積極載入的意思,他相對的就是 lazy loading,所以 laravel 讓需要先行載入的 providers 放在 eager 數組中;這裏 最終實際調用了每個 provider 自己都有的 register(),縱觀 Laravel 引導的全過程,實際上就是 找到列表,調用列表中每個的 register()以及必要的 boot()。

-- 最後,處理 標記爲 deffered 的 providers,這其實就是 lazy loading 的內容了,這個處理只是 add,不是 register。

下面列出 services.php 大體樣子,初學者根據以上描述體會吧。

<?php return array (
  'providers' => 
  array (
    0 => 'Illuminate\\Auth\\AuthServiceProvider',
    1 => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
    // 略去...2~24...
    25 => 'App\\Providers\\EventServiceProvider',
    26 => 'App\\Providers\\RouteServiceProvider',
  ),
  'eager' => 
  array (
    0 => 'Illuminate\\Auth\\AuthServiceProvider',
    1 => 'Illuminate\\Cookie\\CookieServiceProvider',
    // 略去...2~11...
    12 => 'App\\Providers\\EventServiceProvider',
    13 => 'App\\Providers\\RouteServiceProvider',
  ),
  'deferred' => 
  array (
    'Illuminate\\Broadcasting\\BroadcastManager' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
    // 略去更多 lazy loading 的
    'validation.presence' => 'Illuminate\\Validation\\ValidationServiceProvider',
    'command.tinker' => 'Laravel\\Tinker\\TinkerServiceProvider',
  ),
  'when' => 
  array (
    'Illuminate\\Broadcasting\\BroadcastServiceProvider' => 
    array (
    ),
    'Illuminate\\Bus\\BusServiceProvider' => 
    array (
    ),
    // 略去一些事件(實際上都是空的,沒寫)
    'Laravel\\Tinker\\TinkerServiceProvider' => 
    array (
    ),
  ),
);

== 附錄 ===

Laravel的核心思想之一是 container 和 provider,應該更精確的說是, service container 和 service provider。粗淺理解,所有的功能(路由、用戶驗證、緩存、數據庫、分頁等。。。看看 services.php )都是服務,provider 更容易的理解是 “供應商” ,這些功能都被獨立成或收歸到供應商那裏,需要哪個服務,就把供應商叫來,叫哪裏來?叫到app裏,都爲app提供服務,所以,Application 就是個最大的 container。我們這個供應商,其實也可以叫 vendor,只是 vendor 相對於整個框架,而 provider 還是微觀一些。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章