Composer依赖树深度冲突须用composer show -t查看真实已解析结构,再以composer prohibits精准定位卡死目标版本的包;show -t反映lock中实际安装关系,prohibits需带完整版本号(如laravel/framework:11.0.0),末尾标注的(for spatie/laravel-backup v7.2.0)即为需调整对象。

Composer依赖树深度冲突不能靠删vendor或composer.lock解决,必须用composer show -t看清真实已解析结构,再用composer prohibits反向定位谁在卡死目标版本。
composer show -t 看到的才是真实依赖链,不是理想状态
composer show -t输出的是composer.lock里已成功解析并锁定的依赖关系,它反映的是当前项目“实际装了什么”,而不是“理论上该装什么”。很多冲突藏在第二、三层间接依赖里——你没在composer.json里写symfony/console,但它被guzzlehttp/guzzle和phpunit/phpunit分别以不同版本拉进来,show -t会把这两条路径都摊开。
- 每行末尾的
(locked to 5.4.42)表示该版本已被锁死,想松动必须删composer.lock或改约束后重跑update - 如果某包出现两次且版本不同(如
monolog/monolog一次是^1.25、一次是^2.10),说明上游有不兼容分支,得往上翻是谁引入的 - 看到
(replaced)或(provided)标记,要立刻查对应包的composer.json,确认是否真能等价替代
composer prohibits 比 why-not 更准,但必须带完整版本号
composer why-not只查composer.json里写了但没装上的包;而composer prohibits是从你想装却装不上的那个具体版本出发,一层层翻出所有拦路的约束。比如执行composer prohibits laravel/framework:11.0.0,输出里会明确写出spatie/laravel-backup v7.2.0 requires symfony/console ^5.4——这才是真正卡死你的源头。
- 必须写完整版本号,如
laravel/framework:11.0.0,不能写^11.0,否则报[InvalidArgumentException] Package not found - 输出每行末尾的
(for spatie/laravel-backup v7.2.0)就是你要动手调的包,不是laravel/framework本身 - 如果
prohibits没输出,说明问题不在已安装包,而是require-dev里的工具(比如phpunit/phpunit拖进旧版symfony/console)
composer update --dry-run -v 是唯一能看清“谁在拉扯谁”的方式
这个命令不改任何文件,但会把 Composer 的 SAT 求解器推演过程完整打印出来。重点盯三处:
- 日志开头的
Root requirements段落:告诉你哪一行"vendor/name": "^x.y"是整场冲突的起点 - 反复出现的
Because ... requires ...链:比如Because package-a v2.1 requires monolog/monolog ^1.25, and package-b v3.0 requires monolog/monolog ^2.10——这就是无交集的铁证 - 末尾的
Found conflicting requirements:列出具体冲突包名和版本范围,它们才是你要动手调的对象
没加-v参数,你只看到“无法解析”;加了,才能看见“谁在拉扯谁”。
改完 composer.json 后,必须用 update 命令触发重解析
手动编辑composer.json里的版本号(比如把"guzzlehttp/guzzle": "^6.5"改成"^7.2")只是改了约束声明,不会自动生效。Composer 默认走“最小变更”策略:只要已安装版本满足新约束,就不会动。
- 执行
composer update guzzlehttp/guzzle才会触发对该包及其直接依赖的重解析 - 如果冲突卡在子依赖上,必须加
--with-dependencies:composer update guzzlehttp/guzzle --with-dependencies - 想只更新
composer.lock不重装vendor/,用composer update --lock
最常被忽略的一点:有些包在composer.json里写了"conflict": {"laravel/framework": ">=11.0"},哪怕你没require Laravel 11,只要其他依赖间接拉进来了,就会爆错——这时show -t里会出现laravel/framework,但你根本没主动引入它。











