若是你正在使用Laravel做为你的开发框架,那么确定会了解或者使用过它的事件API。Laravel的事件API是基于观察者这种设计模式实现的,而且结合了实际状况作了优化和改进。下面就依次介绍和Laravel事件实现相关的一些内容。php
首先举一个例子,假如你是一个司机,在马路上开车时违反了交通规则,被交警拦下后罚了款。这是在现实中生活中应用到事件机制的一个例子。司机就是一个被观察的对象,而交警就是观察者,当交警观察到司机违法时,就像被打开了一个开关同样,会不自觉地对司机进行处罚。设计模式
下面是一张观察者模式的类图,左边的Concrete Subject类就比如司机,而右边的Concrete Observer就比如交警。Concrete Subject的Notify方法是将司机的违法行为暴露给交警,而Concrete Observer的Update方法是交警的处罚行为。框架
Laravel中的事件API包含了监听(Listen)和触发(Fire)两个核心的方法,其中Listen方法就是将事件(Event)与监听器(Listener)进行绑定,监听和触发方法都封装在Dispatcher类中。Dispatcher类就是一个Concrete Subject,Event就比如司机的违法行为,Fire方法就是上面说到的Notify方法,Listener就比如是交警,它是一个Concrete Observer,Listener类中须要实现handle方法用来作相似交警罚款的动做。异步
public function makeListener($listener) { return is_string($listener) ? $this->createClassListener($listener) : $listener; }
protected function parseClassCallable($listener) { $segments = explode('@', $listener); return [$segments[0], count($segments) == 2 ? $segments[1] : 'handle']; }
protected function handlerShouldBeQueued($class) { try { return (new ReflectionClass($class))->implementsInterface( 'Illuminate\Contracts\Queue\ShouldQueue' ); } catch (Exception $e) { return false; } }
protected function createClassCallable($listener, $container) { list($class, $method) = $this->parseClassCallable($listener); if ($this->handlerShouldBeQueued($class)) { return $this->createQueuedHandlerCallable($class, $method); } else { return [$container->make($class), $method]; } }
public function listen($events, $listener, $priority = 0) { foreach ((array) $events as $event) { if (Str::contains($event, '*')) { $this->setupWildcardListen($event, $listener); } else { $this->listeners[$event][$priority][] = $this->makeListener($listener); unset($this->sorted[$event]); } } } protected function setupWildcardListen($event, $listener) { $this->wildcards[$event][] = $this->makeListener($listener); }
if (is_object($event)) { list($payload, $event) = [[$event], get_class($event)]; }
if (! is_array($payload)) { $payload = [$payload]; } $this->firing[] = $event;
if (isset($payload[0]) && $payload[0] instanceof ShouldBroadcast) { $this->broadcastEvent($payload[0]); }
public function getListeners($eventName) { $wildcards = $this->getWildcardListeners($eventName); if (! isset($this->sorted[$eventName])) { $this->sortListeners($eventName); } return array_merge($this->sorted[$eventName], $wildcards); }
protected function sortListeners($eventName) { $this->sorted[$eventName] = []; if (isset($this->listeners[$eventName])) { krsort($this->listeners[$eventName]); $this->sorted[$eventName] = call_user_func_array( 'array_merge', $this->listeners[$eventName] ); } }
foreach ($this->getListeners($event) as $listener) { $response = call_user_func_array($listener, $payload); if (! is_null($response) && $halt) { array_pop($this->firing); return $response; } if ($response === false) { break; } $responses[] = $response; } array_pop($this->firing); return $halt ? null : $responses;
Laravel的事件API支持一个事件绑定多个监听器,也支持包含通配符的事件,可是包含通配符的事件不支持按优先级触发监听器。Laravel的事件触发后,Listener能够经过队列异步执行,保证了程序能够不中断,且快速响应,就比如本文开头的举例中,若是被电子警察拍到违法行为,能够在指定时间内延迟缴纳罚款。Laravel的事件也支持经过队列广播,Dispatcher能够被Listener订阅,在Listener中完成事件的注册,便于更好地解耦和复用。优化