Symfony 5.4 LTS发送邮件时MessageEvent事件未触发问题
Symfony 5.4 Mailer MessageEvent 不触发问题排查与修复
核心原因
按出现概率从高到低排序:
- 异步邮件未启动消费进程
安装symfony/messenger组件后,框架默认会将邮件发送任务注册到异步队列,此时调用send()方法时只会把任务推到队列,不会立即触发MessageEvent。事件仅会在messenger消费进程实际处理发送逻辑时派发,未启动消费进程的话事件永远不会执行。 - 监听器代码存在致命错误被静默拦截
当前自定义订阅者代码里有两处必现错误,prod环境下关闭debug时错误会被吞掉,表现为监听器未执行:Envelope::getSender()返回值是单个Symfony\Component\Mime\Address对象,不是可遍历集合,直接用foreach遍历会抛出TypeError- 遍历后调用
array_keys()处理Address对象数组无意义,无法得到预期的邮箱地址字符串
- 未使用容器注入的Mailer实例发信
如果手动通过new Mailer()实例化发信类、手动创建Transport对象发信,会绕过框架绑定了事件分发器的Mailer实例,事件完全不会派发。 - 容器配置缓存未同步
执行debug:event-dispatcher显示自定义监听器优先级为0,但代码里getSubscribedEvents()配置的优先级是-255,说明缓存还停留在旧版本,注册的监听器逻辑未更新。
修复步骤
- 先清除项目缓存排除缓存干扰:
bin/console cache:clear - 修正
MailerLoggerSubscriber里的错误逻辑,参考如下可运行代码:<?php namespace App\EventSubscriber; use App\Entity\MailLog; use App\Repository\MailLogRepository; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Mailer\Event\MessageEvent; use Symfony\Component\Mime\Email; class MailerLoggerSubscriber implements EventSubscriberInterface { public function __construct( protected MailLogRepository $mailerRepository, protected EntityManagerInterface $entityManager, protected LoggerInterface $logger ) {} public static function getSubscribedEvents(): array { return [ MessageEvent::class => ['onMessage', -255], ]; } public function onMessage(MessageEvent $event): void { // 跳过已入队的消息,等实际发送时再记录日志,避免重复记录 if ($event->isQueued()) { return; } $message = $event->getMessage(); $envelope = $event->getEnvelope(); // 处理发件人:getSender()返回单个Address对象,无需遍历 $from = [$envelope->getSender()->toString()]; // 处理收件人:遍历转成字符串格式邮箱 $to = array_map(fn($address) => $address->toString(), $envelope->getRecipients()); $mailLog = new MailLog(); $mailLog->setSentAt(new \DateTime()); $mailLog->setFrom($from); $mailLog->setTo($to); // 取邮件真实主题,不要硬编码 $mailLog->setSubject($message instanceof Email ? $message->getSubject() : ''); $mailLog->setMessage($message->toString()); $mailLog->setResult($event->isRejected() ? 'failed' : 'success'); $this->entityManager->persist($mailLog); $this->entityManager->flush(); } } - 检查所有发信逻辑:确保发信处都是通过构造函数注入
Symfony\Component\Mailer\MailerInterface实例调用send()方法,禁止手动实例化Mailer或Transport类。 - 按需求调整异步配置:
- 不需要异步发信:删除
config/packages/messenger.yaml里如下路由配置,邮件会同步发送,事件在当前请求/命令生命周期内直接触发:# 删除该段配置 Symfony\Component\Mailer\Messenger\SendEmailMessage: async - 需要保留异步发信:启动messenger消费进程即可,事件会在消费时正常触发:
bin/console messenger:consume async -vv
- 不需要异步发信:删除
修复后可在dev环境下执行发信操作,查看var/log/dev.log或数据库mail_log表确认日志是否正常写入。
内容的提问来源于stack exchange,提问作者Bellu
相关产品推荐
相关产品推荐

