虚拟包是Composer依赖解析时的替代声明,仅在被其他包require时生效;需在被依赖的库composer.json中用provide字段严格按规范命名声明,不参与自动加载或运行时逻辑。

虚拟包不是让你“发布一个包”,而是告诉 Composer:“我这个包,能顶替某个标准接口或扩展来用”——它只在依赖解析时起作用,不改自动加载、不触发任何运行时逻辑。
provide 字段必须写在被 require 的包里,根项目写无效
你自己的项目(即顶层 composer.json)里加 provide,Composer 完全无视。它只认「被别人 require 的包」里的声明。
- 正确位置:你开发的库(比如
myorg/redis-cache)的composer.json顶级字段 - 错误写法:
"require": { "psr/cache-implementation": "*" }—— 这是错当依赖写,不是提供 - 常见误操作:在 Laravel 项目根目录里加
provide声明自己“提供了 log 实现”,毫无效果
包名必须严格匹配生态约定,拼错一个字符就白写
虚拟包名不是自定义的,得和主流框架、PSR 规范、扩展名完全一致。Composer 不校验实现是否真合规,只做字符串匹配。
- 要提供缓存实现 → 写
"psr/cache-implementation": "*",不是"psr/cache"(那是接口包本身) - 要声明支持 GD 扩展 → 写
"ext-gd": "*",不是"gd"或"php-gd" - 想让包被 Laravel 生态识别 → 有些场景需配合
replace(如"laravel/framework": "*"),单靠provide不够
install/update 时才生效,且仅用于解决 require 冲突
provide 不会自动加载类、不注册服务、不修改运行行为。它只在 Composer 解析依赖图时,帮其他包判断“谁能满足我的 require”。
- 例如:另一个包写了
"require": { "psr/log-implementation": "^1.0" },你的包若声明了"psr/log-implementation": "*",它才可能被选中安装 - 如果你的包没被任何人 require,或者没人 require 对应的虚拟包名,
provide就纯属静态声明,无任何副作用 - 两个包都提供同一虚拟包(如都提供
psr/http-client-implementation),Composer 选第一个解析到的,顺序不可控
最容易被忽略的一点:你写了 provide,也发布了包,但下游项目没在 require 里明确写那个虚拟包名,它就永远不会被触发——它不是“广播通知”,而是“被动响应”。


















