conflict字段仅在composer install/update依赖解析阶段拦截匹配声明范围的包版本安装;不作用于已锁版本、手动require或运行时,且分支名无效,须用标准版本约束语法。

conflict 字段到底能拦住什么
conflict 是 composer.json 中用于**声明当前包与哪些其他包(或版本范围)不兼容**的字段。它不会阻止安装,而是在 composer install 或 composer update 时,当依赖图中同时出现冲突声明里的包时,Composer 会直接报错并中止操作。
注意:它只在「整个依赖图收敛完成前」做静态检查,不运行时检测,也不影响已安装的包——也就是说,如果你手动删了 vendor 再装,冲突才真正起作用。
- 冲突检测发生在依赖解析阶段,不是安装后校验
- 声明
"monolog/monolog": " 并不会让 Composer 自动降级 monolog,而是当项目里其他依赖拉入了monolog/monolog:1.9时,直接拒绝安装 - 不支持通配符(如
"foo/*"),必须写具体包名
怎么写才算有效冲突声明
语法是 "conflict": { "vendor/name": "version-constraint" },其中 version-constraint 遵循 Composer 的标准版本约束规则(如 ^1.0、、!=3.1.4)。
常见误写:
- 写成
"conflict": ["monolog/monolog:^2.0"]→ 错,conflict必须是对象,不是数组 - 写成
"conflict": { "monolog/monolog": "2.*" }→ 模糊,建议用">=2.0 更明确 - 漏掉引号,如
monolog/monolog: ^2.0→ JSON 解析失败
正确示例:
conflict
- 用
replace声明替换了symfony/console,但又在conflict里写"symfony/console": "*",Composer 会困惑:你到底是想取代它,还是彻底禁止它?结果通常是报错退出 -
conflict对require-dev中的包也生效,但开发依赖未启用时(如--no-dev),该冲突不会触发 - 不要试图用
conflict替代版本约束优化——比如想“避开某个有 bug 的小版本”,优先应改require的约束,而非加conflict
为什么 conflict 没生效?排查要点
最常遇到的是“写了 conflict 却没报错”,原因往往不是配置错,而是依赖图没真撞上。
- 检查是否被
composer update --with-all-dependencies绕过:这个选项会忽略部分冲突检查,慎用 - 确认冲突包是否真的进入了最终依赖图——运行
composer show --tree | grep target-package看它是否被某条路径引入 - 注意版本约束的边界:写
" 不会拦住2.0.0-RC1(预发布版默认不满足),要用" 或显式排除"!=2.0.0-RC1" - 私有包若用了
pathrepo,Composer 可能跳过冲突检查——确保path类型仓库的包也发布了正确的composer.json
冲突机制本身轻量,但依赖解析逻辑复杂,一个看似简单的 conflict 条目,可能因间接依赖路径绕行而失效。真正可靠的规避,永远要结合 require 收紧 + 文档说明 + CI 中强制校验。











