composer why-not 能直接告诉你谁在挡路,遇到 your requirements could not be resolved 或 conclusion: don't install xxx 时,应立即运行 composer why-not vendor/package:version(如 composer why-not monolog/monolog:^2.8.0)精准定位阻塞链,它不解决冲突但输出清晰的反向依赖路径,最上层即你的直接依赖,才是需调整的对象。

composer why-not 能直接告诉你谁在挡路
遇到 Your requirements could not be resolved 或 Conclusion: don't install xxx,别翻日志猜,先用 composer why-not 精准定位阻塞链。它不解决冲突,但能把几十行报错压缩成一条依赖路径。
必须带完整包名和版本约束,例如:
composer why-not monolog/monolog:^2.8.0
输出类似这样:
laravel/framework v9.52.5 requires nunomaduro/collision ^6.0 → nunomaduro/collision 6.4.0 requires phpunit/phpunit ^9.3
说明真正卡住的不是 monolog,而是 phpunit 的主版本不匹配。注意看最上层那个“你的直接依赖”,比如 laravel/framework —— 它才是你该调整的对象,不是底层传递依赖。
常见误区:
-
composer why-not monolog/monolog(缺版本号)→ 输出为空或报错 - 拼错包名,比如写成
monolog/monolong→ 找不到任何记录 - 忽略 PHP 版本约束,
why-not不检查环境兼容性,只查依赖图逻辑
对比 composer.lock 和 git 历史看真实变更点
composer.lock 是当前生效的依赖快照,但它不会告诉你“为什么是这个版本”。要定位问题源头,得结合 git 查看最近一次 composer.json 修改引入了什么变化。
执行这两条命令:
git diff HEAD~1 -- composer.json
git diff HEAD~1 -- composer.lock
重点看三类内容:
-
composer.json中新增或修改的require条目(尤其是带固定版本号如"foo/bar": "1.2.0"的写法) -
composer.lock中对应包的version字段是否被意外降级(比如从2.3.0回退到1.8.2) -
packages下某个包的source类型是否从dist变成了vcs,说明可能切到了 dev 分支
如果发现某次合并后 composer.lock 里某个关键包版本倒退了,大概率是分支 A 锁了旧版、分支 B 拉了新版,git 合并时选错了 lock 文件版本。
用 composer show -t 验证当前依赖树是否真有问题
composer show -t 显示的是 vendor 目录里**实际已安装**的依赖层级,不是理论推导结果。它能帮你确认:冲突是不是已经“落地”成运行时问题?
比如你怀疑 symfony/console 版本不对,运行:
composer show -t | grep -A 5 "symfony/console"
观察它的父节点是谁 —— 如果显示:
laravel/framework v9.52.5 → symfony/console v5.4.32
而你刚手动在 composer.json 里加了 "symfony/console": "^6.0",但 show -t 仍显示 v5.4,说明 composer update 根本没跑成功,或者被其他依赖强制压回了 v5。
这时候不要硬 push require,先用 composer why-not symfony/console:^6.0 查清谁在拦路。
别跳过 composer update --dry-run 这一步
所有修改 composer.json 后的操作,必须先 composer update --dry-run。它不改任何文件,只做一次完整解析,并告诉你哪里会失败。
尤其当你做了这些操作时:
- 手动改了某个包的版本号(比如把
"guzzlehttp/guzzle": "^7.2"改成"^7.5") - 删掉了
conflict字段想“试试看” - 刚 merge 了别人提交的
composer.json变更
--dry-run 会提前暴露冲突,比如输出:
Root requires guzzlehttp/guzzle ^7.5, but that version is not installable because it conflicts with your PHP version.
这比等 composer install 失败后再排查快得多。注意:它不检查扩展是否启用,只校验版本约束与 PHP 主版本号是否匹配。
真正容易被忽略的是:很多团队把 --dry-run 当成可选项,其实它是防止锁文件污染的第一道防线。一旦跳过,composer.lock 就可能写入半截失败的状态,后续 install 会更难恢复。











