composer why-not 是唯一能逆向定位版本冲突根源的命令,它从目标版本出发逐层回溯,输出最短阻塞链,最后一行是 root require,往上为间接阻塞源。

直接运行 composer why-not vendor/package:version,它会输出最短阻塞链,精准定位谁在封杀目标版本。 其他命令如 composer depends 或 composer show 只能辅助验证,不能替代这个逆向推导动作。
为什么 composer why-not 是唯一可靠入口
报错里出现 don’t install monolog/monolog:2.0.0 时,错误本身不告诉你“谁不让装”,只说“装不了”。composer why-not 是 Composer 唯一内置的逆向依赖解析命令,它从目标版本出发,一层层往上回溯,直到找到第一个拒绝该版本的约束点。
- 必须带完整包名和版本号,例如
composer why-not monolog/monolog:^2.0或composer why-not monolog/monolog:2.0.0;只写monolog/monolog会报[InvalidArgumentException] Package not found - 输出按从下到上读:最后一行是
Root package requires(即你composer.json里的声明),往上每行带(required by ...)的,就是间接阻塞源 - 如果输出为空,大概率是
require-dev里的包(比如phpunit/phpunit)悄悄拉高了 PHP 版本或子依赖要求,需加--no-dev再试
用 composer show --tree 验证真实装了什么
composer.json 是愿望清单,composer show --tree 才反映 vendor/ 和 composer.lock 里的真实快照。它能暴露你没意识到的钉死路径。
- 查某个包被谁拉入:
composer show --tree monolog/monolog,注意每行末尾括号里标出的来源,例如by laravel/framework[10.48.5] - 看到某行末尾标着
(locked to 2.9.9),说明这个版本已在composer.lock固定,删 lock 文件再composer update才可能松动 - 树太深时用
grep过滤:composer show --tree symfony/console | grep -A3 -B3 "guzzlehttp/guzzle",快速定位交叉依赖段
别被 composer depends 带偏方向
composer depends -r vendor/package 看的是“谁依赖它”,不是“谁在限制它”。它列出所有上游包,但不体现它们各自声明的版本约束范围——而这才是冲突的核心。
- 加
-r才递归查间接依赖,不加只显示直接require,容易漏掉深层拖后腿的组件 - 输出中重点看带
requires的行,例如laravel/framework v8.83.27 requires symfony/console (^5.4);注意^5.4和~5.4.0实际允许的版本范围不同,可能就是冲突点 - 如果想反向查“谁强制要求旧版”,用
composer prohibits vendor/package:^2.0更直接
真正卡住的往往不是你写的那行 "monolog/monolog": "^2.0",而是某条你没打开看过的依赖链里,一个二级包写了 "monolog/monolog": " 或锁死了 PHP 版本。每次运行 composer why-not 前,先确认目标版本确实已写进 composer.json ——否则它分析的是当前已满足的状态,不会报冲突。











