如何深入理解Composer在Laravel等框架中自动发现并注入包的原理?
- 内容介绍
- 文章标签
- 相关推荐
本文共计885个文字,预计阅读时间需要4分钟。
《Composer 本身不解析 extra.laravel.providers,也不自动注册任何服务提供者——所谓自动发现是 Laravel 框架自带的逻辑,依赖 php artisan package:discover 命令驱动。》
为什么改了 composer.json 却没生效
你手动在包的 composer.json 里加了 "providers": ["Vendor\Package\ServiceProvider"],但 Laravel 启动后依然没注册。这不是 Laravel “没看到”,而是它根本没机会读到——因为 Laravel 只从 vendor/composer/installed.php(Composer 2+)或 vendor/composer/installed.json(Composer 1)里提取信息,而不是直接扫描每个包的 composer.json 文件。
- 运行
composer dump-autoload才会刷新installed.php,否则新字段不会写入 - 如果用了
composer install --no-scripts(常见于 CI 环境),post-autoload-dump钩子被跳过,package:discover根本不执行 - 本地 path 仓库未执行
composer update,包不会出现在installed.php中,Laravel 视为“未安装”
ServiceProvider 类找不到的三个硬性条件
package:discover 扫描到 providers 数组后,会逐个验证类是否可加载。任一失败就静默跳过,不报错、不提示——这是最隐蔽的坑。
-
type字段必须是library或laravel-package;vcs、package等类型会被忽略 - 服务提供者类所在的命名空间,必须已在包自己的
autoload.psr-4或autoload.files中声明 - 该包必须真实存在于
vendor/下,且installed.php中有对应条目(composer show vendor/package能查到)
packages.php 缓存不是配置源,只是中间产物
bootstrap/cache/packages.php 是 Laravel 自己生成的缓存文件,内容类似:return ['vendor/package' => ['providers' => [...]]];。它只被 Laravel 启动时读取,不参与 Composer 加载流程。
- 清 config 缓存(
php artisan config:clear)不影响它;但php artisan optimize:clear会删掉它 - 修改包的
composer.json后,必须手动运行php artisan package:discover --force强制重写,否则旧缓存一直生效 - 这个文件里没有你的包?先检查
installed.php是否含该包及extra.laravel.providers字段,再看类名能否class_exists()成功
自动发现 ≠ 自动发布资源
很多开发者以为启用了自动发现,config/、migrations/、views/ 就会自动复制过去。其实完全不是一回事。
-
package:discover只负责把服务提供者类写进packages.php,启动时调用$app->register() - 资源发布必须靠服务提供者里的
$this->publishes()方法显式声明,且源路径必须存在、可读 - 即使
providers注册成功,若publishes()的第一个参数数组为空或路径错误,php artisan vendor:publish也找不到可发布的项
真正容易被忽略的是:整个流程依赖两个独立环节的协同——Composer 刷新 installed.php,Laravel 执行 package:discover。断掉任意一环,就只剩手动加 config/app.php 这一条路。
本文共计885个文字,预计阅读时间需要4分钟。
《Composer 本身不解析 extra.laravel.providers,也不自动注册任何服务提供者——所谓自动发现是 Laravel 框架自带的逻辑,依赖 php artisan package:discover 命令驱动。》
为什么改了 composer.json 却没生效
你手动在包的 composer.json 里加了 "providers": ["Vendor\Package\ServiceProvider"],但 Laravel 启动后依然没注册。这不是 Laravel “没看到”,而是它根本没机会读到——因为 Laravel 只从 vendor/composer/installed.php(Composer 2+)或 vendor/composer/installed.json(Composer 1)里提取信息,而不是直接扫描每个包的 composer.json 文件。
- 运行
composer dump-autoload才会刷新installed.php,否则新字段不会写入 - 如果用了
composer install --no-scripts(常见于 CI 环境),post-autoload-dump钩子被跳过,package:discover根本不执行 - 本地 path 仓库未执行
composer update,包不会出现在installed.php中,Laravel 视为“未安装”
ServiceProvider 类找不到的三个硬性条件
package:discover 扫描到 providers 数组后,会逐个验证类是否可加载。任一失败就静默跳过,不报错、不提示——这是最隐蔽的坑。
-
type字段必须是library或laravel-package;vcs、package等类型会被忽略 - 服务提供者类所在的命名空间,必须已在包自己的
autoload.psr-4或autoload.files中声明 - 该包必须真实存在于
vendor/下,且installed.php中有对应条目(composer show vendor/package能查到)
packages.php 缓存不是配置源,只是中间产物
bootstrap/cache/packages.php 是 Laravel 自己生成的缓存文件,内容类似:return ['vendor/package' => ['providers' => [...]]];。它只被 Laravel 启动时读取,不参与 Composer 加载流程。
- 清 config 缓存(
php artisan config:clear)不影响它;但php artisan optimize:clear会删掉它 - 修改包的
composer.json后,必须手动运行php artisan package:discover --force强制重写,否则旧缓存一直生效 - 这个文件里没有你的包?先检查
installed.php是否含该包及extra.laravel.providers字段,再看类名能否class_exists()成功
自动发现 ≠ 自动发布资源
很多开发者以为启用了自动发现,config/、migrations/、views/ 就会自动复制过去。其实完全不是一回事。
-
package:discover只负责把服务提供者类写进packages.php,启动时调用$app->register() - 资源发布必须靠服务提供者里的
$this->publishes()方法显式声明,且源路径必须存在、可读 - 即使
providers注册成功,若publishes()的第一个参数数组为空或路径错误,php artisan vendor:publish也找不到可发布的项
真正容易被忽略的是:整个流程依赖两个独立环节的协同——Composer 刷新 installed.php,Laravel 执行 package:discover。断掉任意一环,就只剩手动加 config/app.php 这一条路。

