解讀 Laravel 中的擴展自動註冊機制(Package Auto-discovery)

文章轉發自專業的Laravel開發者社區,原始鏈接:https://learnku.com/laravel/t...

在進入探究 Laravel 包提供者與門面如何自動發現之前,讓我們先粗淺剖析一下PHP 中包的概念。

一個包就是一個在多個項目內可複用的代碼片段,例如 包 spatie/laravel-analytics 可以讓你在laravel項目內,用一種簡易方式從谷歌統計(Google Analytics)中取回數據,該包被託管 在 GitHub 上,由 Spatie進行維護,它們會持續發佈,更新和修復該包 bug,如果你在項目當中使用該包,希望獲取這些一旦發佈的更新和修復,無須擔心使用Composer 從 Github 上 拷貝一份新代碼即可。

Composer 是一個 PHP 依賴管理工具。它允許你聲明項目庫依賴且管理(安裝/更新)它們。 -- 詳見官網 getcomposer.org

Laravel 自帶 composer.json文件,文件內的 require 或 require-dev條目下,給出了你擴展應用功能需用到的包,執行 composer update:

{
    "require": {
        "spatie/laravel-analytics": "3.*",
    },
}

你也可以使用下面命令,達到同樣的效果

composer require spatie/laravel-analytics

Composer所做的工作在於,拉取你所需版本包,下載到  vendor 目錄,上述命令執行完畢, 包內所有類和文件被加載進項目,你就可以馬上使用它們了,每次當你再次執行 composer update ,Composer 將會重新獲取(譯者注 通常從 composer 倉庫拉取)更新該包,並且自動更新位於你項目  vendor 目錄下的文件。

在 Laravel 項目中使用某些 Laravel包 需要以下額外幾個步驟

  • 註冊服務提供者
  • 註冊別名/門面
  • 發佈資源

如果你看過 Spatie包安裝說明 你會發現,在繼續下一步這前,項目配置必須註冊服務提供者和一個門面,是一個很好的習慣,這個步驟由 Taylor Otwell定義,只是一個非必要條件, Dries Vints,且達到無論何時你決定引入一個新包或移除包,服務提供者和門面皆可被自動發現。

重溫 Taylor 的新特性聲明  在媒體上.

什麼是服務提供者和門面?

服務提供者負責將事物綁定到 Laravel 的服務容器中,並通知 Laravel 在哪裏加載包資源,例如視圖,配置和本地化文件。-- laravel.com 文檔

你可以在上面閱讀有關服務提供者的更多信息 官方文檔.

門面爲應用程序服務容器中可用的類提供 "static" 接口 -- laravel.com 文檔

你可以再上面閱讀更多有關門面的信息 官方文檔.

在查找和安裝/更新不同的擴展包時,Composer 會觸發你可以訂閱的多個事件並運行你自己的一段代碼甚至是命令行可執行文件,其中一個有趣的事件稱爲 post-autoload-dump。 在 composer 生成項目中自動加載的最終類列表之後直接觸發,此時 Laravel 已經可以訪問所有類,並且應用程序已準備好使用加載到其中的所有包類。

Laravel 在主 composer.json 文件中訂閱此事件:

"scripts": {
    "post-autoload-dump": [
        "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
        "@php artisan package:discover"
    ]
}

首先它調用 postAutoloadDump() 靜態方法,此方法會清理緩存的服務或之前發現的包,另一個是它運行 package:discover artisan 命令,這就是 Laravel 可以自動發現是祕密。

包自動發現

Illuminate\Foundation\Console\PackageDiscoverCommandIlluminate\Foundation\PackageManifest 類中調用 build() 方法,該類是Laravel 發現已安裝包的地方。

 PackageManifest 在應用程序引導程序的早期註冊到容器中,完全來自 Illuminate\Foundation\Application::registerBaseServiceProviders(),此方法在創建 Laravel 應用程序的新實例後直接運行。

在 build() 方法中,Laravel 查找vendor/composer/installed.json 文件,它由composer 生成並保存一個完整的映射,其中包含 composer 安裝的所有擴展包的composer.json 文件內容, Laravel 映射該文件的內容並搜索包含 extra.laravel部分的包:

"extra": {
    "laravel": {
        "providers": [
            "Barryvdh\\Debugbar\\ServiceProvider"
        ],
        "aliases": {
            "Debugbar": "Barryvdh\\Debugbar\\Facade"
        }
    }
}

它首先收集該部分的內容,然後查看主 composer.json 文件下的 extra.laravel.dont-discover 的內容,看看你是否決定不自動發現某些包或所有包:

"extra": {
    "laravel": {
        "dont-discover": [
            "barryvdh/laravel-debugbar"
        ]
    }
}

你可以在數組中添加 * 以指示 laravel 完全停止自動註冊。

現在 Laravel 收集了有關擴展包的信息

是的,一旦獲得所需要的信息,它將在 bootstrap/cache/packages.php 中編寫一個 PHP 文件:

<?php return array (
  'barryvdh/laravel-debugbar' =>
  array (
    'providers' =>
    array (
      0 => 'Barryvdh\\Debugbar\\ServiceProvider',
    ),
    'aliases' =>
    array (
      'Debugbar' => 'Barryvdh\\Debugbar\\Facade',
    ),
  ),
);

包註冊

Laravel 有兩個 bootstrappers,在 HTTP 或控制檯內核啓動時使用:

  • \Illuminate\Foundation\Bootstrap\RegisterFacades
  • \Illuminate\Foundation\Bootstrap\RegisterProviders

第一個使用 Illuminate\Foundation\AliasLoader 將所有門面加載到應用程序中,現在 Laravel 將查看 packages.php 生成的文件並提取包中希望 Laravel 自動註冊的所有別名並註冊這些別名。 它使用 PackageManifest::aliases() 方法來收集這些信息。

// 在 RegisterFacades::bootstrap()

AliasLoader::getInstance(array_merge(
    $app->make('config')->get('app.aliases', []),
    $app->make(PackageManifest::class)->aliases()
))->register();

如你所見,從 config/app.php 文件加載的別名與從 PackageManifest 類加載的別名合併。

類似地,Laravel 在啓動時註冊服務提供者,RegisterProviders bootstrapper 調用 Foundation\ApplicationregisterConfiguredProviders() 方法,並且 Laravel 在這會收集所有應該自動註冊的包提供者並註冊它們。

$providers = Collection::make($this->config['app.providers'])
                ->partition(function ($provider) {
                    return Str::startsWith($provider, 'Illuminate\\');
                });

$providers->splice(1, 0, [$this->make(PackageManifest::class)->providers()]);

在這裏,我們在 Illuminate 服務提供者和可能在你的配置文件中的任何其他服務提供者之間注入自動發現的服務提供者,這樣我們確保你可以通過在配置文件中重新註冊它們來覆蓋擴展包服務提供者,並且 Illuminate 組件將會在嘗試加載任何其他服務提供者之前加載。

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