composer show --tree --no-dev是唯一能清晰展示真实间接依赖路径的命令,它直接读取composer.lock或已安装包元数据,按缩进展开依赖链,缩进越深层级越低,可精准定位硬绑不稳定版本的“背锅”包。

composer show --tree --no-dev 是唯一能看清间接依赖路径的命令
报“Could not find package psr/log with stability dev”却没在 require 里写它,说明问题出在某层间接依赖硬绑了不稳定版本(比如 monolog/monolog 的 composer.json 里写了 "psr/log": "^3.0@dev")。这时候降级项目 minimum-stability 不是解法,只是掩盖问题。
composer show --tree --no-dev 直接读 composer.lock 或已安装包元数据,不解析、不安装、不猜测,只把真实依赖链按缩进展开。缩进越深,层级越低,越接近那个“背锅”的间接包。
- 必须在项目根目录运行,否则输出为空或报错
-
--no-dev要放在-t前面(composer show --no-dev -t .),否则被忽略 - 加具体包名聚焦:如
composer show --no-dev -t guzzlehttp/guzzle,比全量树快且易读 - 看到同一包在不同缩进出现?说明多路径引入,版本很可能被某一层硬锁死
composer prohibits 必须带完整版本号才能定位卡死源头
composer prohibits 是反向查“谁在拦路”的关键命令,但它不像 why 那样宽松——漏掉版本号就失效。
例如执行 composer prohibits laravel/framework:11.0.0,输出末尾的 (for spatie/laravel-backup v7.2.0) 就是你真正要调整的包,不是 laravel/framework 本身。这个括号里的信息就是修改入口。
- 不能写
^11.0或11.*,必须是精确版本如11.0.0,否则报[InvalidArgumentException] Package not found - 如果
prohibits没输出,问题大概率藏在require-dev里(比如phpunit/phpunit拖进旧版symfony/console) - 配合
composer update --dry-run -v可看到 SAT 求解器实际推演过程,确认冲突是否来自宽泛约束(如"^7.0 || ^8.0")
composer why --tree 和 composer show --who 配合确认引入来源
单靠 composer why psr/log 只能看到一级上游(比如 laravel/framework),但无法确认是不是你项目自己写的 require。要验证源头,得用更严格的命令。
composer why --tree psr/log 会一直展开到末尾出现 your-project-name dev-main 才算确认是根项目主动引入;而 composer show --who psr/log 扫描所有已安装包的 require 字段,结果更确定,尤其适合排查 provide 类虚拟依赖。
-
composer why --tree输出里没出现your-project-name?说明它是纯间接依赖,你没直接 require 它 -
composer show --who返回空,不代表没人用——可能是某个包通过provide: "psr/log-implementation"虚拟提供,此时应回头看composer show -t .全局结构 - 注意
composer depends对replace/provide支持差,容易漏判,别单独依赖它
conflict 字段是目前最可靠的手动干预方式
当上游包硬绑了你不想要的版本(比如强制拉 dev 版本的 psr/log),又没法立刻升级那个上游包时,conflict 是唯一兼容所有 Composer 版本(包括 2.x 和 3.x)、无副作用的硬控手段。
在 composer.json 的 conflict 字段里写明你要排除的包和版本范围,Composer 会在求解阶段直接跳过这些组合,避免进入无效回溯。
- 格式示例:
"conflict": {"psr/log": "=3.0.0-dev"} - 它不改变上游包声明,也不要求你 fork 或 patch,只是告诉 Solver:“这个组合不允许出现”
- 注意:不要盲目加
conflict,优先用composer prohibits确认真正卡死的版本和来源包
vendor 或重跑 install 解决不了。最耗时间的不是命令本身,而是看清哪一行缩进对应哪个包的哪条路径——终端宽度不够时,缩进错位会让判断失准,建议用 | less -S 或重定向到文件再查。











