You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony 5.4 LTS发送邮件时MessageEvent事件未触发问题

Symfony 5.4 Mailer MessageEvent 不触发问题排查与修复

核心原因

按出现概率从高到低排序:

  1. 异步邮件未启动消费进程
    安装symfony/messenger组件后,框架默认会将邮件发送任务注册到异步队列,此时调用send()方法时只会把任务推到队列,不会立即触发MessageEvent。事件仅会在messenger消费进程实际处理发送逻辑时派发,未启动消费进程的话事件永远不会执行。
  2. 监听器代码存在致命错误被静默拦截
    当前自定义订阅者代码里有两处必现错误,prod环境下关闭debug时错误会被吞掉,表现为监听器未执行:
    • Envelope::getSender()返回值是单个Symfony\Component\Mime\Address对象,不是可遍历集合,直接用foreach遍历会抛出TypeError
    • 遍历后调用array_keys()处理Address对象数组无意义,无法得到预期的邮箱地址字符串
  3. 未使用容器注入的Mailer实例发信
    如果手动通过new Mailer()实例化发信类、手动创建Transport对象发信,会绕过框架绑定了事件分发器的Mailer实例,事件完全不会派发。
  4. 容器配置缓存未同步
    执行debug:event-dispatcher显示自定义监听器优先级为0,但代码里getSubscribedEvents()配置的优先级是-255,说明缓存还停留在旧版本,注册的监听器逻辑未更新。

修复步骤

  1. 先清除项目缓存排除缓存干扰:
    bin/console cache:clear
    
  2. 修正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();
        }
    }
    
  3. 检查所有发信逻辑:确保发信处都是通过构造函数注入Symfony\Component\Mailer\MailerInterface实例调用send()方法,禁止手动实例化Mailer或Transport类。
  4. 按需求调整异步配置:
    • 不需要异步发信:删除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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 18:54:25