Composer虚拟包是逻辑占位符,通过provide字段声明“我实现了某接口规范”,如monolog/monolog提供"psr/log-implementation": "1.0.0",使下游项目require "psr/log": "^1.0"时可自动匹配,无需显式依赖具体实现。

什么是 Composer 的虚拟包(virtual package)
虚拟包不是真实存在的代码包,而是通过 provide 字段在 composer.json 中声明的“逻辑占位符”,用来表达「我实现了某个接口规范」或「我替代了某个包」。最常见的是 psr/log、psr/container 这类 PSR 标准包——它们本身不带实现,只定义接口,真正干活的是 monolog/monolog 或 php-di/php-di 这类提供者。
provide 字段怎么写才有效
必须写在被依赖方(即提供实现的包)的 composer.json 里,且版本号要与规范一致。例如 monolog/monolog 的 composer.json 中有:
"provide": {
"psr/log-implementation": "1.0.0"
}
这表示它“提供了 psr/log 的 1.0.0 版本实现”。下游项目只要声明 "psr/log": "^1.0" 在 require 中,Composer 就能匹配到这个提供者,而无需直接 require monolog。
- 不能写在消费者(你的扩展)的
provide里——除非你真实现了那个接口 -
provide的值必须是合法包名 + 版本号,不能是通配符(如"psr/log-implementation": "*") - 若多个包都
provide同一个虚拟包,Composer 会按依赖约束选一个,不会自动合并
为什么 composer why psr/log 查不到引用链
因为 composer why 只查显式 require 或 replace,不穿透 provide。即使项目里装了 monolog/monolog 并提供了 psr/log-implementation,composer why psr/log 仍会返回 “Package not found”。
- 正确验证方式:
composer show --tree | grep -B2 -A2 "psr/log"或composer show monolog/monolog看它的provide字段 - 如果项目用
psr/log接口但没装任何提供者,运行时会报Class not found,而不是 Composer 安装时报错 -
provide不解决自动加载问题——你仍需确保提供者的 autoloader 被加载,否则接口存在但实现不可用
扩展包里该不该声明 provide
绝大多数 Laravel/Yii 扩展不需要,除非你明确封装了一个标准实现(比如你写了 myvendor/psr-cache-redis 并想声明它提供 psr/cache-implementation)。更常见的误用是:在扩展里 provide: "laravel/framework" —— 这完全错误,框架由宿主项目提供,扩展只能 require 其子组件(如 illuminate/support)或用 conflict 限制版本范围。
真正需要关注的是:你依赖的包是否通过 provide 引入了隐式依赖,比如用了 symfony/cache,它 provide: "psr/cache-implementation",那你的代码里调 Psr\Cache\CacheInterface 就能工作,但别指望 IDE 自动跳转或静态分析能 100% 覆盖——这种解耦带来的灵活性,是以可追踪性为代价的。











