zend裡的事件管理器主要是為了實現:
1、觀察者模式
2、面向切面設計
3、事件驅動架構
事件管理最基本的功能是將監聽器與事件連接或斷開。不論時連接還是斷開都是透過shared collections; 觸發事件和中斷監聽器的執行。
<span style="color: #0000ff;">use</span><span style="color: #000000;"> Zend\EventManager\EventManagerInterface; </span><span style="color: #0000ff;">use</span><span style="color: #000000;"> Zend\EventManager\EventManager; </span><span style="color: #0000ff;">use</span><span style="color: #000000;"> Zend\EventManager\EventManagerAwareInterface; </span><span style="color: #0000ff;">class</span> Foo <span style="color: #0000ff;">implements</span><span style="color: #000000;"> EventManagerAwareInterface { </span><span style="color: #0000ff;">protected</span> <span style="color: #800080;">$events</span><span style="color: #000000;">;<br>//将EventManager实例注入到Foo类中 </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> setEventManager(EventManagerInterface <span style="color: #800080;">$events</span><span style="color: #000000;">) { </span><span style="color: #800080;">$events</span>-><span style="color: #000000;">setIdentifiers([ </span><span style="color: #ff00ff;">__CLASS__</span>,<span style="color: #000000;"> get_called_class()</span>,<span style="color: #000000;"> ]); </span><span style="color: #800080;">$this</span>->events=<span style="color: #800080;">$events</span><span style="color: #000000;">; </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span><span style="color: #000000;">; }<br>//如果EventManager实例不存在,则新建一个实例。 </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span><span style="color: #000000;"> getEventManager() { </span><span style="color: #0000ff;">if</span> (<span style="color: #0000ff;">null</span> === <span style="color: #800080;">$this</span>-><span style="color: #000000;">events) { </span><span style="color: #800080;">$this</span>->setEventManager(<span style="color: #0000ff;">new</span><span style="color: #000000;"> EventManager()); } </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span>-><span style="color: #000000;">events; } }</span>
EventManager真正感興趣的時觸發事件,最基本的觸發方式時會透過trigger()方法。
<span style="color: #008000;">/*</span><span style="color: #008000;">file:vendor\zendframework\zend-eventmanager\EventManager.php *trigger方法的具体内容 </span><span style="color: #008000;">*/</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> trigger(<span style="color: #800080;">$eventName</span>, <span style="color: #800080;">$target</span> = <span style="color: #0000ff;">null</span>, <span style="color: #800080;">$argv</span> =<span style="color: #000000;"> []) { </span><span style="color: #800080;">$event</span> = <span style="color: #0000ff;">clone</span> <span style="color: #800080;">$this</span>-><span style="color: #000000;">eventPrototype; </span><span style="color: #800080;">$event</span>->setName(<span style="color: #800080;">$eventName</span><span style="color: #000000;">); </span><span style="color: #800080;">$event</span>->setTarget(<span style="color: #800080;">$target</span><span style="color: #000000;">); </span><span style="color: #800080;">$event</span>->setParams(<span style="color: #800080;">$argv</span><span style="color: #000000;">); </span><span style="color: #0000ff;">return</span> <span style="color: #800080;">$this</span>->triggerListeners(<span style="color: #800080;">$event</span><span style="color: #000000;">); }</span>
可以看到trigger()方法實際上將觸發事件這一行為委託給了triggerListeners()方法
trigger接受三個參數(事件名,目標,參數)
trigger方法將會建立事件的實例並觸發事件。 trigger呼叫了setName等幾個截斷器。這幾個方法可以在Event類別裡面找到,主要用來封裝目標環境和被傳遞進來的參數。
關於目標,官網上講的是通常為當前物件實例。實際上就是觸發事件的元素。可以理解為事件的標識
參數則是提供給事件的參數,通常是傳遞給目前函數或方法的參數。
舉例:
<span style="color: #0000ff;">class</span><span style="color: #000000;"> Foo {<br>//....assume events definition from above </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">function</span> bar(<span style="color: #800080;">$baz</span>, <span style="color: #800080;">$bat</span> = <span style="color: #0000ff;">null</span><span style="color: #000000;">) { </span><span style="color: #800080;">$params</span> = <span style="color: #008080;">compact</span>('baz', 'bat'<span style="color: #000000;">); </span><span style="color: #800080;">$this</span>->getEventManager()->trigger(<span style="color: #ff00ff;">__FUNCTION__</span>, <span style="color: #800080;">$this</span>, <span style="color: #800080;">$params</span><span style="color: #000000;">); } }</span>
之前講過,EventManager只對觸發事件感興趣。這裡觸發事件則只關心誰在監聽本事件,也就是監聽器是誰?
監聽器會連接到EventManager,指定一個命名的事件和一個回呼函數用來通知相關訊息(啥叫相關訊息呢?就是你想通知的訊息)。回呼函數需要接收Event對象,Event對象的獲取器可以取得事件的名字,目標,和參數(之前程式碼實例中有setName,反之則是getName)
程式碼實例
<span style="color: #0000ff;">use</span> Zend\<span style="color: #008080;">Log</span>\Factory <span style="color: #0000ff;">as</span><span style="color: #000000;"> LogFactory; </span><span style="color: #800080;">$log</span> = LogFactory(<span style="color: #800080;">$someConfig</span><span style="color: #000000;">); </span><span style="color: #800080;">$foo</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Foo(); </span><span style="color: #800080;">$foo</span>->getEventManager()->attach('bar', <span style="color: #0000ff;">function</span>(<span style="color: #800080;">$e</span>) <span style="color: #0000ff;">use</span>(<span style="color: #800080;">$log</span><span style="color: #000000;">) { </span><span style="color: #800080;">$event</span> = <span style="color: #800080;">$e</span>-><span style="color: #000000;">getName(); </span><span style="color: #800080;">$target</span> = <span style="color: #008080;">get_class</span>(<span style="color: #800080;">$e</span>-><span style="color: #000000;">getTarget()); </span><span style="color: #800080;">$params</span> = json_encode(<span style="color: #800080;">$e</span>-><span style="color: #000000;">getParams()); </span><span style="color: #800080;">$log</span>->info(<span style="color: #008080;">sprintf</span><span style="color: #000000;">( </span>'<span style="color: #000000;">%s called on %s, using params %s, $event, $target, $params )); }); //以下bar方法调用时,事件会被触发,监听器便会被执行 $foo->bar(</span>'baz', 'bat'<span style="color: #000000;">) //Result: //bar called on Foo, using params {"baz": "baz", "bat":"bat"}"</span>
attach()的第二個參數可以是任何有效的可呼叫的PHP函數,可以是匿名函數,也可以使用函數名稱、仿函數、指向靜態函數的字串、、、、
有時候呢,你可能想創建一個新的監聽器,但又不想創建新的事件。於是你想要使用之前建立的事件,這時候你就要將之前的事件設定為shared(就是可分享的,一個事件可以有多個監聽器)。我們可以透過SharedEventManager來達成這項目的。
ZendEventManagerSharedEventManagerInterface描述了一個聚合數個監聽器的物件。這些監聽器透過使用標識連接一個或多個事件。 SharedEventManager不會觸發這些事件。相反,是由EventManager組合SharedEventManager之後查詢ShareEventManager取得標識一致的監聽器,然後觸發。
<span style="color: #0000ff;">use</span><span style="color: #000000;"> Zend\EventManager\SharedEventManager; </span><span style="color: #800080;">$sharedEvents</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> SharedEventManager(); </span><span style="color: #800080;">$sharedEvents</span>->attach('Foo', 'bar', <span style="color: #0000ff;">function</span>(<span style="color: #800080;">$e</span><span style="color: #000000;">){ </span><span style="color: #800080;">$event</span> = <span style="color: #800080;">$e</span>-><span style="color: #000000;">getName(); </span><span style="color: #800080;">$target</span> =<span style="color: #008080;">get_class</span>(<span style="color: #800080;">$e</span>-><span style="color: #000000;">getTarget()); </span><span style="color: #800080;">$params</span> = <span style="color: #800080;">$e</span>-><span style="color: #000000;">getParams(); </span><span style="color: #008080;">printf</span><span style="color: #000000;">( </span>'Handled event "%s" on target "%s", with parameters %s', <span style="color: #800080;">$event</span>, <span style="color: #800080;">$target</span>,<span style="color: #000000;"> json_encode(</span><span style="color: #800080;">$params</span><span style="color: #000000;">) ); });</span>
上面程式碼的attach函數與之前相比增加了第一個參數:‘Foo',意思本監聽器的目標是:Foo,事件是:bar,其他的目標不要來找我啦。
而我們當初創建事件的時候,使用了setIdentifiers()。這個函數便設定了target,也就是標識。
之前我們使用了ShareEventManager註冊了一個監聽器,該監聽器是共用的。注意:事件不共享,監聽器共享。現在我們要做的就是告訴事件Foo:你可以使用某個監聽喇叭啦。我們使用以下程式碼:
<span style="color: #800080;">$foo</span> = <span style="color: #0000ff;">new</span><span style="color: #000000;"> Foo(); </span><span style="color: #800080;">$foo</span>->getEventManager()->setSharedManager(<span style="color: #800080;">$sharedEvents</span><span style="color: #000000;">); </span><span style="color: #800080;">$foo</span>->bar('bar', 'bat');
程式碼的最後我們觸發了bar事件,隨之會觸發上面的事件,結果就不累述了。
如果我們使用SubFoo繼承Foo類,SubFoo裡的bar()仍然會觸發我們的共享事件。原因是們在Foo的setIndentifiers()裡同時傳入了get_class($this),__CLASS__。如果我們在SubFoo裡面呼叫該方法,這兩個參數分別回傳SubFoo和Foo(__CLASS__相當於get_class()不帶參數)。