Composer不自动检测冲突,需用composer why-not(须带完整版本号)、composer show --tree(看locked to及依赖路径)和composer depends -r(递归查间接约束)三命令组合定位阻塞链路。

Composer 本身不“自动检测”冲突链路,它只在安装或更新失败时暴露冲突;所谓“自动检测”,其实是用几个内置命令组合还原出阻塞路径——核心就三个命令:composer why-not、composer show --tree、composer depends -r。
为什么 composer why-not 必须带完整版本号
它不是查“有没有冲突”,而是模拟一次失败的安装:只有明确告诉 Composer “我想装 monolog/monolog:2.0.0”,它才去反向遍历依赖图,找出第一个不兼容节点。写 monolog/monolog:^2.0 或 monolog/monolog:2.* 很可能匹配不到元数据,返回 [InvalidArgumentException] Package not found。
- 正确写法:
composer why-not monolog/monolog:2.0.0 - 输出是倒序的:最后一行是你
composer.json里的声明,往上逐行看哪条required by提出了互斥约束(比如laravel/framework:v8.0要求monolog/monolog:^1.0) - 如果输出为空,先检查
require-dev—— 大量冲突来自phpunit/phpunit或mockery/mockery锁死了底层组件
composer show --tree 看到的 (locked to X.Y.Z) 是什么
这表示该版本已被 composer.lock 固定,不是当前 composer.json 的约束决定的。你改了 composer.json 但没删 lock 文件,Composer 仍会优先复用锁定版本。
- 想验证真实依赖结构,运行:
composer show --tree monolog/monolog | grep -A5 -B5 "guzzlehttp/guzzle" - 看到
(provided)或(replaced)?得去那个包自己的composer.json查它到底提供/替换了什么 - 树太深时别硬读,用
grep过滤关键路径,比如:composer show --tree | grep "symfony/console"
composer depends -r 才能揪出“隐形拖后腿”的包
composer depends 不加 -r 只显示直接依赖;加了才递归查 transitive dependencies,这才是冲突常藏身的地方。
- 查谁锁死了
symfony/console:composer depends -r symfony/console - 重点看带
requires的行,忽略provides和replaces - 注意约束符号差异:
^4.4允许 4.4.0–4.4.99,~4.4.0只允许 4.4.0–4.4.99 —— 表面一样,实际范围不同,就是冲突点 - 平台配置也参与解析:
config.platform.php伪造的 PHP 版本会影响 solver 对require的判断
真正容易被忽略的是:冲突往往不在你写的 require 里,而在 require-dev 或某个二级依赖的 conflict 字段里——后者虽不参与 solver,但会在 composer validate --strict 或 CI 中触发警告。删 vendor、清缓存、重跑 install 都不能绕过这个逻辑,必须从依赖图里一层层翻上去。











