Composer依赖冲突本质是SAT求解器证明版本约束无解,如monolog:^1.0与^2.0交集为空;用composer why-not可定位阻塞链,require-dev也参与解析,lock文件不保证下次安装成功。

依赖树冲突的本质是版本约束无解
Composer install 报错,根本不是因为“包太多”或“网络慢”,而是它在用 SAT 求解器做逻辑推演时发现:**没有任何一个版本组合能同时满足所有 require 字段声明的约束条件**。比如 monolog/monolog:^1.0 和 monolog/monolog:^2.0 在语义化版本规则下完全不重叠,求解器直接判定无解,于是终止安装并抛出 Your requirements could not be resolved。
composer why-not 是唯一能定位“谁在卡死”的命令
报错里出现的 Conclusion: don't install xxx v2.3.0 只是结果,不是原因。真正要查的是“谁不让装”。
-
composer why-not monolog/monolog:2.3.0会列出所有阻止该版本的依赖链,从顶层your-project开始逐级向下,明确指出哪个包写了"requires": "monolog/monolog:1.26.1" -
composer prohibits monolog/monolog:2.3.0补充显示哪些包显式声明了conflict或通过replace排除了这个版本 - 注意:
composer depends monolog/monolog只告诉你“谁用了它”,但不体现版本限制,容易误判
require-dev 也会参与解析,这点常被忽略
即使你只运行 composer install(不带 --dev),require-dev 中的包仍会被 Composer 加入依赖图参与版本计算——只要它们没被 config.platform 或 platform-check 屏蔽。
- 常见陷阱:本地装了
phpunit/phpunit:^10.0,但它要求php: ^8.1,而你项目锁定了"php": "7.4.33",冲突就发生在解析阶段 - 验证方式:删掉
require-dev区块再试composer install --dry-run,如果成功,说明问题出在 dev 包 - 临时绕过:加
--no-dev参数可跳过 dev 包解析,但这只是诊断手段,不能作为长期方案
lock 文件存在 ≠ 依赖树稳定
composer.lock 记录的是上一次成功解析出的版本组合,但它不保证下次 install 一定成功——环境变了,解析结果就可能变。
- PHP 版本升级后,某些包的
require php约束会激活新分支,导致旧 lock 文件里的版本不再被允许 - packagist.org 上某个包的 tag 被删除或重写(如强制 force-push),会导致
dist.url失效,Composer 拒绝安装 - 最稳妥的做法是:遇到 install 失败,先跑
composer update --dry-run -v,看它是否能在当前环境下重新算出可行解;失败再查why-not,而不是直接删 lock


















