composer-merge-plugin 1.x 和 2.x 分别仅兼容 Composer 1.x 和 2.x,混用会导致配置被忽略或静默失效;需按 Composer 版本严格匹配插件版本,并验证路径、autoload 和依赖冲突。

composer-merge-plugin 版本和 Composer 主版本不匹配
Composer Merge Plugin 的 1.x 和 2.x 分别只兼容 Composer 1.x 和 Composer 2.x,混用会直接导致 composer install 报错或插件静默失效。常见现象是 extra.merge-plugin 配置完全被忽略,include 的文件没被合并,但命令也不报错。
确认当前 Composer 版本:composer --version;若输出含 2.x(如 Composer version 2.5.8),则必须使用 wikimedia/composer-merge-plugin:^2.0。旧项目升级 Composer 后,不能跳过插件升级——先运行 composer require wikimedia/composer-merge-plugin:^2.0 --no-update,再 composer update wikimedia/composer-merge-plugin。
- Composer 1.x 用户请勿尝试安装
^2.0,会触发Root composer.json requires wikimedia/composer-merge-plugin ^2.0 -> found wikimedia/composer-merge-plugin[2.0.0, ..., 2.1.0] but it does not match your minimum-stability. - Composer 2.x 用户若强制装了 1.4.x,
composer show --tree中可能看不到合并后的包,且composer diagnose通常不会提示插件问题 - 插件安装后务必检查
vendor/wikimedia/composer-merge-plugin目录是否存在,以及其composer.json中的type是否为composer-plugin
merge-plugin 的 include 路径未被识别或解析失败
路径写错、文件不存在、或路径未按 Composer 插件约定展开,都会让合并逻辑“看起来生效”实则跳过。典型表现是:composer install 成功,但 composer show 中不见预期包,composer.json 里也查不到合并进来的 require 条目。
确保 include 数组中所有路径都是相对于项目根目录的,且文件真实存在。例如配置 "extensions/*/composer.json",需确认目录结构为 extensions/foo/composer.json、extensions/bar/composer.json,而非 ext/*/composer.json 或 extensions/foo/composer.lock。
- 路径支持 glob,但不支持递归通配符(如
**/composer.json不生效);recurse: true只对目录级include生效,不影响文件通配 - 路径中含空格或特殊字符(如括号)时,必须用单引号包裹整个字符串,否则 JSON 解析失败
- 执行
composer install -v或composer update -v,搜索日志中是否出现Merging或Skipped missing file字样——这是唯一能确认插件是否真正读取路径的依据
合并后依赖冲突未被及时暴露
merge-plugin 本身不做依赖求解,它只是把多个 composer.json 的内容拼到一起再交给 Composer solver 处理。所以合并引入的冲突,和手动写在主 composer.json 里的冲突性质完全一样,但更难定位——因为你不知道冲突来自哪个被 include 的文件。
一旦 composer update 或 composer install 报 “Your requirements could not be resolved”,不要先改主文件。先用 composer why-not 定位阻塞源头,再结合 composer show --tree 过滤可疑包,最后逐个打开 include 列表中的文件,检查它们的 require 字段。
- 执行
composer why-not vendor/package:version,若输出中某行末尾出现(required by extensions/foo/composer.json),就说明冲突来自那个扩展 -
composer show --tree | grep -A3 -B3 "package-name"可快速查看该包是从哪条路径被拉入的,注意括号里是否标出(from extensions/bar/composer.json) - 合并文件中若用了
conflict字段,它不参与 solver 计算,仅作提示;真正起作用的只有require和require-dev
插件启用但 autoload 映射未生效
合并成功、依赖也装上了,但类找不到(Class not found),大概率是 PSR-4 或 classmap 映射没进最终自动加载器。merge-plugin 不会自动合并 autoload 段,它只合并 require、scripts、extra 等顶层字段。
每个被 include 的子 composer.json 必须自行声明 autoload,且其映射路径需相对于该子文件所在目录(不是项目根目录)。例如 extensions/foo/composer.json 中写 "App\Foo\": "src/",那实际类文件必须在 extensions/foo/src/ 下。
- 运行
composer dump-autoload -v,观察日志中是否列出所有子目录的 autoload 映射;若没看到extensions/foo/src,说明该子文件的autoload段未被识别或格式错误 - 子
composer.json中的autoload若含相对路径(如"src/"),而该文件位于extensions/foo/,Composer 会自动将其解析为extensions/foo/src/;但若写成"../foo/src/",就会越界失效 - 合并后可运行
composer show -s vendor/package-name查看其 autoload 配置是否完整载入,重点核对autoload和autoload-dev字段值
真正的难点不在插件本身,而在合并动作把原本线性的依赖管理变成了多入口、多上下文的隐式叠加——你得同时盯着主文件、N 个子文件、composer.lock 的实际快照,以及 autoload.php 最终生成的映射表。任何一处路径偏差或版本松动,都可能让问题延迟到运行时才爆发。











