虚拟包是仅通过provide字段声明的语义占位符,无实际代码,不参与autoload;require它会失败,但被其他包require时可被声明了provide的真实包满足依赖。

Composer 虚拟包(virtual package)本身不能被直接“测试”——它没有代码、不提供类、也不参与 autoload,只是个语义占位符。真正在项目中要测的,是声明了 provide 的真实包,或依赖它的逻辑是否按预期解析。
什么是虚拟包?为什么 require 它会“成功”但没效果
虚拟包是 composer.json 中通过 "provide" 字段声明的伪包名,例如:
{
"provide": {
"psr/log-implementation": "1.0.0"
}
}
它不对应任何实际代码,只告诉 Composer:“本包可替代 psr/log-implementation”。常见于日志适配器、缓存驱动等接口实现包。
- 运行
composer require psr/log-implementation会失败:这不是真实包,Packagist 上查不到 - 但若另一个包在
require中写了"psr/log-implementation": "^1.0",而你本地包声明了provide,Composer 就会认为依赖已满足 - 不会生成 vendor/psr/log-implementation 目录,也不会加载任何类——它纯属元数据层面的“承诺”
如何验证 provide 声明是否被项目识别
关键不是跑测试用例,而是确认 Composer 解析器是否采纳了你的虚拟提供关系:
- 执行
composer show --installed | grep "log-implementation",如果输出为空,说明未被识别(哪怕你写了provide) - 检查你本地包的
composer.json是否已正确 require 进主项目(必须用path仓库方式,否则provide不生效) - 运行
composer depends psr/log-implementation,看是否有包把它列为依赖;若无输出,说明无人引用该虚拟名,或引用者没启用minimum-stability: dev - 临时加一个真实依赖来触发校验:在主项目
composer.json的require中写"psr/log-implementation": "*",再composer update—— 此时应报错Could not find package psr/log-implementation,除非你已通过provide+ 正确引入方式“骗过”了依赖解析
真正需要测试的是“提供者包”的行为,而非虚拟名本身
比如你写了一个 my-company/logger-adapter,并在其 composer.json 中声明 "provide": {"psr/log-implementation": "2.0.0"},那你要测的其实是:
- 该包自身能否被正常 autoload(检查
autoload和autoload-dev配置是否匹配命名空间和路径) - 它实现的
Psr\Log\LoggerInterface方法是否符合契约(这才是 PHPUnit 该覆盖的) - 当其他包(如
monolog/monolog)被替换为你的包时,上层调用是否仍能拿到可用 logger 实例(需在主项目中手动替换require并验证 DI 容器注入) - 运行
composer why-not psr/log-implementation:2.0.0看是否还有冲突约束阻止使用你的提供版本
容易忽略的关键点
虚拟包的 provide 生效有强前提:它必须是某个已被 Composer 加载进依赖图的真实包所声明的。单独放一个只有 provide 的空包,且未被任何 require 或 repositories 引入,就等于不存在。很多调试失败,是因为把 provide 当成了全局注册机制,而实际上它只在具体包上下文中起作用。


















