答案是直接运行composer why-not vendor/package:version定位阻塞链,不可删vendor或composer.lock;必须用完整版本号(如guzzlehttp/guzzle:8.0.0),输出倒序显示从根声明到冲突源的完整路径,空输出说明未声明或被conflict屏蔽。

直接运行 composer why-not 定位阻塞链,别删 vendor 或 composer.lock —— 这俩删了只会重跑失败逻辑,不解决任何问题。
看到 “don’t install xxx” 就立刻执行 composer why-not
报错里出现类似 don’t install guzzlehttp/guzzle:^8.0,马上执行:composer why-not guzzlehttp/guzzle:8.0.0(注意:必须用完整版本号,不能带 ^ 或 ~)
输出是倒序链路,从最后一行(你的 composer.json 根声明)往上读,每行末尾的 (required by package-a v1.2) 就是提出互斥约束的来源。
- 如果输出为空,大概率是没在
require里声明这个包,或者被conflict全局屏蔽了 - 特别注意
require-dev:比如phpunit/phpunit锁死sebastian/exporter,间接拖住symfony/console,但它不会出现在根声明里 - 末尾几行带
Root package requires php或ext-dom的,往往暴露的是 PHP 版本或扩展缺失,不是依赖本身的问题
用 composer show --tree 看真实依赖结构
composer.json 是愿望清单,composer.lock 和 vendor/ 才是事实。运行 composer show --tree 能暴露你根本没意识到的间接路径。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 查某个包被谁引入:
composer show --tree | grep "symfony/console",注意是否标着(locked to 5.4.42) - 过滤关键路径:
composer show --tree monolog/monolog | grep -A5 -B5 "guzzlehttp/guzzle",确认是不是某条子依赖在强制降级 - 看到
(replaced)或(provided)?得去那个包自己的composer.json查replace字段,确认是否真能等价替代
升级或降级单个包必须加 --with-dependencies
不加这个参数,composer update vendor/package 默认拒绝更新其子依赖,哪怕新版本根本跑不起来。
- 升版本:
composer update monolog/monolog --with-dependencies - 跨主版本降级(如从
guzzlehttp/guzzle:^8.0切回7.4.5):必须先在composer.json里写死"guzzlehttp/guzzle": "7.4.5",再执行composer update guzzlehttp/guzzle --with-dependencies - 错误写法:
composer update "monolog/monolog:^3"—— 引号 +^会让 Composer 自己选“最新兼容版”,不是你要的3.0.0 - 执行后立刻
git diff composer.lock,只接受目标包及其直系依赖变更;如果改了十几个包,说明约束没控住,得回退
最常被忽略的一点:冲突未必来自生产依赖,require-dev 里的测试工具链(比如 phpunit/phpunit、mockery/mockery)经常悄悄锁死底层组件,导致主依赖无法升级。查的时候一定得把 dev 部分也纳入分析范围。










