答案是先运行composer why-not定位阻断链——它从根依赖逆推,逐层显示谁锁定了冲突版本,最上面一行通常是composer.json中过窄的手动约束或不兼容的中间包。

Composer依赖冲突不是“装不上”,而是它明确告诉你:当前所有约束条件没有交集——你写的、别人写的、平台要求的,三者叠加后无解。
composer why-not 为什么必须第一个跑
报错里出现 Conclusion: don't install laravel/framework v10.32.0,这不是终点,是起点。它只说“不装”,没说“谁不让装”。composer why-not laravel/framework:10.32.0 才会逐层输出阻断链,例如:
-
Root package requires laravel/framework ^9.0—— 你手动锁死了大版本 -
spatie/laravel-backup v7.5.0 requires laravel/framework ^9.0—— 它还没适配 L10 -
orchestra/testbench v7.2.0 requires laravel/framework ^9.0—— 测试工具也卡在旧版
最上面那行 Root package requires 就是你该改的地方;如果全是下游包,说明得等它们发新版,或临时切到兼容分支(比如 spatie/laravel-backup:dev-main),但别直接提交 dev 分支到生产。
composer update 单包时为何反而更糟
执行 composer update laravel/framework 看似精准,实则危险:Composer 会以它为根,重新计算整个子图,可能激活早已被压制的宽松约束(如某个老包声明 "monolog/monolog": "^1.0 || ^2.0"),结果回退到 1.26.1 而非升到 2.9.3。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 正确做法是加
--with-dependencies:composer update laravel/framework --with-dependencies - 加
--dry-run预览改动范围,确认只动了预期包及其直系依赖 - 升级后立刻
git diff composer.lock,如果看到guzzlehttp/guzzle从7.8.1变成8.0.1,说明约束没控住,得回退并检查是否漏写了"guzzlehttp/guzzle": "^7.0"在 require 里
require-dev 是隐形冲突源
很多冲突根本不在 require 区,而在 require-dev。比如你主项目用 laravel/framework: ^10.0,但 phpunit/phpunit: ^10.0 要求 php: >=8.1,而你本地 PHP 是 8.0 —— composer install 就会失败,哪怕你根本没跑测试。
- 运行
composer show --tree | grep -A5 -B5 phpunit查它怎么被拉进来的 - 临时移除
require-dev块再试composer install,确认是否 dev 包引发 - 开发机 PHP 版本低于项目要求?别用
--ignore-platform-reqs,改用指定二进制:/usr/bin/php8.1 composer install
锁定版本 ≠ 写死字符串
把 "monolog/monolog": "2.9.3" 直接写进 composer.json 看似稳妥,但下次别人 composer update 时,只要没显式指定包名,这个版本就会被忽略——Composer 只认 require 里的约束,不认 lock 文件里的快照。
- 真要锁定,就用语义化范围:
"monolog/monolog": "^2.9.3"或"monolog/monolog": "~2.9.0" - 多个包对同一依赖有矛盾要求?查 Packagist 上哪些版本同时被双方接受,比如
v2.9.2出现在 A 包的^2.0和 B 包的>=2.8.0里,那就手动设为"monolog/monolog": "2.9.2",再composer update monolog/monolog - 别在
conflict字段里写“提醒性”内容,比如"conflict": {"laravel/framework": ">=11.0"}—— 只要任何已装包间接引入 L11,就会触发,哪怕你根本没 require 它
最常被忽略的一点:composer.lock 不是缓存,是契约。每次 composer update 后,它必须和 composer.json 一致,且所有团队成员必须用同一份 lock 文件。手改 JSON、跳过 lock、或不同 PHP 版本生成 lock,都会让“本地能跑,CI 报错”成为常态。










