Composer虚拟包不会出现在composer show输出中,因其无真实代码、不安装不加载;验证其生效需通过依赖它的包能否正常安装且不报错。

Composer虚拟包(virtual package)不参与依赖解析,只用于声明“已提供某能力”,验证是否生效的关键不是看它装没装上,而是看其他包能否正确识别并满足其约束。
composer show 能否列出 virtual package?
不能。执行 composer show 或 composer show --all 不会显示任何 virtual package,因为它们没有真实代码、不下载、不安装、不生成 autoload。你写 "php": ">=8.2" 这类平台包,或 "ext-redis": "*",或自定义的 "myorg/virtual-cache-driver": "*",都不会出现在 composer show 输出里。
常见误判:看到 composer show 没有该包,就以为“没生效”——其实这恰恰是正常表现。
require 一个依赖它的包,看是否跳过安装
这是最直接的验证方式。虚拟包的作用是“让别人认为这个功能已存在”,所以你要用一个明确 require 它的包来测试:
- 假设你定义了
"myorg/virtual-queue-driver": "*"作为虚拟包 - 再找一个真实包,比如
myorg/queue-worker,它在composer.json中写了"require": {"myorg/virtual-queue-driver": "^1.0"} - 在空项目中运行:
composer require myorg/queue-worker
如果虚拟包生效,Composer 会输出类似:
Using version ^2.1 for myorg/queue-worker ./composer.json has been updated Running composer update myorg/queue-worker Loading composer repositories with package information Updating dependencies Nothing to modify in lock file Installing dependencies from lock file (including require-dev)
注意其中没有 Installing myorg/virtual-queue-driver —— 因为它根本不会被安装;但整个流程不报错,queue-worker 成功进入 lock 文件,说明 Composer 已认可该约束已被满足。
反之,如果报 Root composer.json requires myorg/virtual-queue-driver ^1.0, it could not be found in any version,说明虚拟包未被识别,大概率是拼写不一致(大小写、连字符)、没写进根 composer.json 的 provide 字段,或版本约束对不上。
检查 provide 字段是否被正确读取
虚拟包必须通过 provide 声明,且只能写在**根项目**的 composer.json 中(不能靠 config 或环境变量注入)。验证三步:
- 确认根目录
composer.json包含合法provide块,例如:"provide": {"myorg/virtual-queue-driver": "1.0.0"} - 运行
composer config --list --verbose,检查 Loaded config file: 路径是否指向你改的那个composer.json(避免改错文件) - 执行
composer validate,它不会校验provide内容,但会拒绝语法错误或非法字段名;若报错provide is not a valid key,说明你用的是极老版本 Composer(
特别注意:provide 的值可以是具体版本号、* 或版本约束,但不能是 "self.version" 这类动态占位符——它会被当作字面量处理,无法匹配。
为什么 composer install 后 vendor/ 里找不到 virtual package?
因为根本不会放进去。虚拟包不是真实包,Composer 不会创建 vendor/myorg/virtual-queue-driver 目录,也不会写 autoload 规则。它的全部作用发生在依赖解析阶段:当 Composer 构建依赖图时,看到 provide 就标记“该能力已就绪”,后续遇到 require 它的包,就跳过安装检查。
最容易被忽略的一点是:虚拟包只对当前项目有效。如果你在 A 项目里定义了 provide,B 项目 require A,那么 B 并不会自动继承 A 的 provide —— 除非 A 是作为 platform package(如 ext-xxx)被系统识别,或者你在 B 的根 composer.json 里也显式声明了同样的 provide。


















