Metapackage 是一个不包含源代码、仅声明依赖的空包,用于打包和复用一组固定版本的依赖组合;它无类无功能,仅在安装阶段生效,autoload 为空,type 推荐设为 "metapackage"。

Metapackage 是什么,它和普通 package 有什么区别?
Composer 的 metapackage 本身不包含任何源代码,只是一个空的 composer.json 文件,只用来声明一组依赖。它的作用是「打包依赖关系」,不是提供功能。当你运行 composer require vendor/metapackage,实际安装的是它 require 字段里列出的所有包,而这个 metapackage 自身不会被加载——它的 autoload 通常为空或被显式禁用("autoload": {"psr-4": {}}))。
常见误判是把它当成“工具集”或“SDK”,但其实它更像一个「依赖清单快照」:比如 laravel/framework 不是 metapackage,而 laravel/laravel(官方项目骨架)才是——它不提供类,只确保你装上 illuminate/*、symfony/* 等一整套组合。
如何创建一个最小可用的 metapackage?
关键点在于:不写源码、不注册自动加载、不发布逻辑类。只需一个合规的 composer.json,并打上 "type": "metapackage" 标签(非强制但强烈建议,可被工具识别)。
-
name必须符合vendor/name格式,且全局唯一 -
require列出你要聚合的包及其版本约束(如"php": "^8.1", "monolog/monolog": "^3.0", "guzzlehttp/guzzle": "^7.5") -
autoload应设为空对象或完全省略(避免 Composer 尝试生成 autoload classmap) - 不要放
src/、tests/目录,也不需要index.php
{
"name": "acme/full-stack-tools",
"type": "metapackage",
"description": "All dev tools we use across projects",
"require": {
"php": "^8.1",
"phpunit/phpunit": "^10.0",
"friendsofphp/php-cs-fixer": "^3.14",
"vimeo/psalm": "^5.10"
},
"autoload": {}
}
为什么 install 后看不到它的类,也调不到它的方法?
因为它根本没类。Metapackage 的价值在安装阶段,不在运行时。你执行 composer require acme/full-stack-tools 后,phpunit、php-cs-fixer 等会被装进 vendor/,但 acme/full-stack-tools 这个目录下只有 composer.json 和可能的 LICENSE ——没有 src/,也没有命名空间可引用。
常见踩坑:
- 试图
use Acme\FullStackTools\Something;→ 报错 Class not found(根本不存在) - 在
autoload里配了 PSR-4 → Composer 会尝试扫描空目录,浪费时间甚至报 warning - 给 metapackage 设了
minimum-stability: dev→ 它会污染整个项目的稳定性策略,导致其他依赖意外升级到dev版本
什么时候该用 metapackage,而不是直接写在根项目 composer.json 里?
适用场景很具体:当多个项目需要**完全一致的工具链或框架组合**,且你希望把这份依赖声明从每个项目中抽离出来统一维护。
例如:
- 公司内部所有 Laravel 项目都必须用同一组 PHP-CS-Fixer 规则 + Psalm 配置 + Pest 测试器 → 做一个
company/laravel-dev-kit - CI 流水线中每次都要装相同扩展(
ext-redis,ext-mbstring)和 CLI 工具 → 用 metapackage 封装成company/ci-prerequisites,再让 CI 脚本composer global require它
反例:只为图省事把几个常用包塞进一个 metapackage,却不打算复用或版本对齐——这种做法反而增加维护负担,还模糊了依赖来源。
真正难处理的是版本冲突:如果两个 metapackage 分别要求 guzzlehttp/guzzle:^6 和 ^7,Composer 会直接拒绝安装。这种耦合必须在设计 metapackage 时就对齐约束,不能等到集成时才发现。










