Composer 不提供“虚拟包”机制来统一消息队列驱动。所谓“虚拟包”仅是 Packagist 占位符,通过 provide 声明能力供依赖约束,不能自动切换 transport 或替代配置与适配器代码。

Composer 本身不提供“虚拟包”机制来统一消息队列驱动——这是个常见误解。所谓“虚拟包”,其实是 Packagist 上不存在真实代码的占位包,靠 composer.json 中的 provide 字段声明能力,供其他包做依赖约束用。它不能自动切换 transport 实现,也不能替代配置或适配器代码。
为什么有人想用“虚拟包”统一队列驱动?
实际场景是:多个项目共用一套消息处理逻辑(比如都调 $bus->dispatch()),但有的用 Redis transport,有的用 Doctrine,有的跑测试用 sync://。开发者希望“写一次 handler,换 transport 不改代码”。这需求合理,但解法不在 Composer 包声明层,而在运行时配置和抽象层。
- “虚拟包”如
symfony/transport-redis并不存在;symfony/messenger本身已通过provide声明了symfony/transport能力,但只是告诉依赖方“我支持 transport 接口”,不等于自动加载任何 transport - 真正起作用的是
config/packages/messenger.php里对transports的定义,以及对应 transport 包是否已安装(如symfony/redis-messenger) - 如果你强行在
composer.json里写"provide": {"myorg/queue-driver": "1.0"},别的包可以require它,但不会因此获得 Redis 连接能力——那还得装包、配 URL、启服务
真正能“统一驱动”的做法:靠配置 + 条件性 require
你不需要虚构包,而是让不同环境按需加载 transport 包,并保持 Messenger 配置结构一致。关键在 composer 和 config 的配合:
- 开发环境用
symfony/messenger+symfony/amqp-messenger,生产环境切到symfony/redis-messenger:两个包可同时 require,Messenger 自动根据TRANSPORT_DSN环境变量选 transport,不用改 PHP 代码 - 在
composer.json中把 transport 包列为require-dev或按环境分组(如用composer install --with=redis配合自定义脚本),避免非必要包进生产镜像 - 若用 Laravel,
laravel/horizon或spatie/laravel-schedule这类包本身已封装 transport 切换逻辑,它们不靠虚拟包,而是读QUEUE_CONNECTION环境变量决定用 Redis 还是 Database - Yii2 项目里,
yiisoft/yii2-queue的driver配置项直接指定redis/db/amqp,背后 require 的是pda/pheanstalk或php-amqplib/php-amqplib,Composer 只管装,不参与路由决策
容易踩的坑:以为 provide 能自动启用 transport
常见错误现象:composer require symfony/messenger 后,messenger:consume 命令可用,但执行时报 No transport found for "async"。这不是包没装对,而是:
-
symfony/redis-messenger没装(只装symfony/messenger不够) -
TRANSPORT_DSN=redis://localhost没设,或拼错成redis://127.0.0.1:6379(Redis transport 要求 schema 显式,localhost和127.0.0.1在某些 Redis 客户端行为不同) - Doctrine transport 依赖
doctrine/dbal,但DATABASE_URL指向 SQLite 文件且该文件不可写,transport 会静默跳过,不报错也不注册 - 在非 Symfony 全栈项目中,
provide声明对命令注册完全无效——messenger:consume是 Bundle 提供的,不是组件自带的
复杂点在于:transport 的“统一”从来不是靠 Composer 元数据实现的,而是靠框架对 TransportFactoryInterface 的实现收敛、环境变量驱动配置、以及 transport 包自身的 DSN 解析逻辑。别在 provide 上花时间,优先检查 TRANSPORT_DSN 是否生效、对应服务是否可达、handler 是否被正确标记为 AsMessageHandler。











