本文主要讲解Laravel如果基于Composer实现自动发现扩展包

Laravel附带了一个composer.json文件,当一些Laravel包被拉取到本地后,还需要几步手动配置使之能在Laravel项目中使用

  • 注册 Service Provider
  • 注册 Alias或Facade
  • 发布 asset
    第一、二步已被 Taylor Otwell 确认有点繁琐,因此联合 Dries Vints 开发并推出了「自动注册 Service Provider 和 Facade」功能

在搜索并安装/更新不同的扩展包时,Composer会触发多个事件,这些事件可供订阅,一旦订阅的事件被触发,可调起一段自定义的代码或一条可执行的命令行.
当Composer生成最终的类加载文件.其中一个名为 post-autoload-dump 的事件将会被触发.而后,Laravel已可访问所有类并且项目可使用这些类了

之所以会这样,是因为Laravel在composer.json文件里订阅了 post-autoload-dump 事件

1
2
3
4
5
6
"scripts": {
"post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover"
]
}

首先,调起postAutoloadDump方法,该方法负责清除之前缓存的services和包.然后运行 package:discover 命令,这是关键所在

找寻扩展包
Illuminate\Foundation\Console\PackageDiscoverCommand 调用 Illuminate\Foundation\PackageManifest 类的 build() 方法. PackageManifest 类里包含 Laravel自动找寻已安装包 的实现
PackageManifest 类在应用启动时就被注册入容器里了(是在 Illuminate\Foundation\Application::registerBaseServiceProviders() 里注册)

build() 方法内,Laravel会去寻找 vendor/composer/installed.json 文件(该文件由composer生成),Laravel会映射这个文件的内容并且递归搜索含有 extra.laravel 的包

1
2
3
4
5
6
7
8
9
10
"extra": {
"laravel": {
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
],
"aliases": {
"Debugbar": "Barryvdh\\Debugbar\\Facade"
}
}
}

然后搜索 composer.json 文件的 extra.laravel.dont-discover 区段来判断是否有指定无需自动发现的包

1
2
3
4
5
6
7
"extra": {
"laravel": {
"dont-discover": [
"barryvdh/laravel-debugbar"
]
}
}

你可以添加 * 到数组区段里来告诉laravel不执行自动发现

至此,laravel已经收集好了有关扩展包的信息.接下来是把这些信息写入到 bootstrap/cache/packages.php 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php return array (
'barryvdh/laravel-debugbar' =>
array (
'providers' =>
array (
0 => 'Barryvdh\\Debugbar\\ServiceProvider',
),
'aliases' =>
array (
'Debugbar' => 'Barryvdh\\Debugbar\\Facade',
),
),
);

注册扩展包
当Laravel Kernel启动时,会有两个 bootstrapper启动器 会被调用到

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

第一个使用 Illuminate\Foundation\AliasLoader 将所有 Facade 加载到容器里,现在唯一不同的是laravel会把 packages.php 里需要加载的 aliases 都一并加载到容器.(使用 PackageManifest::aliases() 方法来收集这些信息)

1
2
3
4
5
// in RegisterFacades::bootstrap()
AliasLoader::getInstance(array_merge(
$app->make('config')->get('app.aliases', []),
$app->make(PackageManifest::class)->aliases()
))->register();

如上所示,config/app.php 里配置的 aliasesPackageManifest类aliases 合并到一起.

相似地,Service Provider 也是这样注册. RegisterProviders 启动器调用 Foundation\Application::registerConfiguredProviders() 把Laravel从所有扩展包中收集的 Service Provider 注册入容器

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

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

欢迎转载但请附上链接,谢谢。

原文:laravels-package-auto-discovery

如有什么错误,欢迎提出、讨论,大家共同进步 _