Composer 不支持 Monorepo 自动识别子模块,因设计为“单项目单包”,每个子模块必须有独立 composer.json 并通过根配置 path 仓库、require 引用及 dev- 版本别名才能被正确加载和自动合并 autoload。

Composer 本身不原生支持 Monorepo 多子模块(multi-package)的依赖解析和自动加载管理,直接在根目录放一个 composer.json 并期望它“自动识别所有子目录下的包”会失败——composer install 只处理当前目录及其 require 声明,不会递归扫描子包。
为什么不能只靠根 composer.json 管理所有子模块?
Composer 的设计模型是“单项目单包”,每个可发布/可 require 的 PHP 包必须有自己独立的 composer.json,且需声明 name(如 "myorg/core")、autoload、版本约束等。Monorepo 中若多个子目录都含 composer.json,但未被 root 显式引用,Composer 就完全忽略它们。
- 子目录如
packages/core、packages/api-client各自带composer.json,但根composer.json没写"myorg/core": "dev-main"→ 这些包不会被安装或 autoload - 即使你手动
composer require myorg/core:dev-main,Composer 默认从 Packagist 或配置的仓库拉取 ZIP/tarball,而不是读取本地路径 - 本地开发时,修改
packages/core的代码,不会实时反映到依赖它的packages/api-client中,除非反复composer update+ 提交 + 切换分支
必须启用 path repository 类型并显式注册每个子模块
让 Composer 认出本地子目录是“可安装的包”,唯一可靠方式是在根 composer.json 的 repositories 字段中,为每个子模块添加一个 path 类型仓库,并确保子模块的 composer.json 有合法 name 和 version(如 "dev-main" 或 "1.0.x-dev")。
示例:根 composer.json 片段
立即学习“PHP免费学习笔记(深入)”;
{
"repositories": [
{
"type": "path",
"url": "packages/core"
},
{
"type": "path",
"url": "packages/api-client"
}
],
"require": {
"myorg/core": "dev-main",
"myorg/api-client": "dev-main"
}
}
-
url必须是相对于根composer.json的路径,不能以./开头(Composer 会报错) - 子模块
packages/core/composer.json必须含"name": "myorg/core",否则 Composer 找不到匹配项 - 如果子模块
composer.json中version是"1.0.0",而你在 rootrequire写"dev-main",Composer 会忽略——path仓库只匹配dev-开头的版本别名,或使用"*@dev"
autoload 自动合并需要手动配置 psr-4 映射
即使子模块被成功 require 并软链接进 vendor/,它们各自的 autoload 不会自动合并到根项目的自动加载器中。运行 composer dump-autoload 时,Composer 只读取 vendor 下各包自己的 composer.json,但前提是这些包已通过 path 正确安装(即 vendor 中存在符号链接)。
- 确认
vendor/myorg/core是指向../packages/core的符号链接(Linux/macOS)或 junction(Windows) - 若仍无法
new MyOrg\Core\Something(),检查子模块composer.json的autoload.psr-4是否正确,例如:"MyOrg\\Core\\": "src/" - 根项目无需额外加
autoload,只要子模块 autoload 定义有效,Composer dump 出的vendor/autoload.php就会包含它们
常见陷阱与绕不过去的细节
Monorepo + Composer 最容易卡住的地方不是配置,而是版本别名、符号链接权限、以及 CI 环境下 path 仓库失效。
- CI 构建时,
path仓库默认被忽略(出于安全),必须显式加--no-plugins或禁用插件,或改用composer config --no-plugins repositories.path.type path动态注册 - Windows 上若启用了 Windows Subsystem for Linux(WSL),符号链接可能跨环境失效,建议统一在 WSL 内执行
composer install - 子模块之间有循环依赖(A require B,B require A)时,Composer 会直接报错,Monorepo 也无法绕过这个限制
- 不要试图用
composer create-project初始化子模块——它会覆盖原有composer.json,破坏 path 映射关系
真正起作用的永远是:每个子模块有完整 composer.json + 根配置 path 仓库 + require 引用 + dev- 版本别名。少一个环节,就只是普通文件夹,不是 Composer 包。











