虚拟包不能发布,仅通过provide字段声明接口能力,须依托真实包放入私有仓库(VCS或Satis)并配置项目级repositories和"packagist.org": false才能生效。

虚拟包不能“发布”,只能在私有仓库中声明
Composer 没有所谓“虚拟包发布”这回事——virtual package 是一个元概念,不是真实存在的代码包。它不包含源码、不打 Git tag、不提供 autoload,只在 composer.json 的 provide 字段里声明“我提供了某个接口能力”,用来解决替代依赖(如替代 psr/log 的自研日志实现)或满足硬性 require 约束。你无法把它推到 Packagist,也不能用 composer publish(根本不存在这个命令)。
真正要让它生效,必须走私有仓库路径:把声明了 provide 的真实包(哪怕只有空 composer.json)放进 VCS 或 Satis 仓库,并确保项目能通过该仓库解析到它。
- 常见错误现象:
Could not find package your-vendor/virtual-log-adapter—— 因为没在项目级composer.json的repositories中声明来源 - 使用场景:替换 PSR 接口实现、桥接旧系统抽象层、绕过被移除的废弃包依赖
- 关键点:
provide必须写在**被 require 的那个真实包**的composer.json里,不是写在使用者项目里
VCS 方式配置私有仓库(最简可用)
适合单个或少量虚拟包,直接指向 Git 仓库根目录(含 composer.json 的那层)。
-
repositories必须是 JSON 数组,写在**项目根目录**的composer.json中,不是私有包自己的composer.json -
type只能是vcs,不能写成git或github;url必须是可克隆地址(如https://gitlab.example.com/internal/log-bridge.git),不能带.git后缀(虽部分兼容,但非规范) - 认证靠
auth.json:Linux/macOS 下权限必须为600,否则 Composer 静默忽略;内容示例:{"http-basic": {"gitlab.example.com": {"username": "token", "password": "glpat-xxx..."}}} - 若用 SSH,确保
url是git@gitlab.example.com:internal/log-bridge.git形式,且本地ssh-agent已加载对应密钥
Satis 方式配置私有仓库(集中管理多个虚拟包)
适合中大型团队,把多个仅含 provide 声明的轻量包统一托管为静态源。
- Satis 构建时,每个包的
composer.json必须含合法name和provide,例如:{"name": "your-org/psr-log-bridge", "provide": {"psr/log": "^2.0"}} -
satis.json中repositories列表要包含这些 Git 仓库,require或require-all控制收录范围;构建后生成packages.json必须能被 HTTP 访问(如https://packages.internal/packages.json) - 客户端项目需禁用 Packagist 默认源:
"packagist.org": false,并添加 typecomposer的仓库:{"type": "composer", "url": "https://packages.internal/"} - dist 回退失败常见于:Satis 输出目录未配置 MIME 类型(
application/json)、ZIP 包未生成(检查archive.format和skip-dev)、域名证书不被信任("secure-http": false临时绕过)
容易被忽略的细节
虚拟包的 provide 不会自动触发安装,它只是“能力声明”。真正起作用的是:项目 require 了某个包名,而该包名恰好被某个仓库里的包通过 provide 覆盖了。如果两个包都声明提供 psr/log,Composer 选谁取决于 repositories 数组顺序——先命中者胜,后面的同名声明完全无效。
另外,composer update --dry-run 是验证虚拟包是否被识别的最快方式:看输出里有没有 “your-org/psr-log-bridge provides psr/log” 这类提示。没有,说明仓库没生效、provide 拼写错误,或包名不匹配 require 的约束。


















