缩进代表依赖层级,每级缩进表示直接子依赖关系。例如guzzlehttp/guzzle下缩进出现psr/http-client,说明Guzzle明确声明了对该包的require;同一包在不同缩进深度重复出现,通常表明被多个上游包分别引入,版本不一致时易引发冲突。

composer show --tree 输出的缩进到底代表什么
缩进就是依赖层级,不是美观排版。每多两个空格(或一个制表符),就表示下一层是上一层的直接子依赖。比如 guzzlehttp/guzzle 下缩进出现 psr/http-client,说明 Guzzle 的 composer.json 里明确定义了对它的 require;如果同一包在不同缩进深度重复出现(如一次在 2 级、一次在 4 级),大概率是被多个上游包分别拉入,版本不一致时就容易触发冲突。
常见误读:把缩进当成“重要性”或“加载顺序”。它只反映声明依赖路径,和自动加载、运行时调用链无关。autoload 的优先级、classmap vs psr-4 的覆盖行为,完全不在这个树里体现。
-
composer show --tree默认只扫描require,require-dev中的包不会出现,除非被某个生产依赖间接带入 - 加
--no-dev是刚需,尤其上线前审查——很多冲突源头是phpunit或nikic/php-parser拉进来的旧版symfony/polyfill - 终端宽度太小会导致缩进换行错位,建议搭配
less -S:composer show --tree --no-dev | less -S
为什么 composer show --tree 看不清深层嵌套
因为它根本不是为“看清”设计的,而是为“快速定位路径”设计的纯文本命令。Composer 官方没做折叠、高亮、交互或图形渲染,全靠缩进+字符串匹配。当项目引入 Laravel + Symfony + Doctrine,依赖树轻松破千行,monolog/monolog 可能从 5 个不同路径进来,你得手动 grep + 数空格才能确认是否真有冲突。
这不是你用错了,是工具边界明确:它适合查“某包 A 是否被 B 依赖”,不适合回答“整个生态里谁在控制日志流向”。
- 别指望
--tree显示版本号——它只显示包名,版本信息得靠composer show vendor/package单独查 -
--dev和--no-dev不能混用,后者优先级更高;单独用--dev反而会让输出更混乱,因为 dev 包可能又依赖生产包 - 想临时聚焦某条链?用
composer show --tree guzzlehttp/guzzle,它会从 Guzzle 开始向下展开,而不是从 root 开始扫全量
真正能看清依赖关系的替代方案
需要可视化图,就得离开 show --tree,转向 Graphviz 生态。核心组合是 composer-unused + dot 命令,不是玩具,是实际排查过 Laravel 多版本 symfony/console 冲突的方案。
注意箭头方向:默认 composer-unused --format=dot 输出的是“被依赖方向”,即 A → B 表示 B 用了 A。如果你习惯说“A 依赖 B”,必须加 --reverse 参数,否则图一打开就理解反了。
- 先装依赖:
composer global require composer-unused/composer-unused - 生成图文件:
composer-unused --format=dot --reverse --include="guzzlehttp/guzzle" > guzzle.dot - 渲染成图:
dot -Tpng guzzle.dot -o guzzle.png(若报Dot command not found,macOS 用brew install graphviz,Ubuntu 用apt install graphviz) - 私有包或
"type": "path"引入的本地包,默认不扫描,得加--scan-path=../my-private-package
依赖树里找不到某个已安装的包,怎么办
先别怀疑 Composer,大概率是它压根没被任何 composer.json 声明过——比如你手动 cp 进 vendor/,或者用了 path 类型仓库但忘了在 require 里写条目。这种包不会出现在 show --tree,也不会被 composer depends 扫到。
验证方式很简单:composer show package/name 能返回信息,说明 Composer 认得它;但 composer why package/name 返回 “not found in your dependencies”,就坐实了它是“幽灵依赖”。
- 查它是否被代码实际引用:运行
composer-unused --dry-run,它会列出所有未被require但又被 PHP 文件use或new的包 - 看
vendor/composer/installed.json?别信。这文件只是上次install/update的快照,删了包不 update,它还在;改了composer.json不 update,它已过期 - 最可靠的方式是反向 grep:
composer show --tree | grep -B3 -A3 "package/name",再顺着上层包名继续show --tree upper/package往上摸
依赖拓扑分析真正的难点,从来不在“怎么画出来”,而在“怎么判断哪条路径该砍、哪条该留”。show --tree 给你骨架,why 告诉你动机,graphviz 帮你看清缠绕,但最终拍板要不要升级 psr/cache 到 v3,还得回到 composer.json 里那行 "php": "^8.1" 和上游包的 conflict 字段。











