照猫画虎 实现 min-laravel 框架系列之事件
- laravel
- 2020-06-08
- 254
- 0
事件
事件本质上是一对多的关系,即当某个事件发生,会触发一系列的系统更新操作。php 提供了 SplSubject, SplObserver, SplObjectStorage 标准库接口用来实现事件功能。
Laravel 的事件提供了一个简单的观察者实现,允许你在应用中订阅和监听各种发生的事件。
laravel 事件是作为 laravel 框架的核心服务提供者,在 application 初始化的时候注册到服务容器的
在这里关键理解两个步骤:
- 事件注册,本质上就是建立 event( 事件 string ) ——> listener ( 具体处理事件的代码函数 ) 之间的关系
- 事件调度。dispatch,即该事件发生,找到所有注册过的 listener 并依次调用
EventServiceProvider 服务提供者
在这里进行了一步简化,暂时不讨论 laravel 中队列的概念。因此,将 EventServiceProvider 的 register( 如果不知道 register 函数是干哈的,请看上一篇文章 ) 函数进行简化如下
public function register(){$this->app->singleton('events', function ($app) {return (new Dispatcher($app));});}
Dispatcher 核心类
这个类就是负责事件的绑定和调度,核心方法为 listen 、dispatch 两个
listen 方法
public function listen($events, $listener){// 依据带不带通配符 * ,分情况处理foreach ((array) $events as $event) {if (Str::contains($event, '*')) {$this->setupWildcardListen($event, $listener);} else {// 常规变量的,保存在 listeners 变量中$this->listeners[$event][] = $this->makeListener($listener);}}}
setupWildcardListen * 型事件名称
protected function setupWildcardListen($event, $listener){$this->wildcards[$event][] = $this->makeListener($listener, true);$this->wildcardsCache = [];}
makeListener 解析处理事件的函数
public function makeListener($listener, $wildcard = false){// 如果 listener 是字符串,表示改事件处理函数是个类if (is_string($listener)) {return $this->createClassListener($listener, $wildcard);}// listener 本身就是一个可执行闭包return function ($event, $payload) use ($listener, $wildcard) {if ($wildcard) {return $listener($event, $payload);}return $listener(...array_values($payload));};}
createClassListener 创建执行函数
public function createClassListener($listener, $wildcard = false){return function ($event, $payload) use ($listener, $wildcard) {// 如果是带通配符的,这里把实际出发的事件名传递给函数了if ($wildcard) {return call_user_func($this->createClassCallable($listener), $event, $payload);}return call_user_func_array($this->createClassCallable($listener), $payload);};}
createClassCallable 通过 IOC 解析类方法
protected function createClassCallable($listener){[$class, $method] = $this->parseClassCallable($listener);// 这块是队列问题,暂时不考虑if ( 0 && $this->handlerShouldBeQueued($class)) {return $this->createQueuedHandlerCallable($class, $method);}return [$this->container->make($class), $method];}
<br><br><br><br>
dispatch 事件调度类
public function dispatch($event, $payload = [], $halt = false){//如果 event 是个对象,那就就以对象名作为事件名,对象本身作为 listener 的参数[$event, $payload] = $this->parseEventAndPayload($event, $payload);// 队列,放弃if ( 0 && $this->shouldBroadcast($payload)) {$this->broadcastEvent($payload[0]);}$responses = [];foreach ($this->getListeners($event) as $listener) {$response = $listener($event, $payload);// If a response is returned from the listener and event halting is enabled// we will just return this response, and not call the rest of the event// listeners. Otherwise we will add the response on the response list.if ($halt && ! is_null($response)) {return $response;}// If a boolean false is returned from a listener, we will stop propagating// the event to any further listeners down in the chain, else we keep on// looping through the listeners and firing every one in our sequence.if ($response === false) {break;}$responses[] = $response;}return $halt ? null : $responses;}
getListeners 获取事件的所有监听器
public function getListeners($eventName){$listeners = $this->listeners[$eventName] ?? [];$listeners = array_merge($listeners,$this->wildcardsCache[$eventName] ?? $this->getWildcardListeners($eventName));return class_exists($eventName, false)? $this->addInterfaceListeners($eventName, $listeners): $listeners;}
getWildcardListeners 获取匹配符事件的监听器
protected function getWildcardListeners($eventName){$wildcards = [];foreach ($this->wildcards as $key => $listeners) {if (Str::is($key, $eventName)) {$wildcards = array_merge($wildcards, $listeners);}}return $this->wildcardsCache[$eventName] = $wildcards;}
addInterfaceListeners 找接口类是否有监听器,借此实现事件冒泡
protected function addInterfaceListeners($eventName, array $listeners = []){foreach (class_implements($eventName) as $interface) {if (isset($this->listeners[$interface])) {foreach ($this->listeners[$interface] as $names) {$listeners = array_merge($listeners, (array) $names);}}}return $listeners;}
事件测试
创建事件类
namespace App\Events;class OrderEvent{public $id;public function __construct( int $id ){$this->id = $id;}}
创建事件监听类
namespace App\Listeners;use App\Events\OrderEvent;class OrderListenerOne{public function handle( OrderEvent $one ){echo '事件listener :'. $one->id;}}
在 index.php 中进行实验
// 绑定事件名称 和 闭包函数$event->listen('order',function( $a , $b ){echo "<hr>";echo $a, "<br>";echo $b, "<br>";echo "<hr>";});// 字符串名称触发$event->dispatch("order",[1,11,22]);// 绑定事件名称 和 类字符串$event->listen(App\Events\OrderEvent::class,App\Listeners\OrderListenerOne::class);$one = new App\Events\OrderEvent(1);$event->dispatch($one,[1,11,22]);