composer 依赖冲突本质是约束不一致,需用 composer why-not 定位阻塞源,优先精准更新、检查 changelog 和私有源配置,而非直接删 lock 文件。

composer install 报错:“Conclusion: don’t install packageA”
这是 Composer 依赖解析失败最典型的提示,本质是锁文件(composer.lock)里记录的版本与当前 composer.json 中声明的约束冲突,或者多个包对同一依赖提出了互斥要求。
别急着删 lock 文件重装——它本意是保证环境一致性。先用 composer why-not <code>packageX 查清谁在阻止安装,比如:
composer why-not guzzlehttp/guzzle:^8.0会列出所有间接依赖中强制要求
guzzlehttp/guzzle 必须是 ^7.0 的包。
- 如果只是本地开发想快速验证,可临时加
--ignore-platform-reqs跳过 PHP 扩展或版本检查(但上线前必须去掉) - 若报错涉及
php版本不匹配,检查config.platform.php是否被硬编码成旧版本 - 某些包(如
laravel/framework)会通过conflict字段主动拒绝其他包的特定版本,得去它的composer.json里确认
require 和 require-dev 冲突时怎么破?
require-dev 里的包也会参与依赖解析,哪怕只在测试时用。常见陷阱是:生产环境跑 composer install --no-dev 没问题,但 CI 流水线里执行 composer install 却失败——因为某个 require-dev 包(比如 phpunit/phpunit)拉了和主项目不兼容的 symfony/console。
- 用
composer show --tree <code>vendor/package看清依赖树层级,定位是哪个 dev 包带偏了 - 如果确定某 dev 包只用于本地,可改用
require+autoload-dev分离逻辑,而非直接写进require-dev - 升级
phpunit前务必查它对php和ext-json的真实要求——有些小版本会悄悄提高最低 PHP 版本
composer update 更新后功能异常,但没报错
没报错 ≠ 没问题。composer update 默认更新所有包到满足约束的最新版,可能触发语义化版本中的“次要更新”(minor),而某些包的 minor 版本实际包含破坏性变更(比如 monolog/monolog v2 → v3 的 handler 接口变动)。
- 永远优先用
composer update <code>vendor/package精准更新,而不是全量update - 更新前先看 CHANGELOG:很多包把 breaking change 写在 GitHub Release 的 “Breaking Changes” 小节,不是 README 里
- 如果用了
^版本约束(如"guzzlehttp/guzzle": "^7.0"),Composer 可能升到 7.9,但某些中间件行为在 7.5+ 已静默变更,得翻 PR 记录
私有包和 packagist.org 同名包冲突
当你的私有 Git 仓库也叫 myorg/utils,而 Packagist 上恰好有个同名废弃包,Composer 可能优先拉错源,尤其当你没配 repositories 顺序时。
- 在
composer.json顶部明确定义私有源,并设"packagist.org": false关闭默认源(慎用,需确保所有依赖都可在私有源或自定义源找到) - 用
composer config repositories.myorg vcs https://git.example.com/myorg/utils.git注册后,再require时加上-vvv看 Composer 实际从哪下载 - 私有包的
version字段必须和 tag 严格一致;如果打的是v1.2.3tag,但composer.json里写"version": "1.2.3",Composer 会认为这是不同包
依赖冲突从来不是“能不能装上”的问题,而是“谁该听谁的”——Composer 的 resolver 会穷举所有组合,但你的 composer.json 约束、lock 文件快照、平台配置、甚至 Git tag 格式,都在暗中投票。稍有不一致,它就卡住不动。










