事件系统 #

一、事件概述 #

1.1 事件机制 #

Symfony事件系统基于观察者模式,允许不同组件之间松耦合通信。

text
┌─────────────────────────────────────────────────────┐
│                  事件流程                            │
├─────────────────────────────────────────────────────┤
│                                                     │
│   事件派发器                                        │
│       │                                             │
│       ├── 派发事件                                  │
│       │                                             │
│       ├── 监听器1 ──→ 处理事件                      │
│       ├── 监听器2 ──→ 处理事件                      │
│       └── 监听器3 ──→ 处理事件                      │
│                                                     │
└─────────────────────────────────────────────────────┘

1.2 核心组件 #

组件 说明
EventDispatcher 事件派发器
Event 事件对象
EventSubscriberInterface 事件订阅者接口
EventListener 事件监听器

二、创建事件 #

2.1 定义事件类 #

php
<?php

namespace App\Event;

use App\Entity\User;
use Symfony\Contracts\EventDispatcher\Event;

class UserRegisteredEvent extends Event
{
    public const NAME = 'user.registered';

    public function __construct(
        private User $user
    ) {}

    public function getUser(): User
    {
        return $this->user;
    }
}

2.2 派发事件 #

php
<?php

namespace App\Service;

use App\Entity\User;
use App\Event\UserRegisteredEvent;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class UserService
{
    public function __construct(
        private EntityManagerInterface $entityManager,
        private EventDispatcherInterface $eventDispatcher
    ) {}

    public function registerUser(array $data): User
    {
        $user = new User();
        $user->setName($data['name']);
        $user->setEmail($data['email']);

        $this->entityManager->persist($user);
        $this->entityManager->flush();

        $event = new UserRegisteredEvent($user);
        $this->eventDispatcher->dispatch($event, UserRegisteredEvent::NAME);

        return $user;
    }
}

三、事件监听器 #

3.1 创建监听器 #

php
<?php

namespace App\EventListener;

use App\Event\UserRegisteredEvent;
use Psr\Log\LoggerInterface;

class UserRegisteredListener
{
    public function __construct(
        private LoggerInterface $logger
    ) {}

    public function onUserRegistered(UserRegisteredEvent $event): void
    {
        $user = $event->getUser();
        
        $this->logger->info('User registered', [
            'id' => $user->getId(),
            'email' => $user->getEmail(),
        ]);
    }
}

3.2 注册监听器 #

yaml
# config/services.yaml
services:
    App\EventListener\UserRegisteredListener:
        tags:
            - { name: kernel.event_listener, event: 'user.registered', method: onUserRegistered }

四、事件订阅者 #

4.1 创建订阅者 #

php
<?php

namespace App\EventSubscriber;

use App\Event\UserRegisteredEvent;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class UserEventSubscriber implements EventSubscriberInterface
{
    public function __construct(
        private LoggerInterface $logger,
        private MailerInterface $mailer
    ) {}

    public static function getSubscribedEvents(): array
    {
        return [
            UserRegisteredEvent::NAME => [
                ['logRegistration', 10],
                ['sendWelcomeEmail', 0],
            ],
        ];
    }

    public function logRegistration(UserRegisteredEvent $event): void
    {
        $user = $event->getUser();
        
        $this->logger->info('User registered', [
            'id' => $user->getId(),
            'email' => $user->getEmail(),
        ]);
    }

    public function sendWelcomeEmail(UserRegisteredEvent $event): void
    {
        $user = $event->getUser();
        
        $this->mailer->send($user->getEmail(), 'Welcome!');
    }
}

4.2 自动注册订阅者 #

yaml
# config/services.yaml
services:
    _defaults:
        autoconfigure: true

    App\EventSubscriber\UserEventSubscriber: ~

五、内置事件 #

5.1 内核事件 #

php
<?php

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class KernelSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::REQUEST => [['onKernelRequest', 20]],
            KernelEvents::RESPONSE => [['onKernelResponse', 0]],
            KernelEvents::EXCEPTION => [['onKernelException', 0]],
            KernelEvents::CONTROLLER => [['onKernelController', 0]],
            KernelEvents::TERMINATE => [['onKernelTerminate', 0]],
        ];
    }

    public function onKernelRequest(RequestEvent $event): void
    {
        $request = $event->getRequest();
        // 处理请求
    }

    public function onKernelResponse(ResponseEvent $event): void
    {
        $response = $event->getResponse();
        // 处理响应
    }

    public function onKernelException(ExceptionEvent $event): void
    {
        $exception = $event->getThrowable();
        // 处理异常
    }
}

5.2 安全事件 #

php
<?php

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
use Symfony\Component\Security\Http\Event\LogoutEvent;
use Symfony\Component\Security\Http\Event\LoginFailureEvent;

class SecuritySubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            LoginSuccessEvent::class => 'onLoginSuccess',
            LoginFailureEvent::class => 'onLoginFailure',
            LogoutEvent::class => 'onLogout',
        ];
    }

    public function onLoginSuccess(LoginSuccessEvent $event): void
    {
        $user = $event->getUser();
        // 记录登录成功
    }

    public function onLoginFailure(LoginFailureEvent $event): void
    {
        $exception = $event->getException();
        // 记录登录失败
    }

    public function onLogout(LogoutEvent $event): void
    {
        // 记录登出
    }
}

六、事件优先级 #

6.1 设置优先级 #

php
<?php

public static function getSubscribedEvents(): array
{
    return [
        KernelEvents::REQUEST => [
            ['firstMethod', 30],
            ['secondMethod', 20],
            ['thirdMethod', 10],
        ],
    ];
}

6.2 优先级说明 #

text
优先级规则:
├── 数字越大,优先级越高
├── 默认优先级为 0
├── 相同优先级按注册顺序执行
└── 优先级范围:-255 到 255

七、停止事件传播 #

7.1 停止传播 #

php
<?php

namespace App\EventSubscriber;

use App\Event\UserRegisteredEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class ValidationSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            UserRegisteredEvent::NAME => [['validate', 100]],
        ];
    }

    public function validate(UserRegisteredEvent $event): void
    {
        $user = $event->getUser();
        
        if (!$this->isValid($user)) {
            $event->stopPropagation();
        }
    }

    private function isValid(User $user): bool
    {
        return true;
    }
}

八、自定义事件派发器 #

8.1 创建派发器 #

php
<?php

namespace App\Service;

use Symfony\Component\EventDispatcher\EventDispatcher;

class CustomEventDispatcher extends EventDispatcher
{
    public function dispatchWithLog(object $event, string $eventName = null): object
    {
        $this->logger->info("Dispatching event: $eventName");
        
        $result = parent::dispatch($event, $eventName);
        
        $this->logger->info("Event dispatched: $eventName");
        
        return $result;
    }
}

九、事件最佳实践 #

9.1 命名规范 #

text
事件命名规范:
├── 使用常量定义事件名
├── 格式:entity.action 或 component.action
├── 示例:user.registered, order.completed
└── 使用点号分隔,小写字母

9.2 设计原则 #

text
事件设计原则:
├── 事件类应该是不可变的
├── 事件处理应该是幂等的
├── 避免在事件中执行耗时操作
├── 使用优先级控制执行顺序
└── 合理使用事件停止传播

十、总结 #

本章学习了:

  • 事件系统概述
  • 创建和派发事件
  • 事件监听器
  • 事件订阅者
  • 内置事件
  • 事件优先级
  • 停止事件传播
  • 最佳实践

下一章将学习 命令行工具

最后更新:2026-03-28