composer虚拟包是解决依赖冲突与架构分层失控的关键控制点,它通过composer.json中的"provide"字段声明能力契约,不包含真实代码,仅参与依赖解析阶段的“提供-满足”匹配。

Composer虚拟包不是“用来凑数的占位符”,而是解决真实依赖冲突与架构分层失控的关键控制点——当你在TP8项目里想替换掉think\Cache的Redis实现,又不想动框架源码;或在Laravel中强制约束某个第三方包只能用v3不许升v4;甚至让支付宝SDK不加载guzzlehttp/guzzle时,虚拟包就是你手里的开关。
什么是虚拟包:不是真实代码,是依赖契约
虚拟包(virtual package)在Composer里没有实际源码,只在composer.json中以"provide"字段声明:“我提供了某个接口/能力”。它不下载任何PHP文件,只告诉Composer:“这个能力已就绪,别再装别人了”。
- 典型场景:你想用自研的
MyCacheAdapter替代TP8默认的think\Cache,但框架代码里写了"require": {"psr/simple-cache": "^3.0"},而你的适配器已实现Psr\SimpleCache\CacheInterface——这时你只需在自己包的composer.json里加"provide": {"psr/simple-cache": "3.0.0"} - 它不触发自动加载,也不影响
vendor/autoload.php,只参与依赖解析阶段的“提供-满足”匹配 - 错误认知:以为加了
provide就能让框架自动调用你的类——不会。你仍需手动绑定到服务容器,比如在TP8中$app->bind('cache', MyCacheAdapter::class)
如何用虚拟包解耦HTTP客户端依赖
支付宝轻量SDK(alphasnow/alipay-easysdk-php)不带guzzlehttp/guzzle,但它需要一个PSR-18兼容的HTTP客户端。你不希望项目里同时存在Guzzle和另一个HTTP库(比如symfony/http-client),又怕框架其他部分悄悄拉取Guzzle v7导致版本冲突。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
- 方案:建一个空包
myorg/psr18-adapter,composer.json中写"provide": {"psr/http-client-implementation": "1.0"} - 然后在项目根目录
composer.json的require里显式引入它,并确保symfony/http-client也已安装(它才真正提供实现) - 关键点:Composer会检查所有已安装包的
provide字段,只要有一个满足psr/http-client-implementation,就不会再尝试安装Guzzle——哪怕别的包require了它 - 验证方式:
composer show --platform能看到psr/http-client-implementation被哪个包提供;composer why-not guzzlehttp/guzzle能查清谁在阻止它安装
虚拟包 + 替换(replace)组合拳:彻底移除冲突包
ThinkPHP6插件think-addons本身不依赖monolog/monolog,但某些老版日志组件会硬依赖它,导致CI失败。你不想改插件源码,又不能删掉整个Monolog——因为主项目其他模块还在用。
- 建一个
myorg/monolog-shim包,在composer.json中写"replace": {"monolog/monolog": "*"},同时"provide": {"psr/log-implementation": "3.0.0"} -
replace会让Composer认为“monolog/monolog已存在”,跳过安装;而provide则满足其他包对psr/log-implementation的需求 - 注意:必须确保你的shim包确实实现了
Psr\Log\LoggerInterface,否则运行时报Class not found——虚拟包不校验实现,只管声明 - 风险点:如果某包用到了Monolog特有的
Logger::alert()等方法,而你的shim只实现了基础接口,就会出错。此时应优先用conflict字段明确禁止该包升级
虚拟包真正的难点不在声明,而在厘清“谁在依赖什么、谁在提供什么、谁在消费什么”。一次composer depends --tree psr/log比十次瞎试更管用。它不帮你写代码,但能让你看清依赖图谱里哪条线不该连。










