conflict字段对虚拟包生效但仅限composer识别的平台包名,且只参与依赖解析阶段的sat求解,不检查运行时存在性;它需满足被解析到、出现在当前版本组合、触发重新解析等隐含前提才能起效。

conflict字段对虚拟包(如 ext-xxx、php、lib-*)是否生效
生效,但仅限于 Composer 识别的平台包名,且只参与依赖解析阶段的 SAT 求解——它不会检查运行时是否存在,也不会阻止 install 成功后执行代码。
Composer 内置支持的虚拟包包括:php、ext-*(如 ext-json、ext-mbstring)、lib-*(如 lib-iconv)、curl、openssl 等。这些不是真实可安装的包,而是平台能力声明。
-
conflict中写"php": "8.3.0"→ 解析时若当前环境 PHP 版本被锁定为 8.3.0(或 composer.json 中 require 强制了该版本),则 conflict 触发,报 “Your requirements could not be resolved” -
"ext-redis": "*"→ 表示“我明确不兼容任何 redis 扩展启用状态”,但 Composer 不会去extension_loaded('redis')校验,它只看依赖图中有没有其他包 require 或 replace 了ext-redis "lib-iconv": " 是合法的,但实际极少用;真正起作用的是 require 中对 lib 的约束,conflict 只是反向排除- 写错名字(如
"ext-imagickk"拼错)不会报错,只会被忽略——Composer 不校验虚拟包名是否存在
为什么写了 "conflict": {"ext-curl": "*"} 却没拦住安装
因为 conflict 不是运行时拦截器,它只在依赖解析阶段起作用,而 ext-curl 这类扩展通常由其他包通过 require 或 replace 引入(比如 guzzlehttp/guzzle require ext-curl),你的 conflict 声明必须和那个 require/replace 在同一解析上下文中“交集为空”,才可能触发失败。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 如果你的包没被任何路径 require,它的
conflict就不会进入求解器视野 - 如果已有
composer.lock锁定了一个不含ext-curl的旧依赖树,composer install会跳过解析,直接复用 lock —— conflict 完全不触发 -
ext-curl被symfony/polyfill-curl用replace声明替代了?那 conflict 就失效了:Composer 认为“已满足”,不再校验原扩展是否存在 - 想强制校验,得加
--dry-run或跑composer check-platform-reqs,后者会读取所有已安装包的 conflict 字段并做提示(但默认不中断)
virtual package + conflict 的典型误用场景
最常踩坑的是把 conflict 当成运行时守卫,或者指望它替代 require 的语义。
- 想禁止项目在没装
ext-gd时启动?conflict做不到——它不执行 PHP 代码。该用require+platform-check插件,或在服务提供者里手动extension_loaded() - 写
"php": ">=8.2.5"在 conflict 里?这是反逻辑的:你应该是require它,而不是“冲突”它 - 用
"lib-libxml": "*"想屏蔽所有 libxml 版本?没意义——没有包会 require 具体 lib 版本,它几乎不会出现在依赖图中 - 在私有包里写
"ext-xdebug": "*"并期望 CI 报错?只有当 xdebug 被某个 require 显式拉入(比如 phpunit 间接 require)时才可能触发;否则静默
真正让 virtual package conflict 起效的实操条件
它不是“写了就管用”,而是需要满足一组隐含前提。
- 该包必须被 Composer 解析到:要么是根
composer.json的require,要么是某条依赖链上的活跃节点(不能被replace或provide吞掉) - 冲突目标必须出现在当前解析的版本组合中:例如你
require laravel/framework:11.0.0,而它内部require ext-pdo_sqlite,这时你的conflict: {"ext-pdo_sqlite": "*"}才可能让 solver 失败 - 必须触发重新解析:删
vendor/后跑composer install,或改composer.json后跑composer update;单纯composer install --no-dev可能绕过 - 别依赖默认行为:加
--verbose或--dry-run看 solver 输出,确认 conflict 是否被载入求解器输入项(日志里会出现conflict关键字)
真正难的不是写法,而是判断 conflict 是否进入了本次求解上下文——它藏在依赖图深处,不展开 composer show --tree 和 composer why-not 几乎看不到。










