若是你正在使用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中完成事件的註冊,便於更好地解耦和複用。優化