最准命令是composer show --tree,但需加包名或--no-dev等参数过滤;默认输出含底层包、缩进深、无版本标识,易成“缩进迷宫”,查引入源应使用composer why而非show --tree。

直接用 composer show --tree 看不到你想查的包?不是命令错了,是默认输出太全、太深、太乱——得加过滤和上下文控制。
为什么 composer show --tree 默认输出像“缩进迷宫”
它从项目 root 开始递归展开所有已安装包,包括 symfony/polyfill-*、composer/xdebug-handler 这类底层支撑包。你关心的 guzzlehttp/guzzle 可能藏在第 17 行、缩进 12 格的位置;同名包不同版本(如 psr/log v1.2.0 和 v3.0.0)并存时,也不标版本差异,容易误判冲突源头。
- 终端宽度不够时缩进错位,
| less -S横向滚动更可靠 - 不加
--no-dev会混入phpunit、mockery等开发依赖,干扰生产环境分析 - 输出里带
[dev]标记的,说明来自require-dev,上线部署时不会被装
查某个包是谁引入的:用 composer why,不是 show --tree
composer show --tree 是“向下看”——查一个包依赖了谁;而定位“为什么装了这个包”,该用 composer why,它直击上游显式声明点。
-
composer why monolog/monolog显示直接 require 它的包(如laravel/framework)及满足的约束(如^3.0) -
composer why --recursive monolog/monolog展开完整路径,效果接近子树裁剪 - 如果查不到,先确认包是否真在
vendor/下:ls vendor/monolog/monolog - 若包只在
require-dev里,得加--dev参数,否则静默忽略
查依赖树里某包的上下文:grep 比图形化工具更快更准
别急着装 baethon/composer-graph 或找 Web 可视化工具。真实排障中,90% 的场景靠终端管道就能定位清楚。
-
composer show --tree | grep -A 5 -B 5 "monolog/monolog"—— 显示匹配行前后 5 行,看清它是被laravel/framework还是spatie/laravel-ignition拉入的 - 包名必须带斜杠,写成
"monolog/monolog",否则可能误匹配monolog-handle等无关包 - 输出过长时加
| less -R(前提是 Composer 配置了color = true) - 想排除开发依赖干扰:
composer show --tree --no-dev | grep -A 3 -B 3 "guzzlehttp/guzzle"
注意 composer show --tree 不反映 provide 和 replace 的实际替换逻辑
依赖树显示的是“安装结构”,不是“语义覆盖”。比如 monolog/monolog 在 composer.json 里声明 "psr/log": "^1.0" 并通过 provide 字段声称自己实现了它,但树里只会显示 monolog/monolog → psr/log,不会告诉你这个 psr/log 其实是虚拟包、没真正安装。
- 遇到
(provided)或(replaced)标记,得去那个包自己的composer.json里确认它到底替代了什么 - 验证实际提供者:
composer show psr/log,看输出里有没有Provides字段 -
composer show --tree读的是vendor/composer/installed.json,不是composer.json的愿望清单——刚require但没install,就一定不会出现在树里


















