定位依赖冲突需先用 composer why-not 查反向依赖链,重点检查带 your-project 的行和 conflict 声明;改 composer.json 前须实测兼容性,更新必须用 composer update 加约束参数并配合 --dry-run 验证。

直接改 composer.json 的版本号不是万能解药,多数时候反而让 Composer 更难算出解——关键得先知道谁在拦路、为什么拦,再决定改哪一行、怎么改。
用 composer why-not 定位真正卡住的包
报错里出现 Your requirements could not be resolved 或 don't install xxx 时,别急着打开 composer.json。先运行:
-
composer why-not vendor/package:version(必须带具体版本,比如guzzlehttp/guzzle:^8.0.0) - 输出是反向依赖链:最后一行是你项目根的
require,往上逐行看谁提出了互斥约束 - 重点盯住带
your-project的那一行——那是你能改的地方;如果某行写着conflict,说明不是误配,是作者明确拒绝共存 - 输出为空?检查
require-dev,很多冲突来自phpunit/phpunit或mockery/mockery拖着老版sebastian/exporter
改 require 字段前先确认兼容性边界
手动编辑只在以下情况有效,否则大概率引入新问题:
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 你已实测过某个版本能跑通,但当前锁的是旧版(例如
monolog/monolog实际用3.5.0没问题,但锁在3.4.0) - 要加
conflict阻止已知不兼容版本(如"conflict": {"laravel/framework": "10.25.0"},因该版有路由解析 bug) - 仅放宽
require-dev中工具类包(如phpunit/phpunit),避免它间接拉低主依赖版本 - 切勿删
require里的生产依赖“腾位置”,这会导致运行时Class not found - 别把
^1.0改成*——等于放弃语义化版本控制,下次update可能引入破坏性变更
更新必须用 composer update 加约束参数
改完 composer.json 后,composer install 不会重新求解,它只按 composer.lock 装。必须用:
-
composer update vendor/package --with-dependencies:只更新目标包及其直系依赖,避免全量升级引发连锁不兼容 - 不确定是否真能解?先加
--dry-run看计划,或加-v输出详细冲突路径 - 极端调试才用
--ignore-platform-reqs,之后务必验证 PHP 扩展和版本是否真兼容 - 每次
update都重写composer.lock,提交前用git diff composer.lock核对,确保没波及其他无关包
别迷信 ||,交集比逻辑或更重要
把 "monolog/monolog": "^1.0 || ^2.0" 写进 composer.json 是常见误区:
-
||不表示“动态适配”,只是逻辑或;Composer 仍需选出**唯一确定的版本**,而^1.0和^2.0在 SemVer 下无交集,求解器依然失败 - 真正有效的写法是收敛到最小可行交集:去 Packagist 查
composer show monolog/monolog --all,看哪些版本同时出现在冲突包的require列表中 - 如果必须跨主版本共存,用
replace声明替代关系(如"replace": {"monolog/monolog": "2.9.3"}),但这是高危操作,需确保功能完全实现并经充分验证
最常被忽略的一点:冲突往往不在你写的 require 里,而在某个间接依赖的 conflict 字段、平台要求(PHP 版本/扩展缺失)、或 composer.lock 锁定的旧版本残留——所以 why-not 必须带版本号查,show --tree 必须看真实快照,而不是只盯着自己写的那几行。

















