Laravel 的“自动发现”是框架自身逻辑,非 Composer 功能;它通过 post-autoload-dump 钩子触发 php artisan package:discover,读取 installed.php 中 extra.laravel.providers,校验类可加载后写入 bootstrap/cache/packages.php 并注册。

Composer 本身不处理“自动发现”——这个词在 PHP 生态里常被误用,实际是 Laravel 等框架基于 Composer 的钩子机制自己实现的,不是 Composer 的功能。
composer install 后为什么 Laravel 才注册服务提供者
因为 Laravel 的 php artisan package:discover 命令只在 Composer 触发 post-autoload-dump 钩子时运行。这个钩子默认在 composer install 或 composer update 结束后执行(前提是没加 --no-scripts)。
- 它不扫描文件系统,也不逐个读取
vendor/*/composer.json - 它只读
vendor/composer/installed.php(Composer 2+)或vendor/composer/installed.json(Composer 1) - 对每个已安装包,检查其
extra.laravel.providers字段是否存在且格式合法 - 验证每个类名是否能被当前 autoloader 加载(
class_exists($class)必须返回true) - 通过校验的类才写入
bootstrap/cache/packages.php
为什么改了 composer.json 却没生效
常见现象:你在包的 composer.json 里加了 "extra": {"laravel": {"providers": [...]}},但 php artisan tinker 里 app()->getProviders() 看不到它。
-
composer dump-autoload没运行 →installed.php里没更新你的包信息,package:discover根本读不到 - 用了
composer install --no-scripts(CI 环境常见)→post-autoload-dump钩子被跳过,命令完全不执行 - 服务提供者类名拼错,或命名空间没在包自己的
autoload.psr-4里声明 →package:discover静默跳过,不报错 - 包是本地
path类型,但没执行composer update→installed.php没记录,Laravel 当它没装
extra.laravel.providers 的三个硬性门槛
Laravel 不会“尽力而为”,以下任一条件不满足,整条配置就被忽略:
-
type字段必须是library或laravel-package(不能是vcs、package等) - 服务提供者类(如
Vendor\Package\ServiceProvider)必须能被当前 autoloader 加载——即该类所在的命名空间已在包自己的autoload.psr-4或autoload.files中声明 - 包必须真实出现在
vendor/composer/installed.php里(即已通过composer require或composer update安装,且不是仅靠repositories配置“假装存在”)
packages.php 缓存文件是怎么来的
bootstrap/cache/packages.php 是 Laravel 写的临时缓存,不是你手动维护的配置源。
- 由
Illuminate\Foundation\PackageManifest类驱动,只在php artisan package:discover运行时生成 - 内容只是
extra.laravel.providers数组的“可加载校验后快照”,一旦类不可加载,就不会写进去 -
php artisan config:clear不影响它;但php artisan optimize:clear或手动删掉这个文件,下次启动就会重建 - 最常被忽略的一点:自动发现不是“安装即注册”,而是“安装 + 钩子触发 + 类可加载 + 缓存写入”四步缺一不可











