composer remove 不会自动清理间接依赖,仅删除无任何包引用的顶层依赖;需用 composer why 检查引用关系,确认无依赖后执行 remove --no-update 再统一 update。

composer remove 会自动处理依赖关系吗
不会。执行 composer remove vendor/package-name 只会移除指定包及其在 composer.json 中的声明,但不会主动清理它曾经引入、但当前其他包仍依赖的“间接依赖”。真正被删掉的,仅是那些已无任何包(包括你的项目代码和其余已安装包)引用的顶层依赖及其子树。
常见错误现象:运行 composer remove monolog/monolog 后,composer show 里还看到 psr/log 留着——因为它正被 symfony/console 依赖着,不是“未使用”,只是你没直接 require 它。
- 判断是否真“未使用”:先跑
composer why psr/log,看谁在用它;如果输出为空,才说明可安全清理 - 想批量查孤儿包?没有内置命令,但可用
composer show --tree | grep -v "├──\|└──"辅助筛选(不严谨,仅作初筛) - 别手动删
vendor/下文件或改composer.lock,这会导致状态不一致,下次composer install可能失败
composer update --with-dependencies 不等于清理
这个参数常被误解为“清理冗余依赖”,其实它只控制更新范围:composer update foo/bar --with-dependencies 表示“更新 foo/bar,同时更新它所有直接依赖(哪怕版本没变)”,和删除完全无关。
真正影响依赖图收缩的操作只有两个:remove 和 update(后者通过版本约束收紧间接触发淘汰)。比如把 "guzzlehttp/guzzle": "^7.0" 改成 "^7.2" 并运行 composer update guzzlehttp/guzzle,可能让旧版 ralouphie/getallheaders 被新版 Guzzle 的更严格依赖声明踢出图谱。
- 想靠
update清理?先检查composer.json里的版本约束是否过于宽松(如"*"或"^1.0 || ^2.0") - 运行
composer update --dry-run预览变更,重点关注 “Removing” 行——那是真正会被删的包 -
composer update默认不降级,所以即使某个包已无用,只要锁文件里有且满足约束,就不会被删
如何识别并删除真正的“未使用包”
核心逻辑:一个包属于“未使用”,当且仅当它既不在 composer.json 的 require / require-dev 中,也不被任何已安装包的 composer.json 声明为依赖(含 require-dev,但注意 dev 依赖在生产 install 时默认不加载)。
实操分三步:
- 运行
composer show --installed列出所有已装包 - 对每个包执行
composer why;若返回 “Did not find package”,说明它没被任何根依赖或其传递依赖引用 → 可删in any registered root dependencies - 确认后执行
composer remove(加--no-update --no-update避免连锁更新干扰判断),再统一composer update
注意:composer why 在某些旧版 Composer(如 1.x)中不支持,需升级到 2.2+;若无法升级,可临时用 composer depends (Composer 2.5+ 新增)替代。
dev 依赖残留特别容易被忽略
很多项目把测试工具(如 phpunit/phpunit)、静态分析(如 phpstan/phpstan)放在 require-dev,但重构后忘了删。它们不会出现在生产环境 vendor/(如果用了 --no-dev),但仍在本地 composer.json 里占位,且 composer why 默认不查 dev 依赖链。
查 dev 包是否真被需要:
- 加
-D参数:composer why -D phpunit/phpunit - 或者直接删掉
require-dev条目,再跑composer install --dry-run,看是否报错缺失依赖 - CI 脚本里如果用了
--no-dev,那所有require-dev包本就不该影响运行时,可放心清理
最麻烦的是那种“被注释掉的 require”或“写在 README 里但没进 composer.json”的包——它们根本不会出现在 vendor/,也无需清理,但容易让人误以为是“残留”。盯紧 composer.json 和 composer.lock 两个文件,才是真实依据。










