composer why-not 是唯一能逆向定位依赖冲突的命令,从目标版本回溯约束链直至首个拒绝包,需写全包名和版本号,配合 --dry-run -v 和 composer show -t 可精准定位隐藏冲突源。

直接运行 composer why-not vendor/package:version 查阻塞链
报错里出现 Conclusion: don't install monolog/monolog:2.9.0 时,别翻日志堆栈,composer why-not 是唯一能逆向定位“谁在拦路”的命令。它从目标版本出发,一层层往上回溯依赖约束,直到找到第一个拒绝该版本的包。
必须写全包名和版本号,例如:composer why-not monolog/monolog:^2.0 或 composer why-not guzzlehttp/guzzle:8.0.0。常见错误包括:
- 只写
monolog/monolog不带版本 → 报[InvalidArgumentException] Package not found - 目标包还没写进
composer.json的require或require-dev→ 命令无输出,因为 Composer 认为“你根本没想装它” - 忽略
require-dev中的包 → 它们全程参与解析,比如phpunit/phpunit可能悄悄要求 PHP 8.2+
用 composer show -t vendor/package 验证实际装了谁、被谁拉入
composer.json 是你的愿望清单,composer show -t 才是当前 vendor/ 和 composer.lock 的真实快照。它能暴露你没意识到的间接路径,比如某行末尾标着 (locked to 2.9.9) 或 by laravel/framework[10.48.5]。
关键要看树中每层的约束表达式是否真有交集:
- 看到
symfony/console ^5.4和symfony/console ^6.0同时存在 → 冲突根源就在这里 - 某包被多个父包引入且版本不同(如
guzzlehttp/guzzle[7.8.1]和guzzlehttp/guzzle[8.0.0])→ Composer 尝试合并失败就会报错 - 输出中出现
(replaced)或(provided)→ 要去那个包自己的composer.json看它到底替代了什么,是否真能覆盖
加 -v 和 --dry-run 让 Composer 摊开推理过程
composer update --dry-run -v 不改任何文件,但会完整走一遍 SAT 求解器的推理流程,最后几行会明确写出冲突源头。重点盯住含 because 的嵌套行,例如:
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- Root composer.json requires laravel/framework ^10.0 -> satisfiable by laravel/framework[10.0.0, ..., 10.48.5]. - laravel/framework 10.48.5 requires php ^8.1 -> your PHP version (8.0.30) does not satisfy that requirement.
这种信息在普通 install 错误里常被折叠掉。输出太长时,直接翻到最后 10 行,反复出现的包名(如 monolog/monolog、symfony/console)大概率是冲突枢纽。
如果只想验证某次改动是否真能解出路径,用 composer require vendor/package:version --dry-run -v,不改任何文件就提前看到结果。
注意 require-dev 和平台配置才是隐藏最深的冲突源
很多冲突根本不在 require 里,而藏在测试或调试工具链中。比如:
-
phpunit/phpunit要求php ^8.1,但你的项目还卡在 PHP 8.0 -
spatie/laravel-ignition在require-dev中锁死了symfony/var-dumper的旧版,间接拖垮整个symfony生态 -
ext-intl或ext-json缺失,但某个包的composer.json明确写了"ext-intl": "*",Composer 会在解析阶段直接排除所有不满足的版本
验证方法:执行 composer update --no-dev,如果这时成功了,说明冲突源就在 require-dev 里;再配合 composer prohibits vendor/package 反查谁在强制要求旧版本。
真正卡住的地方,往往不是你写的那行 "laravel/framework": "^10.0",而是某个低版本包通过 conflict 字段锁死了 PHP 版本,或某个私有仓库的 composer.json 里硬写了 "monolog/monolog": "1.*" —— 这些都不会出现在 show -t 的默认输出里,得靠 why-not 和 --dry-run -v 把它们逼出来。










