事件系统
概念说明
Viswoole 的事件系统基于 发布-订阅(Publish-Subscribe) 模式实现,允许各个模块之间通过事件进行松耦合通信。事件系统由 Event 类提供,支持:
- 注册监听器响应特定事件
- 触发事件并传递数据给监听器
- 移除不再需要的监听器
- 类名形式的批量监听器注册
工作原理
核心API
| 方法 | 说明 |
|---|---|
on(event, handle, limit) | 注册事件监听器,limit 限制触发次数 |
emit(event, arguments) | 触发事件,按注册顺序通知所有监听器 |
off(event, id) | 移除指定事件的指定监听器 |
offAll() | 移除所有已注册的监听器 |
事件名规范
- 事件名 大小写不敏感,内部统一转为小写存储和匹配
- 推荐使用 点分命名法:
模块.动作,如user.login、order.created - 支持 “事件名.方法名” 形式,用于触发类监听器的特定公共方法
类名监听器
当传入类名作为监听器时,框架会自动扫描该类的 公共非静态方法,将每个方法注册为独立的监听器。这使得一个监听器类可以集中管理某类事件的所有相关处理逻辑。
监听器执行流程
text
emit('user.login', [$userId])
│
▼
┌──────────────────┐
│ 查找 'user.login' │
│ 的所有监听器 │
└────────┬─────────┘
│
▼
┌──────┴──────┐
│ │
▼ ▼
闭包监听器 类方法监听器
handle($args) Method($args)
│ │
▼ ▼
检查 limit 检查 limit
│ │
▼ ▼
执行回调 达到限制则 off代码示例
基本用法
php
use Viswoole\Core\Facade\Event;
// 注册监听器
Event::on('user.login', function ($userId) {
Log::info("用户 {$userId} 已登录");
});
// 触发事件
Event::emit('user.login', [$userId]);限制触发次数
某些场景下只需要监听器响应有限次数:
php
// 仅响应前 3 次,之后自动移除
Event::on('cache.miss', function ($key) {
Log::warning("缓存未命中: {$key}");
}, 3);类名监听器
将相关的事件处理逻辑组织到一个类中:
php
class OrderListener
{
public function created(array $orderData): void
{
// 订单创建后的处理
Log::info("订单创建: {$orderData['order_no']}");
}
public function paid(array $orderData): void
{
// 订单支付完成后的处理
Notification::send($orderData['user_id'], '支付成功');
}
public function shipped(array $orderData): void
{
// 订单发货后的处理
SmsService::send($orderData['phone'], '您的订单已发货');
}
}
// 注册整个类作为监听器,自动扫描公共方法
Event::on('order', OrderListener::class);
// 通过 "事件名.方法名" 触发特定方法
Event::emit('order.created', [$orderData]);
Event::emit('order.paid', [$orderData]);
Event::emit('order.shipped', [$orderData]);移除监听器
php
// on() 返回监听器 ID,可用于精确移除
$listenerId = Event::on('user.register', function ($data) { /* ... */ });
// 移除单个监听器
Event::off('user.register', $listenerId);
// 移除某事件的所有监听器
Event::off('user.register');
// 清空所有监听器(慎用)
Event::offAll();全局辅助函数
php
use Viswoole\Core\Facade\Event;
// 注册监听器
Event::on('app.booted', function () {
echo '应用启动完成';
});最佳实践
1. 事件命名规范
采用统一的命名约定,保持项目一致性:
php
// ✅ 推荐:模块.动作 格式,语义清晰
Event::on('user.created', ...);
Event::on('order.paid', ...);
Event::on('cache.cleared', ...);
// ❌ 不推荐:过于简短或格式混乱
Event::on('userCreate', ...);
Event::on('order_pay', ...);
Event::on('done', ...);2. 合理使用类名监听器
当一个事件涉及多个关联操作时,使用类监听器组织代码:
php
// ✅ 推荐:同一领域的多个子事件用类监听器管理
Event::on('payment', PaymentListener::class);
// PaymentListener 包含: success(), failed(), refunded() 等方法
// ❌ 不推荐:零散注册多个闭包
Event::on('payment.success', fn($d) => ...);
Event::on('payment.failed', fn($d) => ...);
Event::on('payment.refunded', fn($d) => ...);3. 监听器保持轻量
监听器应尽快返回,耗时操作应异步处理:
php
// ✅ 推荐:快速返回,耗时任务异步投递
// 先注册任务主题(通常在服务提供者中注册一次)
Task::register('order.notify', function ($task) {
$orderData = $task->data;
NotificationService::send($orderData);
StatisticsService::record($orderData);
});
Event::on('order.created', function ($orderData) {
// 同步:记录日志(快速)
Log::info("订单创建: {$orderData['order_no']}");
// 异步:投递任务到 Task Worker 处理
Task::emit('order.notify', $orderData);
});
// ❌ 不推荐:在监听器中执行耗时同步操作
Event::on('order.created', function ($orderData) {
sleep(5); // 阻塞当前协程!
ExternalApi::syncData($orderData);
});4. 注意事件触发的时机
在服务提供者的 boot() 方法中注册事件监听,确保所有服务都已就绪:
php
class EventServiceProvider extends Provider
{
public function register(): void {}
public function boot(): void
{
// boot 阶段所有服务已注册完毕,可以安全地使用其他服务
Event::on('user.login', function ($userId) {
// 此时 Logger、Cache 等服务已经可用
app()->get(Logger::class)->info("用户 {$userId} 登录");
});
}
}