Composer 因依赖版本无交集而拒绝安装,如 psr/log "^1.0" 与 "^3.0" 冲突;用 composer why-not psr/log:^2.0 定位阻断包,再通过 composer why 追查源头,优先升级或替换已废弃包。

Composer 无法让两个包共存于互斥的同一依赖版本范围中,必须找到有交集的版本区间,或调整约束、替换包、升级上游依赖。
为什么 composer update 卡在 “Conclusion: don’t install”
这不是网络或缓存问题,是 Composer 明确告诉你:A 包要求 psr/log: "^1.0",B 包要求 psr/log: "^3.0",这两个范围没有重叠 —— 它不会降级 A 或升级 B 去“凑合”,只会拒绝安装。
- 错误里出现
don't install或Your requirements could not be resolved,基本就是版本范围无交集 - 常见于老项目引入新包(如 Laravel 10 + 旧 SDK),或 dev 包(如
phpstan/phpstan)悄悄锁死了主依赖 - PHP 版本不匹配也会触发类似报错,但本质不同:它属于平台约束冲突,不是依赖逻辑冲突
用 composer why-not 直接定位谁在卡死版本
别猜哪个包有问题,直接问 Composer:
composer why-not psr/log:^2.0
输出会类似:
myapp/myproject dev-main requires monolog/monolog (^1.25) monolog/monolog 1.25.0 requires psr/log (^1.0) laravel/framework v10.32.0 requires psr/log (^2.0 || ^3.0)
这说明 monolog/monolog 是阻断点。再顺查:
composer why monolog/monolog
就能看到是哪个私有 SDK 或旧工具包把它钉在了 1.x。
-
composer why-not必须带具体版本号(如^2.0),否则返回空 - 如果目标包没出现在
why-not结果里,说明它根本没被任何已装包显式 require,可能是require-dev引入的“幽灵依赖” - 搭配
composer show --tree看完整路径,注意是否出现package-a → package-b → package-a这类闭环
怎么改 composer.json 才真正有效
改版本不是越宽越好,也不是越新越稳,关键看交集是否存在、代码是否兼容:
- 先去 Packagist 查
psr/log的所有发布版本,确认哪些版本同时被上下游接受(比如 v2.0.0 可能被 A 的^1.25和 B 的^2.0同时接受) - 放宽约束示例:
"monolog/monolog": "^1.25 || ^2.10"—— 但前提是你的代码真能跑通两个大版本,否则只是把问题延后到运行时 - 收紧约束更安全:
"monolog/monolog": "2.9.2"(选一个已知稳定、且被双方接受的中间版),然后只更新它:composer update monolog/monolog - 根项目里写死
"psr/log": "1.1.4"是最危险操作:它会强制所有子依赖降级,可能破坏新包功能
什么时候该换包,而不是硬调版本
有些冲突没法靠改数字解决,尤其当一方已停止维护:
- 如果冲突来自
aws/aws-sdk-php:2.x这类明确废弃的包,别花时间调guzzlehttp/guzzle版本,直接换成async-aws/core或升级到aws/aws-sdk-php:3.x - 私有包(如
acme/payment-sdk)是你能改的,优先放开它的require字段,比如把"guzzlehttp/guzzle": "^6.5"改成"guzzlehttp/guzzle": "^6.5 || ^7.0 || ^8.0" - 用
"replace": { "old-sdk": "*" }可以跳过冲突包,但必须确保你提供的实现完全覆盖其接口,否则运行时报Class not found -
"conflict": { "old-sdk": ">=1.0.0" }更适合强互斥场景,但它不会自动卸载旧包,得先composer remove old-sdk再装新包
真正难处理的不是报错本身,而是某个包在 composer.lock 里锁着旧版本,而它的上游又悄悄发布了不兼容的新版 —— 这时候 composer update --dry-run 输出里的 downgrading 行就是唯一预警信号。











