事件系统 #
一、事件概述 #
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