Metapackage 是一种空的、不包含 PHP 代码的 Composer 包,仅通过 require 字段批量声明依赖,type 必须设为 metapackage 且不可配置 autoload;它不提供功能,只触发依赖安装,所有工具实际存于 vendor/ 下并软链至 vendor/bin/。

Metapackage 是什么,它和普通包有什么区别?
Composer 的 metapackage 本质是一个空的、不包含任何 PHP 代码的包,它的 composer.json 里只有 require 字段,用来声明一组依赖。它不提供功能,只起“打包引用”作用——安装它,就等于批量安装它所要求的那些工具。
关键区别在于:type 字段必须设为 metapackage(而非默认的 library),且不能有 autoload 配置。否则 Composer 会尝试加载它的类,而它根本没有。
如何定义一个 Metapackage 来聚合开发工具?
新建一个空目录,写入最小可用的 composer.json:
{
"name": "myorg/dev-tools",
"description": "Common dev tools for our projects",
"type": "metapackage",
"require": {
"phpunit/phpunit": "^10.5",
"phpstan/phpstan": "^1.12",
"friendsofphp/php-cs-fixer": "^3.54",
"symfony/var-dumper": "^7.1"
}
}
注意几点:
-
name必须遵循vendor/name格式,且需在 Packagist 或私有仓库中可解析 - 所有依赖应锁定合理版本范围,避免因子依赖冲突导致安装失败
- 不要加
autoload、scripts或bin字段——它不运行代码,只触发依赖安装 - 若工具含全局二进制(如
php-cs-fixer),它们会在vendor/bin/下可用,无需额外配置
为什么 install 后没看到任何文件,但命令却能用了?
这是 metapackage 最容易让人困惑的地方:你 composer require myorg/dev-tools 后,vendor/myorg/dev-tools 目录下确实几乎为空(可能只有 composer.json 和 README.md),但所有列出的工具已按需装进 vendor/ 对应路径,并软链或复制到 vendor/bin/。
常见误判场景:
- 执行
php-cs-fixer报 “command not found” → 检查是否在项目根目录运行,且vendor/bin是否在$PATH中;更稳妥的是直接用vendor/bin/php-cs-fixer - 升级 metapackage 后部分工具未更新 → 因 Composer 默认复用已满足版本约束的本地包;加
--with-all-dependencies强制刷新整条依赖树 - CI 环境里命令失效 → 确保 CI 运行了
composer install(而非install --no-dev),因为 metapackage 通常放在require-dev里
发布 Metapackage 到 Packagist 的实际注意事项
把 metapackage 推送到 GitHub 后,在 Packagist 上 submit 即可自动同步,但有三个硬性条件必须满足:
- 仓库必须公开,且
composer.json在根目录 -
name字段值必须与 Packagist 的 vendor 名完全一致(大小写敏感) - 首次提交后,Packagist 不会自动抓取后续 tag;需手动点击 “Update” 或配置 webhook
私有项目更推荐用 composer config repositories.myrepo vcs https://git.example.com/myorg/dev-tools + composer require myorg/dev-tools,绕过 Packagist 审核和可见性问题。
metapackage 的真正价值不在“省几行命令”,而在统一团队的工具版本策略——只要改一行 composer.json,所有成员下次 composer update 就自动对齐,前提是没人绕过它直接 require 工具本身。










