composer why-not 直接倒查阻断目标版本的依赖,输出缩进最深者常为真正卡点;prohibits 则扫描全图主动探雷;--dry-run -v 可暴露求解器真实瓶颈,锁定中间版本比盲目升级更可控。

composer why-not 能直接告诉你谁在封杀目标版本
它不猜测、不绕弯,只做一件事:从你想装的包出发,一层层倒查哪些已安装包的 require 字段正在阻止该版本落地。比如报错说装不上 monolog/monolog:2.10.0,运行 composer why-not "monolog/monolog:2.10.0",输出里缩进最深的那一行,往往就是真正卡住你的私有包或旧分支。
常见误判是看到 Root package 就以为是自己写错了约束——其实可能是 conflict 字段在起作用,或者 config.platform.php 锁死了 PHP 版本,导致远程包元数据根本没被纳入候选集。
- 必须带引号和完整版本约束,例如
"phpunit/phpunit:^10.0",否则命令静默退出 - 即使目标包当前未安装,也能查;但若输出为空,说明冲突不在依赖链,而在平台环境或语法错误
- 如果输出中出现
dev-main或dev-fix-branch,立刻检查repositories配置和minimum-stability设置
composer prohibits 比 why-not 更早暴露阻断源头
composer prohibits 不关心你“想装什么”,而是扫描整个依赖图,列出所有明确声明排斥目标版本的包。它适合在更新前主动探雷:比如准备升 laravel/framework 到 v11,先跑 composer prohibits "laravel/framework:11.0",就能提前看到 spatie/laravel-backup 或 orchestra/testbench 这类扩展是否还在锁死旧版。
它和 composer depends 是互补关系:depends 告诉你“谁用了它”,prohibits 告诉你“谁不让它进来”。两者合起来,能快速区分问题是出在“下游依赖太老”,还是“上游扩展太激进”。
- 输出里如果某包版本带
~或^,说明它的约束本身是弹性的,问题可能出在其他更硬的限制上 - 若多个包都禁止同一版本,优先处理 require-dev 里的工具链包——它们常因测试兼容性而滞后于主框架
- 别忽略输出中带
(by your requirements)的行,那是你自己的composer.json在作祟
用 --dry-run + -v 看清 Composer 解析器的真实卡点
当 composer update 卡在 Resolving dependencies 超过 3 分钟,不是网络慢,是 SAT 求解器在暴力试错。此时加 --dry-run -v 能让日志吐出真实瓶颈:比如反复出现 Trying monolog/monolog 1.25.0 → failed,就说明这个版本被反复验证又否决,大概率是某个包把它钉死了。
这种日志比报错信息更原始,也更准——它不包装结论,只记录求解器每一步尝试。重点关注 “Root composer.json requires” 和 “conflicts with” 同时出现的行,那里就是约束交集断裂的位置。
- 不要在 CI 或部署脚本里直接跑
update,install才是上线标准动作 - 若日志里大量出现
Retrying或Backtracking,说明依赖树太复杂,建议分批执行composer update vendorA/pkgA --with-dependencies -
--dry-run不改任何文件,但会读取并验证composer.lock,所以它是安全的诊断入口
锁定中间版本比盲目升级更可控
找到冲突包后,别急着全量升级或降级。去 Packagist 查那个包的发布历史,找一个同时出现在多个依赖 require 列表里的版本,比如 guzzlehttp/guzzle 的 7.8.1 可能既满足 A 包的 ^7.2,又满足 B 包的 >=7.4.0。把它写死在 composer.json 的 require 里,再只跑 composer update guzzlehttp/guzzle。
这样做的好处是:不牵连其他包,不触发意外的主版本跃迁,也不需要改 conflict 或 replace 这类高风险字段。真正的麻烦往往不在第一层依赖,而在你半年前为临时调试加上的那个 "foo/bar": "dev-fix-branch",至今还钉在 composer.json 里没清理。
- 写死版本后务必执行
composer update vendor/package,而不是require --no-update,后者只改 JSON,不更新 lock 文件 - 如果多个包对同一依赖提出互斥大版本(如
^1.0vs^2.0),先确认你的代码是否真能兼容两套 API,再决定是否用"package": "^1.0 || ^2.0" - 别信
composer outdated显示的“可升级”,它不验证兼容性,只比对最新 tag —— 很多 BC BREAK 就藏在 patch 版本里


















