关于本页
在包开发基础中,我们介绍了只要在composer.json 的 extra.laravel 部分进行配置,服务提供者和门面就会被自动注册。本页将从源码层面讲解其背后的实现,即 Illuminate\Foundation\PackageManifest 类是如何工作的。
本页是包开发基础的姊妹篇。建议先阅读自动发现的基本用法。
自动发现机制的整体视图
PackageManifest 类
自动发现的核心是 Illuminate\Foundation\PackageManifest。以下是框架 13.x 时的实现(摘要)。
- 清单一旦被加载就会缓存在内存中(
$this->manifest属性)。在一次请求中即使多次调用providers(),文件 I/O 也只发生一次。 - 只有当清单文件不存在时才会执行
build()。在常规运行中并不会每次都进行构建。 - 其实体是一个仅通过
return返回朴素 PHP 数组的文件(bootstrap/cache/packages.php),仅需require即可加载,是最高效的形式。
清单的构建处理
build() 方法是实际汇总 composer.json 信息的部分。
composer.json,而是读取 vendor/composer/installed.json。这是 Composer 在执行 composer install / composer update 时生成的、包含所有已安装包元数据的文件。由于每个包的 composer.json 中所写的 extra 部分都被汇总到此,Laravel 侧只信赖并读取 Composer 管理下的信息。
installed.json 通常被 .gitignore,因此在初始搭建时(尚无 vendor/ 的状态)自动发现不会生效。只有在 composer install 完成之后,缓存才会首次被构建。dont-discover 的两种写法
dont-discover 可以写在包侧或应用侧的 composer.json 中,但含义不同。
包侧的 composer.json
应用侧的 composer.json
build() 的实现可知,$ignore 数组通过 array_merge 累积各包的 configuration['dont-discover']。也就是说,从技术上讲包自身也可以”禁用其依赖包的自动发现”(例如:不希望内部使用的子包的提供者被重复注册的情况)。不过实际中最常用的还是应用侧的禁用。
在 dont-discover 中指定 *
若 packagesToIgnore() 返回的数组中包含 *,则 $ignoreAll = true,会整体禁用所有包的自动发现。适用于在 CI 或测试环境下希望避免自动发现开销的情况,或希望通过 bootstrap/providers.php 完全手动管理的场景。
缓存文件的实体
若存在环境变量APP_PACKAGES_CACHE,getCachedPackagesPath() 会返回它,否则返回 bootstrap/cache/packages.php。
return 返回一个简单的关联数组。
php artisan package:discover 的输出中确认”检测到了哪些包”。
缓存重建的时机
Illuminate\Foundation\ComposerScripts 钩入了三个 Composer 事件,任何一个都会调用相同的 clearCompiled()。
composer install、composer update 还是 composer dump-autoload,配置缓存、服务缓存和包缓存都会一并被删除。下次 Laravel 启动时,PackageManifest::build() 会运行,并从最新的 installed.json 状态重建。
这是通过在 composer.json 的 scripts 中注册的、标准 Laravel 项目的默认行为。
Laravel 应用的 composer.json(摘录)
使用 php artisan package:discover 手动重建
若缓存过旧未更新,或者绕过 Composer 直接修改了 vendor/,可以使用 package:discover 命令进行手动重建。
$manifest->build() 并输出结果,几乎没有命令特有的逻辑。在 CI 流水线中使用 composer install --no-scripts 等 Composer 事件不会触发的场景下,需要显式调用该命令。
部署时的注意事项
在生产部署中通常会执行composer install --no-dev --optimize-autoloader,但如果加上了 --no-scripts,包缓存将不会被更新。为了安全起见,请在部署脚本中显式加入重建步骤。
config:cache 之前执行 package:discover。如果先创建了配置缓存,后续新增的包配置(例如通过 mergeConfigFrom 注册的配置)可能不会被反映。
小结
- 自动发现不是基于
composer.json的静态内容,而是通过读取 Composer 生成的vendor/composer/installed.json来运行。 - 结果作为纯 PHP 数组缓存到
bootstrap/cache/packages.php,仅需require即可高速加载。 - 缓存在
composer install/update/dump-autoload各事件中被自动删除,下次启动时进行重建。 - 在使用
--no-scripts执行 Composer 的环境中,需要显式调用php artisan package:discover。 - 在
dont-discover中指定*可以整体禁用自动发现。
相关页面
- 包开发基础 — 服务提供者与
extra.laravel的基本用法 - 延迟服务提供者 — 优化已发现的提供者的加载时机
- Orchestra Testbench 中的包测试 — Testbench 独有的
package:discover命令