答案是运行 composer why-not 'vendor/package:version' 定位阻塞链——它模拟安装失败,反向输出谁强制锁定了冲突版本,需写全包名和版本号,重点关注 requires 行及最短路径。

依赖冲突不是“找不到包”,而是多个版本约束无法同时满足;检测的关键不是看报错文字,而是让 Composer 主动暴露它卡在哪条约束上。
composer why-not 怎么用才定位到真正阻塞点
它不分析“可能的冲突”,只模拟安装失败时的反向依赖链,输出谁在强制锁死目标版本。
- 必须写全包名和版本号,格式为
composer why-not 'vendor/package:2.0.0'或composer why-not 'monolog/monolog:^3.5.0',冒号后不能有空格 - 如果只写
monolog/monolog:^3,很可能返回 “no problem found”,因为 ^3 可能兼容当前 lock 文件里的 3.4.0 - 输出中带
requires的行才是真实约束来源,比如laravel/framework v10.30.0 requires monolog/monolog ^2.0就说明 Laravel 10 拦住了你升到 monolog 3.x - 出现多条链时,优先看最短路径——也就是离你目标包最近的那一行,往往就是主因
composer show --tree 为什么看不到冲突但必须看
它不报错,但能验证“为什么明明写了 ^3.0,最终却装了 2.9.9”,帮你顺藤摸瓜找到间接引入的老版本。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 输出里某行末尾带
(locked to 2.9.9),说明这个版本已在composer.lock固定,删 lock 再 update 才可能松动 - 树太深时直接加
grep过滤,例如composer show --tree monolog/monolog | grep -A3 -B3 "guzzlehttp/guzzle" - 注意区分
requires和replaces:只有前者参与 solver 计算,后者只是元信息 - 别忽略
require-dev下的包,比如phpunit/phpunit对 PHP 版本或ext-json的强约束常被漏查
--verbose 输出太多?盯住这三类关键行
默认静默的回溯过程一旦打开,就变成调试流,不是用户日志。重点不是读全,而是快速抓线索。
- 以
Reading开头的行,确认 Composer 读的是你预期的项目根目录下的composer.json,不是父目录或全局配置 - 反复出现
Skipped package-name because it requires php ^8.1,说明瓶颈是 PHP 版本,不是包本身 - 最后几行出现
Trying rule: vendor/package == x.y.z后突然中断,那个x.y.z很可能就是 solver 卡死的版本点 - 别和
--dry-run混用——--dry-run跳过安装,--verbose在它下面依然有效,但日志量少很多,不利于定位
conflict 字段为什么查不到、也拦不住安装
它不是 solver 规则,只是作者写的兼容性提示,默认不参与依赖解析,也不阻止 composer install 成功。
-
composer show默认不显示conflict字段,得加-s参数:composer show -s vendor/package - 即使包里写了
"conflict": {"php": "8.3.0"},你在 PHP 8.3.0 环境下照样能install成功 - 真正起作用的是
require和require-dev中的版本约束,solver 全程围绕它们做图遍历 - 想让它生效?只能靠外部机制,比如 CI 中加
composer validate --strict,但它仍不校验 conflict 内容是否合理
真正难处理的从来不是报错本身,而是冲突藏在 require-dev 里、锁在 composer.lock 某个深层节点、或者由平台配置(如 config.platform.php)悄悄改写了 solver 的判断逻辑。










