composer depends 查不到已卸载的包,因为它只分析当前 composer.json 中显式声明的依赖和 vendor/ 中已安装的包,不扫描残留文件或历史记录;所谓“幽灵依赖”是代码中仍存在 use、配置引用等静态残留,而该命令对此完全无感。

composer depends 为什么查不到已卸载的包?
它只分析当前 composer.json 和已安装的 vendor/ 内容,不会回溯历史或扫描残留文件。所谓“幽灵依赖”,其实是包已被 composer remove 掉,但代码里还留着 use 语句、配置引用或硬编码类名——composer depends 对这种静态残留完全无感。
真正该用它查的是:那些仍被其他已安装包间接拉入、但你早已不主动 require 的废弃包。比如你删了 monolog/monolog,但 laravel/framework 还依赖它,那它就还在 vendor/ 里活着,且 composer depends monolog/monolog 会显示 Laravel 是它的上层依赖。
- 运行前确保
composer install已完成,否则vendor/缺失会导致结果为空或不准 - 加
--tree参数能看清依赖链层级,比纯列表更易定位“谁在偷偷留着它” - 注意区分
composer show --tree(展示当前装了啥)和composer depends(反查谁依赖它),后者才是挖幽灵包的关键动作
怎么用 depends 找出没人要却还在 vendor 里的包?
先列出所有已安装但未出现在 composer.json 的包——它们就是候选幽灵。执行:
composer show --installed --format=json | jq -r 'keys[]' | while read pkg; do
if ! composer show --no-ansi 2>/dev/null | grep -q "^$pkg "; then
echo "$pkg (not in composer.json)";
composer depends "$pkg" --tree 2>/dev/null || echo " → no active dependency found";
fi
done
这段脚本本质是做两件事:确认包不在 composer.json 中,再用 composer depends 查它是否被其他包间接依赖。如果返回空或 “No package depends on…” 就基本坐实它是幽灵:没人要、没声明、还占着磁盘。
-
jq不是必须项,可用composer show --installed纯文本解析替代,但易受格式变化影响 - 某些包(如
phpunit/phpunit)可能只在require-dev里,检查时别漏掉--dev参数 - 输出中若出现
symfony/console被laravel/framework依赖,而你又没直接 require 它——这不算幽灵,是合法传递依赖
查到幽灵包后,删还是不删?
不能光看 composer depends 返回空就直接 rm -rf vendor/xxx。PHP 自动加载机制会让类文件即使没被依赖,只要路径匹配仍可能被加载——尤其当项目用了自定义 autoload 或 classmap。
- 搜索整个项目:
grep -r "use.*OldPackage\|new OldPackage\" --include="*.php" . - 检查
composer.json的autoload和autoload-dev字段,看是否手动映射了已删包的命名空间 - 运行
composer dump-autoload -o后再试php -m | grep oldpackage(如果它带扩展)或触发疑似调用路径,观察是否报Class not found - 某些包(如
doctrine/inflector)在 PHP 8.1+ 被标记为 abandoned,但 Laravel 9 仍通过illuminate/support拉入——这时删它会导致运行时报错,得等框架升级
为什么 vendor 里总有 abandoned 包却删不掉?
因为 composer depends 显示的依赖链末端,往往连着一个你不敢动的主包,比如 laravel/framework 或 symfony/symfony。这些主包没升级,它依赖的子包就只能一起卡住。
典型例子:paragonie/random_compat 在 PHP 7.0+ 已废弃,但旧版 firebase/php-jwt 仍 require 它;而你项目里 firebase/php-jwt 又被 google/apiclient 依赖……一层套一层,最终你得升级 google/apiclient 才能甩掉它。
- 用
composer why-not vendor/package:version可模拟升级尝试,看哪些包在拦路 -
composer outdated --direct只看直系依赖,容易漏掉深层废弃包;必须配合depends往下挖 - 有些废弃包(如
guzzlehttp/ringphp)连abandoned标签都没打,只能靠人工查 packagist 页面或 GitHub star 停更时间判断
幽灵依赖真正的麻烦不在命令行输出里,而在 autoload 加载器静默容忍了不该存在的类路径——删之前,务必确认 composer dump-autoload 后的 autoloader 文件里,真没留下任何指向它的映射。











