文章转发自专业的Laravel开发者社区,原始连接:learnku.com/laravel/t/3…php
在进入探究 Laravel 包提供者与门面如何自动发现以前,让咱们先粗浅剖析一下PHP 中包的概念。laravel
一个包就是一个在多个项目内可复用的代码片断,例如 包 spatie/laravel-analytics 可让你在laravel项目内,用一种简易方式从谷歌统计(Google Analytics)中取回数据,该包被托管 在 GitHub 上,由 Spatie进行维护,它们会持续发布,更新和修复该包 bug,若是你在项目当中使用该包,但愿获取这些一旦发布的更新和修复,无须担忧使用Composer 从 Github 上 拷贝一份新代码便可。git
Composer 是一个 PHP 依赖管理工具。它容许你声明项目库依赖且管理(安装/更新)它们。 -- 详见官网 getcomposer.orggithub
Laravel 自带 composer.json
文件,文件内的 require
或 require-dev
条目下,给出了你扩展应用功能需用到的包,执行 composer update
:json
{
"require": {
"spatie/laravel-analytics": "3.*",
},
}
复制代码
你也可使用下面命令,达到一样的效果bootstrap
composer require spatie/laravel-analytics
复制代码
Composer所作的工做在于,拉取你所需版本包,下载到 vendor
目录,上述命令执行完毕, 包内全部类和文件被加载进项目,你就能够立刻使用它们了,每次当你再次执行 composer update
,Composer 将会从新获取(译者注 一般从 composer 仓库拉取)更新该包,而且自动更新位于你项目 vendor
目录下的文件。数组
在 Laravel 项目中使用某些 Laravel包 须要如下额外几个步骤缓存
若是你看过 Spatie包安装说明 你会发现,在继续下一步这前,项目配置必须注册服务提供者和一个门面,是一个很好的习惯,这个步骤由 Taylor Otwell定义,只是一个非必要条件, Dries Vints,且达到不管什么时候你决定引入一个新包或移除包,服务提供者和门面皆可被自动发现。bash
重温 Taylor 的新特性声明 在媒体上.app
服务提供者负责将事物绑定到 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\PackageDiscoverCommand
在 Illuminate\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 彻底中止自动注册。
是的,一旦得到所须要的信息,它将在 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\Application
的 registerConfiguredProviders()
方法,而且 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 组件将会在尝试加载任何其余服务提供者以前加载。