本文目录导读:

在 PHP 中,事件管理(Event Management)通常指的是观察者模式(Observer Pattern)或 发布-订阅模式(Publish-Subscribe Pattern),PHP 本身没有内置原生的“事件系统”(如 Node.js 的 EventEmitter),但你可以通过以下几种方式来实现。
下面我会从简单到复杂介绍几种常见的 PHP 事件管理方法。
手动实现:简单的回调钩子(最常用)
这是最轻量级的方法,常见于 WordPress、Laravel 等框架的底层,核心思想是在代码的特定位置执行一组注册的回调函数。
核心代码:
<?php
class EventManager {
private static array $listeners = [];
// 注册一个监听器
public static function listen(string $event, callable $callback): void {
self::$listeners[$event][] = $callback;
}
// 触发事件
public static function dispatch(string $event, ...$args): void {
if (!empty(self::$listeners[$event])) {
foreach (self::$listeners[$event] as $callback) {
call_user_func($callback, ...$args);
}
}
}
}
// ---- 使用 ----
// 1. 注册事件监听
EventManager::listen('user.login', function($username) {
echo "$username logged in. <br>";
});
EventManager::listen('user.login', function($username) {
// 记录日志
file_put_contents('log.txt', "$username login at " . time() . PHP_EOL, FILE_APPEND);
});
// 2. 触发事件
EventManager::dispatch('user.login', 'Alice');
优点:完全自主控制,无依赖。
缺点:功能简单,不支持停止传播、优先级、异步等高级特性。
使用 PSR-14 规范:事件调度器
PSR-14(Event Dispatcher)是 PHP-FIG 组织的标准,定义了事件调度器(Dispatcher)和监听器(Listener)的接口,很多现代框架(如 Symfony、Laravel)都遵循这个规范。
核心接口:
EventDispatcherInterface:分发事件ListenerProviderInterface:提供给定事件的监听器
常见实现:你可以直接使用 Symfony 的 EventDispatcher 组件。
安装:
composer require symfony/event-dispatcher
示例:
<?php
require 'vendor/autoload.php';
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\EventDispatcher\Event;
// 1. 定义自定义事件类(可选)
class UserLoginEvent extends Event {
public function __construct(public string $username) {}
}
// 2. 创建调度器
$dispatcher = new EventDispatcher();
// 3. 注册监听器
$dispatcher->addListener('user.login', function(UserLoginEvent $event) {
echo "User {$event->username} logged in. <br>";
});
// 4. 还可以通过实现 EventSubscriberInterface 来批量注册
class LogSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents(): array {
return [
'user.login' => 'onUserLogin',
];
}
public function onUserLogin(UserLoginEvent $event): void {
file_put_contents('log.txt', "{$event->username} logged in", FILE_APPEND);
}
}
$dispatcher->addSubscriber(new LogSubscriber());
// 5. 触发事件
$event = new UserLoginEvent('Bob');
$dispatcher->dispatch($event, 'user.login'); // 注意参数顺序(事件对象,事件名称)
优点:标准化、性能好(支持优先级、停止传播 $event->stopPropagation())、可测试。
缺点:需要额外包。
面向对象封装:EventEmitter(类似 Node.js)
如果你更习惯 Node.js 的 EventEmitter 风格,可以轻松实现一个 PHP 版。
<?php
class EventEmitter {
private array $listeners = [];
public function on(string $event, callable $listener): void {
$this->listeners[$event][] = $listener;
}
public function emit(string $event, ...$args): void {
if (isset($this->listeners[$event])) {
foreach ($this->listeners[$event] as $listener) {
$listener(...$args);
}
}
}
public function removeListener(string $event, callable $listener): void {
if (isset($this->listeners[$event])) {
$key = array_search($listener, $this->listeners[$event], true);
if ($key !== false) {
unset($this->listeners[$event][$key]);
}
}
}
public function removeAllListeners(?string $event = null): void {
if ($event) {
unset($this->listeners[$event]);
} else {
$this->listeners = [];
}
}
}
// 使用
$emitter = new EventEmitter();
$emitter->on('data', function($data) {
echo "Received: $data\n";
});
$emitter->emit('data', 'Hello World');
很多 PHP 异步框架(如 ReactPHP、Amp)都使用这种模式。
框架内置的事件系统
如果你使用已知框架,通常不需要自己实现,框架已经提供。
Laravel
// 注册事件和监听器 (EventServiceProvider)
protected $listen = [
'App\Events\UserLoggedIn' => [
'App\Listeners\LogLogin',
'App\Listeners\SendWelcomeEmail',
],
];
// 触发事件
event(new UserLoggedIn($user));
Symfony
// 使用依赖注入,通过 dispatcher 触发 $this->dispatcher->dispatch(new UserLoginEvent($user), UserLoginEvent::class);
Yii2
// 组件触发事件
$component->on('beforeLogin', [$model, 'validateLogin']);
$component->trigger('beforeLogin', new Event(['data' => $data]));
WordPress
// 钩子机制(类似事件)
do_action('user_login', $user_login);
add_action('user_login', 'my_custom_logging_function');
我应该怎么选?
| 场景 | 推荐方式 |
|---|---|
| 小型项目、框架偏上层的简单功能 | 手动回调钩子(静态类) |
| 中型项目、需要可测试性 | Symfony EventDispatcher(PSR-14) |
| 异步/非阻塞应用 | ReactPHP EventEmitter |
| 使用 Laravel/Symfony/Yii2/WordPress | 直接使用框架内置事件系统 |
最佳实践建议:
- 命名规范:事件名使用点号(如
user.login)或类名(如UserLogin::class)。 - 单一责任:每个事件类只携带一个事件的数据。
- 不要滥用:事件系统适合解耦,但过多事件会让代码流难以跟踪。
- 性能:事件监听器应轻量,避免在监听器里执行慢操作(如数据库写入)——可以结合队列。