Composer无内置校验命令,需用PHP脚本读取installed.json,检查autoload.files文件及psr-4根路径是否存在,跳过path类型包,通过file_exists/is_dir验证并集成到pre-autoload-dump钩子。

Composer 脚本怎么检测 vendor 里缺失的文件
Composer 本身不提供「检查 vendor 是否完整」的内置命令,composer install 和 composer update 都是靠 vendor/composer/installed.json 和 composer.lock 推导依赖状态,但不会校验磁盘上每个文件是否存在。实际中,你手动删了某个包的文件、或 rsync 同步中断、或 CI 缓存污染,都可能导致 vendor/ “看起来有,其实缺”。这时候得自己写脚本兜底。
用 PHP 脚本遍历 installed.json 检查文件路径
核心思路:读取 vendor/composer/installed.json(v2 格式)或 vendor/composer/installed.php(v1),提取每个包的 dist.type、dist.url、dist.shasum,再结合 source.type 和 source.reference 判断是否该有源码目录;但最轻量的做法是——只检查每个包的 autoload.psr-4 或 autoload.files 声明的入口文件是否存在。
实操建议:
- 优先检查
autoload.files数组里的每个.php文件,它们通常是初始化钩子,缺失会直接导致Class not found - 对
psr-4映射,只需验证命名空间根路径是否存在(例如"Monolog\": "src/"→ 检查vendor/monolog/monolog/src/目录) - 跳过
dist.type === "path"的包(本地路径包,文件状态由你控制,不纳入自动校验) - 用
file_exists()+is_dir()判定,别用class_exists()——它可能被 opcache 缓存误导
示例片段(保存为 check-vendor-files.php):
<?php $installed = json_decode(file_get_contents('vendor/composer/installed.json'), true);
foreach ($installed['packages'] as $pkg) {
$path = 'vendor/' . $pkg['name'];
if (!is_dir($path)) {
echo "MISSING: {$pkg['name']} (dir)
";
continue;
}
if (isset($pkg['autoload']['files'])) {
foreach ($pkg['autoload']['files'] as $file) {
if (!file_exists($path . '/' . $file)) {
echo "MISSING: {$pkg['name']} {$file}
";
}
}
}
}
在 composer.json 里注册为脚本并绑定事件
把校验逻辑包装成可复用命令,比每次敲 php check-vendor-files.php 更可靠,尤其适合加到 CI 或 pre-commit 流程里。
关键点:
- 脚本必须放在项目根目录下(如
bin/check-vendor.php),且有可执行权限(chmod +x)或明确用php调用 - 在
composer.json的scripts里声明:"check-vendor": "php bin/check-vendor.php" - 不要绑定到
post-install-cmd—— 安装失败时这个钩子根本不会触发;推荐绑定到pre-autoload-dump,因为任何依赖变更(install/update/dump-autoload)前都会走这一步 - 若需失败中断流程,脚本末尾必须
exit(1),否则 Composer 默认当成功处理
为什么不用 composer validate 或 composer show --tree
composer validate 只校验 composer.json 格式和字段合法性,跟 vendor 文件毫无关系;composer show --tree 读的是 lock 文件和已安装元数据,只要 installed.json 还在,就认为包“存在”,哪怕里面所有 .php 文件都被 rm -rf 了它也照常输出树状结构。
真正容易被忽略的点是:某些包(比如 Laravel 的 laravel/framework)把核心类放在 src/ 下,但 autoload.psr-4 映射的是 "Illuminate\": "src/Illuminate/" —— 如果你误删了 vendor/laravel/framework/src/Illuminate/,脚本必须能识别出 src/Illuminate/ 目录不存在,而不是只查 src/。路径拼接时务必保留原始 autoload 声明里的相对路径层级。











