Composer虚拟包是通过provide字段声明实现某接口(如psr/log-implementation)的占位标识,本身不提供代码也不触发事件;它仅间接影响post-autoload-dump事件,因自动加载文件重建而触发,但该事件不感知提供者信息,纯provide无运行时钩子,需绑定真实包才能可靠触发行为。

什么是Composer虚拟包,它和事件系统有关系吗?
虚拟包(virtual package)本身不提供任何代码或功能,只是在composer.json中用provide字段声明“我提供了某个接口/能力”,比如"psr/log-implementation": "1.0"。它不触发任何事件——pre-install-cmd、post-update-cmd这些脚本事件只响应真实包的安装/更新动作,而虚拟包不会被下载、解压或写入vendor/,自然也不会触发post-autoload-dump以外的生命周期事件。
虚拟包唯一能影响的事件:post-autoload-dump
虚拟包只有在改变自动加载逻辑时才可能间接起作用。典型场景是:你用虚拟包声明实现了某个PSR标准(如psr/container-implementation),而项目里又依赖了需要该实现的库(如symfony/dependency-injection)。这时Composer会检查是否满足提供关系,若满足,才会继续解析依赖树——最终导致autoload.php内容变化,从而触发post-autoload-dump事件。
- 这个事件本身不感知“谁提供了什么”,只响应“自动加载映射是否重建”
- 如果你在
post-autoload-dump里写了清理缓存或生成代理类的逻辑,那它确实会运行,但原因不是虚拟包被“安装”,而是自动加载文件被重写 - 不要在
post-autoload-dump里做条件判断“如果提供了X就执行Y”,因为Composer不传递提供者信息给脚本
想让虚拟包“触发行为”?得靠真实包兜底
真正可控的协同方式,是把虚拟包和一个轻量真实包绑定。例如:
- 创建一个空包
myorg/container-bridge,在它的composer.json中同时写"provide": {"psr/container-implementation": "2.0"}和"scripts": {"post-autoload-dump": "php bin/setup-container.php"} - 主项目
require这个桥接包,而不是直接provide - 这样每次
composer install都会真实安装该包,事件可被可靠触发,且provide声明依然生效
否则,纯provide字段就像一个静态断言,没有运行时钩子,也没有回调入口。
容易忽略的关键点
很多人试图在pre-install-cmd里检查composer show --provides输出,指望据此动态启用功能——这行不通。因为show --provides查的是当前锁文件已解析出的提供关系,而pre-install-cmd运行时,依赖树还没开始解析,composer.lock可能根本没变,结果永远滞后或为空。真正可靠的时机只有post-autoload-dump之后,并且必须依赖真实包作为载体。











