先确认包是否真被项目使用:若composer why vendor/package为空,可能因该包仅在require-dev中、被replace隐式替代或未安装;应先检查vendor/目录是否存在,再试composer why --dev或composer show --tree定位依赖链。

composer why 查不到包?先确认它是不是真被项目用了
运行 composer why vendor/package 返回空,不等于这个包没在项目里——它可能只被 require-dev 引用、或通过 replace / provide 隐式引入,甚至根本没装进 vendor/。先执行 ls vendor/vendor/package 确认目录存在;再检查是否在 require-dev 里,如果是,加 --dev 参数重试:composer why --dev vendor/package。
常见漏判场景:
-
psr/log这类虚拟包,composer why直接静默失败,得用composer show --tree | grep "psr/log"手动翻树 -
illuminate/support被laravel/framework的replace声明替代,why查不到,但composer show --tree laravel/framework里能看到它被展开 - 输出里带
(replaced by laravel/framework),说明该包已无独立生命周期,删它没意义,必须升级框架本身
composer why-not 报错说 Package not found?版本写法错了
composer why-not 不是模糊搜索工具,它只分析「已被声明但无法满足」的依赖。如果报 [InvalidArgumentException] Package not found,大概率是你没在 composer.json 里写过这个包,或者版本号格式不合法。
正确写法要点:
- 必须带完整冒号分隔:
composer why-not 'guzzlehttp/guzzle:^8.0.0'(引号防 shell 解析错误) - 不能省略小数点后位数:写
^8.0可能匹配不到元数据,得写成^8.0.0或8.0.0 - 版本约束要具体:只写
monolog/monolog:^3可能返回 “no problem found”,因为 ^3 兼容当前锁文件;得写明确冲突版如^3.5.0 - 命令前确保
composer.json已更新目标版本(比如你刚require过但失败了),否则它分析的是旧状态
why-not 输出里有多个 (required by),哪条才是主因?
输出是倒序链,最后一行是你 composer.json 的根声明(比如 "laravel/framework": "^10.0"),往上每行末尾的 (required by ...) 是上游约束路径。关键不是看有多少条,而是盯住最短那条——离你目标包最近的一条,往往就是直接封杀者。
典型干扰项:
- 出现
root requires行 → 冲突源头就在你顶层composer.json里写的某条require - 出现
phpunit/phpunit或mockery/mockery→ 很可能是它们拖着老版sebastian/exporter,间接锁死symfony/console - 出现
conflicts with行 → 比如laravel/framework v11.0.0 conflicts with guzzlehttp/guzzle,说明另一端有包硬要求guzzlehttp/guzzle:^6.5,和 Laravel 11 不兼容
为什么 show --tree 和 why 输出对不上?
composer why 只查直接声明依赖,composer show --tree 展示的是真实安装快照。两者不对等是常态,不是 bug。
例如:
-
composer why symfony/console只显示laravel/framework,因为它是composer.json里唯一直接声明者;但composer show --tree symfony/console会暴露出它其实还被aws/aws-sdk-php和spatie/laravel-backup间接拉入 -
composer show --tree里看到(locked to 6.4.12),说明版本由composer.lock固定,不是composer.json里写的^6.4决定的 - 输出里有
(provided)或(replaced)标记 → 必须去那个提供包自己的composer.json查provide/replace字段,才能确认它是否真能覆盖目标功能
真正容易被忽略的点是:composer.lock 里的锁定版本,永远优先于 composer.json 的愿望清单;而 why 和 why-not 分析的,正是这个锁定上下文下的真实约束关系。











