依赖冲突源于 composer.json 中版本约束互斥,需用 composer why-not 定位阻塞链,结合 composer show --tree 查真实依赖结构,升级单个包须带 --with-dependencies,镜像配置需正确且清缓存。

依赖冲突不是网络或缓存问题,是 composer.json 里写的约束互相打架,删 vendor 或 lock 文件只会重演失败过程。
用 composer why-not 定位阻塞链
报错里出现 don’t install xxx,别猜,直接查谁在拦路。命令必须带完整版本号,比如 composer why-not guzzlehttp/guzzle:^8.0,输出是从底往上读的反向链:最后一行是你 composer.json 的根声明,往上逐行看哪条 (required by) 提出了互斥约束,那就是冲突源头。
- 输出为空?先检查
require-dev——很多冲突来自phpunit/phpunit或mockery/mockery拖着老版sebastian/exporter,间接锁死symfony/console - 写
^8或8可能匹配不到元数据,必须写guzzlehttp/guzzle:8.0.0或guzzlehttp/guzzle:^8.0.0 - 加
--dry-run验证改动是否真能解出路径,例如composer require laravel/framework:11.0.0 --dry-run,不改任何文件就提前看到结果
用 composer show --tree 看真实依赖结构
composer.json 是你写的“愿望清单”,composer show --tree 才是 vendor/ 和 lock 文件里的真实快照。它能暴露你根本没意识到的间接路径,比如某个包标着 (locked to 5.4.42) 或 (replaced)。
- 查某个包被谁引入:
composer show --tree | grep "symfony/console" - 过滤关键段:
composer show --tree monolog/monolog | grep -A5 -B5 "guzzlehttp/guzzle" - 看到
(provided)或(replaced),得去那个包自己的composer.json里确认它到底替代了什么、是否真能覆盖
定点更新,别全量 composer update
全量 composer update 容易触发 SAT 求解器卡死,尤其依赖多的项目。升级单个包必须带 --with-dependencies,否则 Composer 默认拒绝更新其子依赖,哪怕新版本根本跑不起来。
- 正确写法:
composer update monolog/monolog --with-dependencies—— 只动它和直系依赖 - 错误写法:
composer update "monolog/monolog:^3"—— 引号 +^会让 Composer 自己找“最新兼容版”,不是你要的3.0.0 - 降级跨主版本(如从
guzzlehttp/guzzle:^8.0切回7.4.5),必须在composer.json里写死:"guzzlehttp/guzzle": "7.4.5",不能只写"^7.0" - 执行后立刻
git diff composer.lock,确认只有目标包及其直系依赖被改,没波及其他
镜像源和缓存不是万能解药,但必须先配对再排查
中文镜像不能解决依赖冲突,只让冲突暴露得更快、更准。卡在 Resolving dependencies through SAT 是本地穷举所致,换镜像不会跳过该过程,但能帮你快速定位谁拦住了安装。
- 镜像生效三要素:
repo.packagist(单数)、末尾带/、类型写composer,正确命令:composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ - 配完必须清缓存:
composer clear-cache;若怀疑 provider 分片同步滞后,加--refresh:composer update --refresh - 只要
composer.json里有"repositories"字段(哪怕空数组),全局镜像就会被丢弃——不是合并,是直接忽略 - 验证是否生效:
composer config -g repo.packagist输出应为完整 URL 字符串;composer show packagist/support的source.url应含mirrors.aliyun.com
真正难处理的从来不是版本号本身,而是 conflict 字段的硬性拦截、autoload 带来的隐式循环、或者私有包悄悄升级了依赖要求却没同步文档。这些点一旦漏掉,why-not 和 show --tree 都可能给你一个看似“无冲突”的假象。











