本文目录导读:

设计一个PHP事件系统,核心是解耦代码逻辑——让“发生什么”和“做什么”分离,下面从设计原则到具体实现,给出一个完整的方案。
核心概念
- 事件(Event):描述发生的事情,携带数据
- 监听器(Listener):对事件做出反应,执行逻辑
- 调度器(Dispatcher):触发事件,通知所有监听器
- 订阅器(Subscriber):批量注册多个监听器
基础架构设计
事件接口定义
<?php
namespace App\Event;
interface EventInterface
{
/**
* 获取事件名称(可选,可用于自动映射)
*/
public function getName(): string;
/**
* 停止事件传播
*/
public function isPropagationStopped(): bool;
/**
* 停止传播
*/
public function stopPropagation(): void;
}
基础事件类
<?php
namespace App\Event;
use App\Event\EventInterface;
class Event implements EventInterface
{
protected string $name;
protected bool $propagationStopped = false;
protected array $data = [];
public function __construct(string $name = '', array $data = [])
{
$this->name = $name ?: static::class;
$this->data = $data;
}
public function getName(): string
{
return $this->name;
}
public function getData(): array
{
return $this->data;
}
public function get(string $key, mixed $default = null): mixed
{
return $this->data[$key] ?? $default;
}
public function set(string $key, mixed $value): self
{
$this->data[$key] = $value;
return $this;
}
public function isPropagationStopped(): bool
{
return $this->propagationStopped;
}
public function stopPropagation(): void
{
$this->propagationStopped = true;
}
}
事件调度器
<?php
namespace App\Event;
use App\Event\EventInterface;
use InvalidArgumentException;
class EventDispatcher
{
/**
* 事件 => 监听器列表
* @var array<string, array<int, callable|array>>
*/
private array $listeners = [];
/**
* 注册监听器
*/
public function addListener(string $eventName, callable $listener, int $priority = 0): void
{
if (!isset($this->listeners[$eventName])) {
$this->listeners[$eventName] = [];
}
// 使用优先级排序
$this->listeners[$eventName][$priority][] = $listener;
krsort($this->listeners[$eventName]); // 高优先级在前
}
/**
* 移除监听器
*/
public function removeListener(string $eventName, callable $listener): bool
{
if (!isset($this->listeners[$eventName])) {
return false;
}
foreach ($this->listeners[$eventName] as $priority => $listeners) {
foreach ($listeners as $key => $registeredListener) {
if ($registeredListener === $listener) {
unset($this->listeners[$eventName][$priority][$key]);
return true;
}
}
}
return false;
}
/**
* 触发事件
*/
public function dispatch(EventInterface $event): EventInterface
{
$eventName = $event->getName();
if (!isset($this->listeners[$eventName])) {
return $event;
}
foreach ($this->listeners[$eventName] as $priority => $listeners) {
if ($event->isPropagationStopped()) {
break;
}
foreach ($listeners as $listener) {
if ($event->isPropagationStopped()) {
break;
}
$result = call_user_func($listener, $event);
// 如果监听器返回 false,停止传播
if ($result === false) {
$event->stopPropagation();
}
}
}
return $event;
}
/**
* 获取所有监听器
*/
public function getListeners(string $eventName = null): array
{
if ($eventName !== null) {
return $this->flatten($this->listeners[$eventName] ?? []);
}
$all = [];
foreach ($this->listeners as $name => $listeners) {
$all[$name] = $this->flatten($listeners);
}
return $all;
}
/**
* 扁平化优先级数组
*/
private function flatten(array $listeners): array
{
$result = [];
foreach ($listeners as $priority => $items) {
foreach ($items as $item) {
$result[] = $item;
}
}
return $result;
}
}
订阅器支持
<?php
namespace App\Event;
interface EventSubscriberInterface
{
/**
* 返回订阅的事件列表
*
* @return array<string, array|string>
* 例:['user.created' => 'onUserCreated', 'user.updated' => ['onUserUpdated', 10]]
*/
public static function getSubscribedEvents(): array;
}
在调度器中添加订阅器注册方法:
public function addSubscriber(EventSubscriberInterface $subscriber): void
{
foreach ($subscriber::getSubscribedEvents() as $eventName => $params) {
if (is_string($params)) {
$this->addListener($eventName, [$subscriber, $params]);
} elseif (is_array($params)) {
foreach ($params as $listener) {
if (is_string($listener)) {
$this->addListener($eventName, [$subscriber, $listener]);
} elseif (is_array($listener) && count($listener) === 2) {
$this->addListener($eventName, [$subscriber, $listener[0]], $listener[1]);
}
}
}
}
}
具体事件示例
<?php
namespace App\Event\Domain;
use App\Event\Event;
class UserCreatedEvent extends Event
{
public const NAME = 'user.created';
public function __construct(
private readonly int $userId,
private readonly array $userData = []
) {
parent::__construct(self::NAME);
}
public function getUserId(): int
{
return $this->userId;
}
public function getUserData(): array
{
return $this->userData;
}
}
事件总线(封装调度器)
<?php
namespace App\Event;
class EventBus
{
private static ?EventBus $instance = null;
private EventDispatcher $dispatcher;
private function __construct()
{
$this->dispatcher = new EventDispatcher();
}
public static function getInstance(): EventBus
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function emit(EventInterface $event): void
{
$this->dispatcher->dispatch($event);
}
public function on(string $eventName, callable $listener, int $priority = 0): void
{
$this->dispatcher->addListener($eventName, $listener, $priority);
}
public function subscribe(EventSubscriberInterface $subscriber): void
{
$this->dispatcher->addSubscriber($subscriber);
}
}
使用示例
<?php
// 注册监听器
$dispatcher = new EventDispatcher();
// 方法一:闭包监听器
$dispatcher->addListener('user.created', function (UserCreatedEvent $event) {
// 发送欢迎邮件
Mailer::send($event->getUserData()['email'], '欢迎注册');
}, 10); // 优先级10
// 方法二:类方法监听器
class SendSmsListener
{
public function onUserCreated(UserCreatedEvent $event): void
{
// 发送短信通知
}
}
$dispatcher->addListener('user.created', [new SendSmsListener(), 'onUserCreated']);
// 触发事件
$event = new UserCreatedEvent(123, ['email' => 'user@example.com']);
$dispatcher->dispatch($event);
高级特性设计
异步事件
<?php
trait AsyncEventTrait
{
private bool $async = false;
private int $delaySeconds = 0;
public function setAsync(bool $async = true): self
{
$this->async = $async;
return $this;
}
public function isAsync(): bool
{
return $this->async;
}
public function setDelay(int $seconds): self
{
$this->delaySeconds = $seconds;
return $this;
}
}
事件追踪
<?php
class TraceEventDispatcher extends EventDispatcher
{
private array $history = [];
public function dispatch(EventInterface $event): EventInterface
{
$this->history[] = [
'event' => $event->getName(),
'time' => microtime(true),
'memory' => memory_get_usage(),
];
return parent::dispatch($event);
}
public function getHistory(): array
{
return $this->history;
}
}
容器集成
<?php
// 与依赖注入容器结合
class ContainerAwareDispatcher extends EventDispatcher
{
public function __construct(
private readonly ContainerInterface $container
) {}
public function dispatchNamed(string $eventName, array $params = []): EventInterface
{
$event = new Event($eventName, $params);
return $this->dispatch($event);
}
// 从容器解析监听器
public function registerFromContainer(string $eventName, string $serviceId, string $method = '__invoke'): void
{
$this->addListener($eventName, function ($event) use ($serviceId, $method) {
$service = $this->container->get($serviceId);
return $service->{$method}($event);
});
}
}
设计要点总结
规范建议
- 命名规范:
{实体}.{动作},如user.created、order.paid - 参数传递:通过事件对象传递数据,不要直接传数组
- 返回值约定:监听器返回
false或调用stopPropagation()停止传播 - 异常处理:监听器异常不影响其他监听器执行
性能考虑
// 使用索引优化
private array $listenerIndex = [];
// 事件名称哈希索引
public function addListener(string $eventName, callable $listener, int $priority = 0): void
{
$hash = md5($eventName);
// ... 使用哈希索引
}
测试建议
- 单元测试:测试调度器逻辑
- 集成测试:测试具体业务事件
- 性能测试:大量事件触发下的性能表现
这个设计涵盖了事件系统的主要功能需求,可以根据项目实际情况选择合适的部分使用。