Laravel 事件系统

Laravel框架
628
0
0
2022-04-18
标签   Laravel基础

Laravel 事件系统

Dispatcher 是一个观察者模式的实现,它最大的优点就是在对象之间没有依赖关系的情况下,把对象内部的状态暴露给对该状态感兴趣的对象,

通常我们把这类对象称为监听器,监听器的本质是一个可执行的函数;状态称为事件,通常事件都有一个唯一的名称,和携带一个可选的状态数据。

通过下面的例子可以体验下事件系统的优点。

class Page
{
protected $dispatcher;
public function __construct(Dispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
public function render()
{
$this->dispatcher->dispatch('css.loaded');
$this->dispatcher->dispatch('javascript.loaded');
$this->dispatcher->dispatch('html.painting');
$this->dispatcher->dispatch('html.painted');
}
}
$dispatcher = new Dispatcher();
$dispatcher->listen('css.loaded', function () {
echo "css loaded ok \n";
});
$dispatcher->listen('javascript.loaded', function () {
echo "javascript loaded ok \n";
});
$dispatcher->listen('html.painting', function () {
echo "ready for painting\n";
});
$dispatcher->listen('html.painted', function () {
echo "render ok \n";
});
$page = new Page($dispatcher);
$page->render();

在写 render 方法不用考虑对外的依赖关系,只需要把相应的事件派发就好,这些事件派发的点,本质就是一个功能延申扩展的切入点。

这种用法在 laravel 很多地方都有使用,例如每个 http 请求被处理完成之后都有派发一个 new RequestHandled($request, $response)

这个事件表示请求已经被处理,如果你想在请求被处理完成之后,做一个额外的操作–比如记录日志,就可以监听该事件。

基本使用

通常 Dispatcher 会作为一个全局的单例对象被使用,在系统初始化的事件设置监听器,然后在具体业务逻辑中派发相应的事件。

定义事件

事件通常有两部分组成

  • 事件的唯一名称,为了简单,如果事件是一个类,可以使用类的全名称作为事件的唯一标记;
  • 事件携带的数据,这部分是可选;

RequestHandled 就是一个事件,它的事件名称为Illuminate\Foundation\Http\Events\RequestHandled

它包含了请求对象和相应对象两个数据属性。

class RequestHandled
{
/**
* The request instance.
*
* @var \Illuminate\Http\Request
*/
public $request;
/**
* The response instance.
*
* @var \Illuminate\Http\Response
*/
public $response;
/**
* Create a new event instance.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Response $response
* @return void
*/
public function __construct($request, $response)
{
$this->request = $request;
$this->response = $response;
}
}

事件监听器设置

使用 public function listen($events, $listener = null) 设置监听器,通常有以下4种用法

  • $events 为事件的唯一名称,$listener 为一个函数
  • $events 为一个函数,但函数的第一个参数为事件的唯一名称,$listener 为空
  • $events 为事件的唯一名称 $listener 为一个监听器类的名称,但是该类必须有 handle 方法或者 __invoke
  • $events为事件的唯一名称$listener` 为一个数组,数组的第一个元素为监听器类名称,第二个参数为处理事件的方法名称
// 方式一 函数监听器
$dispatcher->listen(RequestHandled::class,function (RequestHandled $event){
echo sprintf("请求%s已经被处理,相应的状态码为%d\n",$event->request->url(),$event->response->status());
});
// 方式二 省略事件名称
$dispatcher->listen(function (RequestHandled $event){
echo sprintf("请求%s已经被处理,相应的状态码为%d\n",$event->request->url(),$event->response->status());
});
//方式三:使用类监听器,默认是使用监听器的`handle`方法处理,如果找不到则使用`__invoke`方法,否则处抛出异常
class RequestHandledListener{
public function handle($event){
echo sprintf("请求%s已经被处理,相应的状态码为%d\n",$event->request->url(),$event->response->status());
}
}
$dispatcher->listen(RequestHandled::class,RequestHandledListener::class);
// 方式四:指定监听器处理的方法
class RequestHandledListenerMethod{
public function hand2($event){
echo sprintf("请求%s已经被处理,相应的状态码为%d\n",$event->request->url(),$event->response->status());
}
}
$dispatcher->listen(RequestHandled::class,[RequestHandledListenerMethod::class,'hand2']);

事件订阅器

事件订阅器的是一个带有 subscribe 方法的类,subscribe 方法内部实现一些列的监听器的设置。

下面例子的本质是把用户相关的事件监听器的设置封装到一个名为 UserEventSubscriber 中

class UserEventSubscriber
{
/**
* Handle user login events.
*/
public function handleUserLogin($event) {}
/**
* Handle user logout events.
*/
public function handleUserLogout($event) {}
/**
* Register the listeners for the subscriber.
*
* @param \Illuminate\Events\Dispatcher $events
* @return void
*/
public function subscribe($events)
{
$events->listen(
'Illuminate\Auth\Events\Login',
[UserEventSubscriber::class, 'handleUserLogin']
);
$events->listen(
'Illuminate\Auth\Events\Logout',
[UserEventSubscriber::class, 'handleUserLogout']
);
}
}
$dispatcher->subscribe(UserEventSubscriber::class); // 设置订阅器