composer prohibits 更准,因为它从目标包出发反向追溯所有直接/间接阻止安装的依赖约束,而 why-not 仅检查 composer.json 中已声明但未安装的包;prohibits 必须带精确版本号,输出明确指向冲突源头,适合定位根因。

为什么 composer prohibits 比 composer why-not 更准?
因为 composer why-not 只检查当前 composer.json 中已声明但未安装的包是否能被满足,而 composer prohibits 是反向追溯——它从你「想装却装不上」的那个包出发,一层层找出所有直接/间接阻止它被安装的依赖约束。实际排错时,90% 的“装不上”问题靠 composer prohibits 才能定位到根因。
常见错误现象:composer require laravel/framework:^11.0 报错 Conclusion: don't install laravel/framework v11.0.0,但 composer why-not laravel/framework:11.0 返回空或只说 “not installed”,这时必须切到 prohibits。
-
composer prohibits必须带具体版本号(如laravel/framework:11.0.0),不支持通配符或分支别名(^11.0会报错) - 它默认只查 root package 的冲突;如果要查某个已安装包为何阻止新包,得加
--root参数 - 输出中每行末尾的
(for ...)表示该约束来自哪个包的require或conflict字段
composer prohibits 的典型调用方式
假设你想装 symfony/console:v6.4,但失败了:
composer prohibits symfony/console:v6.4
如果输出太长,加 --tree 看依赖路径,或用 --format=json 交给脚本处理。注意:它不会自动检测 PHP 版本不兼容,但会显示类似 php: ^8.1 conflicts with php: >=8.2 这样的硬约束。
- 若提示
No version set (parsed as 1.0.0),说明你运行在非项目目录,先cd进项目根目录 - 如果结果里出现
your-project-name,说明是composer.json里的conflict或require字段直接锁死了版本 - 遇到
doctrine/dbal和laravel/framework冲突,大概率是前者要求php:^8.0而后者某子依赖要求php:^8.1——prohibits会把这层链路打出来
为什么 composer why-not 经常“查不到东西”?
composer why-not 的逻辑是:“当前 composer.json 里写了这个包吗?写了但没装上?那我看看谁不让它装。” 它不关心你临时 require 的那个包,只关心 JSON 里已存在的条目。所以你执行 composer require foo/bar 失败后立刻跑 why-not foo/bar,大概率返回 foo/bar is not required in your composer.json ——这不是 bug,是设计如此。
- 它适合自查:比如你删掉了
phpunit/phpunit的 require,又忘了删autoload-dev,运行测试时报错,这时composer why-not phpunit/phpunit能提醒你“你没声明它” - 它对
dev-main、dev-develop这类开发分支无效,只认稳定版本号 - 如果项目用了
platform配置(如"php": "8.0"),why-not不会把它当冲突源展示,但prohibits会
真正卡住时,该先跑哪个命令?
流程很明确:装不上 → 看报错最后一行(通常是 don't install X)→ 提取完整包名+精确版本 → 跑 composer prohibits 。如果输出里有 your-project-name,立刻打开 composer.json 查 require、conflict、platform;如果有第三方包,再进那个包的 composer.json 看它的约束。
- 别依赖
composer update --dry-run,它只模拟,不解释“为什么” -
composer show -t能看依赖树,但无法标出冲突点;prohibits是唯一自带归因能力的命令 - 某些私有包或 fork 分支没打 tag,
prohibits可能显示Could not find package——此时要确认是否已配置正确的 repository
复杂点在于:一个冲突往往由多层 conflict 和宽松 require 共同导致,prohibits 输出的每一行都可能是关键,漏掉任意一行都可能绕远路。











