本文目录导读:

- 直接回调函数(最简单的“事件”)
- 面向对象的事件分发器(自定义类)
- 使用 SplSubject / SplObserver(PHP 内置观察者模式)
- 使用 Symfony EventDispatcher 组件(推荐生产环境)
- 使用事件驱动框架(如 Workerman / Swoole)
- 总结:如何选择?
在PHP中实现“事件响应”机制,通常有几种不同的方式,具体取决于你的使用场景(是简单的回调、面向对象的事件驱动,还是使用现有框架的事件系统)。
以下从 基础到高级 介绍几种常见实现方式:
直接回调函数(最简单的“事件”)
这是最朴素的方式,将函数名或匿名函数作为参数传递。
<?php
// 定义一个“事件触发点”
function onUserLogin($username, $callback) {
echo "用户 {$username} 登录了。\n";
// 执行回调(模拟事件响应)
if (is_callable($callback)) {
call_user_func($callback, $username);
}
}
// 定义事件响应函数
function sendWelcomeEmail($user) {
echo "发送欢迎邮件给 {$user}\n";
}
// 使用:调用事件并传入响应
onUserLogin("Alice", 'sendWelcomeEmail');
// 也可以使用匿名函数
onUserLogin("Bob", function($user) {
echo "记录登录日志:{$user}\n";
});
面向对象的事件分发器(自定义类)
更结构化的方式,适用于插件系统或复杂应用。
<?php
class EventDispatcher {
private $listeners = [];
// 注册事件监听器
public function on($eventName, callable $listener) {
$this->listeners[$eventName][] = $listener;
}
// 触发事件
public function dispatch($eventName, $data = null) {
if (isset($this->listeners[$eventName])) {
foreach ($this->listeners[$eventName] as $listener) {
call_user_func($listener, $data);
}
}
}
}
// 使用
$dispatcher = new EventDispatcher();
// 注册监听
$dispatcher->on('user.registered', function($user) {
echo "新用户注册:{$user['name']}\n";
});
$dispatcher->on('user.registered', function($user) {
echo "发送验证邮件到:{$user['email']}\n";
});
// 触发事件
$dispatcher->dispatch('user.registered', ['name' => 'Alice', 'email' => 'alice@example.com']);
优点:解耦,灵活添加/移除监听器。
使用 SplSubject / SplObserver(PHP 内置观察者模式)
PHP SPL 提供了标准的观察者模式接口,适用于一对多的依赖关系。
<?php
// 被观察者(事件源)
class User implements SplSubject {
private $observers = [];
public $name;
public function __construct($name) {
$this->name = $name;
}
public function attach(SplObserver $observer) {
$this->observers[] = $observer;
}
public function detach(SplObserver $observer) {
$index = array_search($observer, $this->observers, true);
if ($index !== false) {
unset($this->observers[$index]);
}
}
public function notify() {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
// 模拟登录事件
public function login() {
echo "{$this->name} 登录了\n";
$this->notify(); // 触发通知所有观察者
}
}
// 观察者(事件响应者)
class Logger implements SplObserver {
public function update(SplSubject $subject) {
echo "记录日志:{$subject->name} 登录\n";
}
}
class Mailer implements SplObserver {
public function update(SplSubject $subject) {
echo "发送邮件给:{$subject->name}\n";
}
}
// 使用
$user = new User("Alice");
$user->attach(new Logger());
$user->attach(new Mailer());
$user->login(); // 自动触发所有观察者
使用 Symfony EventDispatcher 组件(推荐生产环境)
最流行的PHP事件系统之一,Laravel、Drupal等核心也借鉴了这种思路。
安装:
composer require symfony/event-dispatcher
使用示例:
<?php
require 'vendor/autoload.php';
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
// 1. 定义自定义事件类(可选)
class UserRegisteredEvent extends Event {
const NAME = 'user.registered';
public $user;
public function __construct($user) {
$this->user = $user;
}
}
// 2. 创建调度器
$dispatcher = new EventDispatcher();
// 3. 注册监听器
$dispatcher->addListener(UserRegisteredEvent::NAME, function(UserRegisteredEvent $event) {
echo "发送欢迎邮件给:{$event->user['name']}\n";
});
$dispatcher->addListener(UserRegisteredEvent::NAME, function(UserRegisteredEvent $event) {
echo "记录注册日志\n";
});
// 4. 触发事件
$event = new UserRegisteredEvent(['name' => 'Alice', 'email' => 'alice@example.com']);
$dispatcher->dispatch($event, UserRegisteredEvent::NAME);
使用事件驱动框架(如 Workerman / Swoole)
对于 常驻内存 的PHP应用(如WebSocket、TCP服务),事件响应是核心模式。
// 伪代码示例(Workerman)
$worker = new Worker('websocket://0.0.0.0:8080');
$worker->onConnect = function($connection) {
echo "新连接\n";
};
$worker->onMessage = function($connection, $data) {
echo "收到消息: $data\n";
$connection->send('已收到');
};
$worker->onClose = function($connection) {
echo "连接关闭\n";
};
Worker::runAll();
这里 onConnect、onMessage、onClose 就是预定义的事件。
如何选择?
| 场景 | 推荐方式 |
|---|---|
| 简单的回调逻辑 | 直接回调函数 |
| 中等复杂度,需要解耦 | 自定义 EventDispatcher 或 SplObserver |
| 生产级系统,框架已有 | 使用框架自带事件(Laravel Event、Symfony EventDispatcher) |
| 常驻内存应用(长连接) | Workerman、Swoole 内置事件 |
如果你是新手,建议从 自定义 EventDispatcher 开始理解核心概念,然后过渡到框架中的事件系统。