在之前的 Laravel 版本中,安裝包通常需要幾個步驟,例如添加服務提供器到 app 配置文件并注冊相關的 facades。現在,從 Laravel 5.5 開始,Laravel 可以自動檢測并注冊服務提供器和 facades。
本文不是聚焦于他是怎么用的,而是看看它的源碼,是怎么實現Package Auto Discovery的。
composer.json
一切的起源都是來自 composer.json ,在使用 composer 的時候,你可以在 post-autoload-dump 部分指定你想執行的腳本,比如在 Laravel 5.5 的時候,我們可以看到這樣的定義:
- "scripts": {
- "post-autoload-dump": [
- "Illuminate//Foundation//ComposerScripts::postAutoloadDump",
- "@php artisan package:discover"
- ]
- }
對于 postAutoloadDump 是很熟悉了,Laravel 之前的版本基本都有,它的工作是清理一些緩存,刪除一些舊的文件。
我們的關注重點是@php artisan package:discover ,也就是會執行@php artisan package:discover 這個命令。
這個命令是干嘛的呢?它其實是位于IlluminateFoundationConsolePackageDiscoverCommand 中,主要是通過執行IlluminateFoundationPackageManifest 的 build() 方法來達到 發現 package 的目的。
而 PackageManifest 早就注冊在 Laravel 的 Container 中,那么它可以保證每次在啟動 Laravel 的時候都能使用 PackageManifest 的 build() 方法,這個 build() 方法主要的邏輯就是:
找尋 vendor/composer/installed.json 這個文件,這個文件是 composer 自己生成的,記錄著每一次的 composer autoload 的 class map。
而此時,Laravel 又將這些內容映射到 extra.laravel 的部分,比如:
- "extra": {
- "laravel": {
- "providers": [
- "Barryvdh//Debugbar//ServiceProvider"
- ],
- "aliases": {
- "Debugbar": "Barryvdh//Debugbar//Facade"
- }
- }
- }
Laravel 首先將以上內容直接讀取下來放到一個 collection 中,然后在去檢查下面這個部分的定義,在決定是否需要執行 Package Discover 動作:
- "extra": {
- "laravel": {
- "dont-discover": [
- "barryvdh/laravel-debugbar"
- ]
- }
- }
如果你不想執行 @php artisan package:discover 的發現效果,可以直接在 dont-discover 的數組里面填上 *。
這樣判斷完成之后,Laravel 將 collection 中需要發現的 Package 內容保存到一個緩存文件中bootstrap/cache/packages.php:
- return array (
- 'barryvdh/laravel-debugbar' => array (
- 'providers' => array (
- 0 => 'Barryvdh//Debugbar//ServiceProvider',
- ),
- 'aliases' => array (
- 'Debugbar' => 'Barryvdh//Debugbar//Facade',
- ),
- ),
- );
Laravel 怎么拿到 Package 信息?
這個時候,我們又需要看看 Laravel 項目啟動的時候,主要啟動下面的兩個服務:
IlluminateFoundationBootstrapRegisterFacades
IlluminateFoundationBootstrapRegisterProvider
而 上面的第一個服務會使用 IlluminateFoundationAliasLoader 加載所有的 alias ,在 AliasLoader 中:
- // in RegisterFacades::bootstrap()
- AliasLoader::getInstance(array_merge(
- $app->make('config')->get('app.aliases', []),
- $app->make(PackageManifest::class)->aliases()
- ))->register();
你可以看到,它首先讀取 app.php 的 aliases 數組,然后與 bootstrap/cache/packages.php 的 packages 數組合并,這個時候,就可以獲取到所有的 packages 信息進行發現和自帶加載了。這樣合并的好處就是,你還是可以直接在 app.php中定義你的 alias 來覆蓋自動發現的 package,從而保證你的項目還是可以運行得很流暢。
新聞熱點
疑難解答