本文目录导读:

在 PHP 中实现事件总线,通常有两种思路:手动实现(轻量级)和使用成熟库(功能丰富),这里提供完整的实现方案。
手动实现(轻量级事件总线)
这是一个最基础的实现,适合中小型项目:
<?php
/** 事件接口 - 所有事件都实现此接口 */
interface Event { }
/** 监听器接口 */
interface Listener {
public function handle(Event $event): void;
}
/** 事件总线核心类 */
final class EventBus {
private static ?EventBus $instance = null;
private array $listeners = [];
// 单例模式
public static function getInstance(): EventBus {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// 禁止外部实例化
private function __construct() { }
/** 注册监听器 */
public function addListener(string $eventClass, Listener $listener, int $priority = 0): void {
$this->listeners[$eventClass][$priority][] = $listener;
// 按优先级排序
ksort($this->listeners[$eventClass]);
}
/** 注册可调用监听器 */
public function on(string $eventClass, callable $callback, int $priority = 0): void {
$this->addListener($eventClass, new CallbackListener($callback), $priority);
}
/** 触发事件 */
public function dispatch(Event $event): void {
$eventClass = get_class($event);
if (!isset($this->listeners[$eventClass])) {
return;
}
foreach ($this->listeners[$eventClass] as $priority => $listeners) {
foreach ($listeners as $listener) {
$listener->handle($event);
}
}
}
/** 移除监听器 */
public function removeListener(string $eventClass, Listener $listener): void {
foreach ($this->listeners[$eventClass] ?? [] as $priority => $listeners) {
$this->listeners[$eventClass][$priority] = array_filter(
$listeners,
fn($l) => $l !== $listener
);
}
}
}
/** 可调用监听器适配器 */
class CallbackListener implements Listener {
private $callback;
public function __construct(callable $callback) {
$this->callback = $callback;
}
public function handle(Event $event): void {
call_user_func($this->callback, $event);
}
}
// ===== 使用示例 =====
/** 定义事件 */
class UserRegistered implements Event {
public function __construct(public string $email, public string $name) { }
}
class OrderCreated implements Event {
public function __construct(public int $orderId, public float $amount) { }
}
/** 定义监听器 */
class SendWelcomeEmail implements Listener {
public function handle(Event $event): void {
// 这里触发时不阻塞业务
echo "📧 发送欢迎邮件到: {$event->email}\n";
}
}
class CreateUserBin implements Listener {
public function handle(Event $event): void {
// 这里触发时不阻塞业务
echo "📁 为用户创建初始目录\n";
}
}
class SendOrderNotification implements Listener {
public function handle(Event $event): void {
echo "🔔 订单 #{$event->orderId} 金额会计入报表\n";
}
}
// 业务代码
$bus = EventBus::getInstance();
// 注册监听器
$bus->addListener(UserRegistered::class, new SendWelcomeEmail(), 10);
$bus->addListener(UserRegistered::class, new CreateUserBin(), 20);
// 使用闭包
$bus->on(OrderCreated::class, function(OrderCreated $event) {
echo "📦 创建订单通知: #{$event->orderId}\n";
});
// 业务代码中触发事件(不阻塞业务)
$bus->dispatch(new UserRegistered('huang@example.com', 'Huang'));
$bus->dispatch(new OrderCreated(1001, 299.99));
改进版(支持异步队列、事件继承)
<?php
interface Event {
public function getName(): string; // 事件名称
}
interface Listener {
public function handle(Event $event): void;
}
/** 异步事件处理器 */
interface AsyncEventBus {
/** 添加异步监听器,通过队列(如 Redis)处理 */
public function addAsyncListener(string $eventName, string $queueName, Listener $listener): void;
/** 触发异步事件 */
public function dispatchAsync(Event $event, string $queueName): void;
}
final class AdvancedEventBus implements AsyncEventBus {
private array $listeners = [];
private array $asyncListeners = [];
private array $emittedEvents = []; // 用于调试
public function __construct(
private ?EventQueueInterface $queue = null // 队列实现(如 Redis)
) { }
/** 同步监听 */
public function addListener(string $eventName, Listener $listener, int $priority = 0): void {
$this->listeners[$eventName][$priority][] = $listener;
}
/** 同步触发 */
public function dispatch(Event $event): void {
$eventName = $event->getName();
$this->emittedEvents[] = $eventName . ' @ ' . date('H:i:s');
// 处理父类事件
foreach (class_parents($event) as $parentClass) {
$this->handleListeners($parentClass, $event);
}
// 处理自身事件
$this->handleListeners($eventName, $event);
}
private function handleListeners(string $eventName, Event $event): void {
if (!isset($this->listeners[$eventName])) return;
foreach ($this->listeners[$eventName] as $priority => $listeners) {
foreach ($listeners as $listener) {
try {
$listener->handle($event);
} catch (\Throwable $e) {
// 记录错误,不让错误中断主流程
error_log("事件监听器错误: " . $e->getMessage());
}
}
}
}
/** 异步触发 */
public function dispatchAsync(Event $event, string $queueName = 'default'): void {
if ($this->queue) {
$this->queue->push($event, $queueName);
}
}
/** 获取已触发事件(调试用) */
public function getEmittedEvents(): array {
return $this->emittedEvents;
}
}
使用成熟库
Symfony EventDispatcher(推荐)
composer require symfony/event-dispatcher
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\EventDispatcher\Event;
// 定义事件
class OrderPlaced extends Event {
public const NAME = 'order.placed';
public function __construct(
public readonly int $orderId,
public readonly string $orderNumber
) { }
}
// 监听器
class SendOrderEmail {
public function __invoke(OrderPlaced $event): void {
echo "📨 发送订单邮件: {$event->orderNumber}\n";
}
}
// 注册和使用
$dispatcher = new EventDispatcher();
$dispatcher->addListener(OrderPlaced::NAME, new SendOrderEmail());
// 或使用订阅者
class OrderSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents(): array {
return [
OrderPlaced::NAME => [['sendEmail', 10], ['updateInventory', 20]],
];
}
public function sendEmail(OrderPlaced $event): void { /* ... */ }
public function updateInventory(OrderPlaced $event): void { /* ... */ }
}
// 业务代码中触发
$dispatcher->dispatch(new OrderPlaced(1001, 'ORDER-2024-1001'), OrderPlaced::NAME);
League/Event(轻量级)
composer require league/event
use League\Event\Emitter;
use League\Event\Listener;
$emitter = new Emitter();
// 监听
$emitter->addListener('user.registered', new class implements Listener {
public function handle($event): void {
echo "用户注册了: {$event->email}\n";
}
});
// 触发
$emitter->emit('user.registered', (object)['email' => 'user@example.com']);
最佳实践建议
生产环境方案(完整示例)
<?php
// event-bus.php
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
// 1. 定义事件基类
abstract class BaseEvent {
public const NAME = '';
}
// 2. 具体事件
class UserRegisteredEvent extends BaseEvent {
public const NAME = 'user.registered';
public function __construct(
public readonly string $userId,
public readonly string $email
) { }
}
// 3. 监听器接口
interface EventListener {
public function handle(BaseEvent $event): void;
}
// 4. 事件总线封装(单例)
final class AppEventBus {
private static ?AppEventBus $instance = null;
private EventDispatcher $dispatcher;
private array $queuedEvents = []; // 延迟处理的事件
private function __construct() {
$this->dispatcher = new EventDispatcher();
$this->registerDefaultListeners();
}
public static function getInstance(): AppEventBus {
if (self::$instance === null) {
self::$instance = new AppEventBus();
}
return self::$instance;
}
/** 注册默认监听器 */
private function registerDefaultListeners(): void {
// 注册日志监听
$this->dispatcher->addListener(UserRegisteredEvent::NAME, function($event) {
$this->log("事件触发: " . $event::class . " 用户: " . $event->email);
});
}
/** 触发事件 */
public function emit(BaseEvent $event): void {
$this->dispatcher->dispatch($event, $event::NAME);
}
/** 延迟到请求结束后触发 */
public function emitLater(BaseEvent $event): void {
$this->queuedEvents[] = $event;
}
/** 处理延迟事件(通常在框架的 shutdown 钩子中调用) */
public function flushQueuedEvents(): void {
foreach ($this->queuedEvents as $event) {
$this->emit($event);
}
$this->queuedEvents = [];
}
private function log(string $message): void {
// 记录到日志文件
file_put_contents(
__DIR__ . '/events.log',
date('Y-m-d H:i:s') . " - " . $message . "\n",
FILE_APPEND
);
}
}
// 5. 业务代码中的使用
$bus = AppEventBus::getInstance();
// 用户注册流程
$registerUser = function(string $email) use ($bus) {
// 业务逻辑:保存用户...
$userId = 123;
// 触发事件(不阻塞业务)
$bus->emit(new UserRegisteredEvent($userId, $email));
};
$registerUser('newuser@example.com');
$bus->flushQueuedEvents(); // 处理延迟事件(在框架生命周期末尾调用)
性能优化建议
- 避免同步阻塞:对于邮件、短信等耗时操作,使用异步队列
- 优先级控制:关键逻辑监听器优先执行
- 异常隔离:监听器内部异常不影响主逻辑
- 事件命名规范:
对象.动作格式,如user.registered、order.paid
选择建议
| 场景 | 推荐方案 |
|---|---|
| 小型项目、快速开发 | 手动实现的轻量事件总线 |
| 中型项目、有明确事件分类 | Symfony EventDispatcher |
| 大型项目、需要集群广播 | 使用 Redis Pub/Sub 配合 |
| 需要异步处理 | 加上队列(RabbitMQ、Kafka) |
选择适合自己项目的方案,推荐使用 Symfony EventDispatcher(成熟稳定、功能完善、文档丰富),如果是小型项目,手动实现一个轻量级的也完全够用。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播