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

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

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

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

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

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

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

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

composer require spatie/laravel-analytics

Composer所作的工做在於,拉取你所需版本包,下載到  vendor 目錄,上述命令執行完畢, 包內全部類和文件被加載進項目,你就能夠立刻使用它們了,每次當你再次執行 composer update ,Composer 將會從新獲取(譯者注 一般從 composer 倉庫拉取)更新該包,而且自動更新位於你項目  vendor 目錄下的文件。json

在 Laravel 項目中使用某些 Laravel包 須要如下額外幾個步驟bootstrap

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

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

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

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

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

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

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

你能夠再上面閱讀更多有關門面的信息 官方文檔.composer

在查找和安裝/更新不一樣的擴展包時,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 組件將會在嘗試加載任何其餘服務提供者以前加載。

相關文章
相關標籤/搜索