詳解EventDispatcher事件分發(fā)組件
引言
考慮這樣一個(gè)問(wèn)題,現(xiàn)在你想給為你的項(xiàng)目提供一個(gè)插件系統(tǒng),插件可以添加一些方法,或者在某些方法執(zhí)行之前或者之后做些事情,而不干擾其他插件。要實(shí)現(xiàn)這個(gè)系統(tǒng),簡(jiǎn)單的單繼承不是個(gè)好辦法,即使多繼承在PHP中是可能的,他也有與生俱來(lái)的缺點(diǎn)(多繼承不太了解,感覺(jué)挺操蛋的)。
Symfony EventDispatcher以一個(gè)簡(jiǎn)單有效的方式實(shí)現(xiàn)了中介者模式,事件分發(fā)器就是那個(gè)中介,讓系統(tǒng)和插件不會(huì)耦合在一起,這讓上面的插件系統(tǒng)成為可能,而且他會(huì)讓你的項(xiàng)目可擴(kuò)展性更好。
上面的話,翻譯自Symfony官方文檔片段
系統(tǒng)剖析
事件存儲(chǔ)
上面這張圖是分析Symfony EventDispatcher組件源碼得出來(lái)的,可以看到事件在系統(tǒng)中是如何存儲(chǔ)的
這里面將事件存儲(chǔ)了兩遍,用來(lái)加入優(yōu)先級(jí)priority的概念,存如的時(shí)候放入上圖中上面的結(jié)構(gòu)中,取出時(shí)候從上圖中下面的結(jié)構(gòu)中拿出來(lái),相同的事件名稱可以有不同的優(yōu)先級(jí),優(yōu)先級(jí)越高的事件優(yōu)先觸發(fā),優(yōu)先級(jí)相同的時(shí)候,先插入的事件優(yōu)先觸發(fā)。
排序事件(上圖中下面的結(jié)構(gòu))在插入事件的時(shí)候不會(huì)構(gòu)建,而是當(dāng)取出事件的時(shí)候會(huì)生成排好序的事件,當(dāng)相同的事件名中插入新的事件或刪除某個(gè)事件的時(shí)候,會(huì)刪除對(duì)應(yīng)的排好序的事件名,后面用到的時(shí)候重新構(gòu)建
執(zhí)行事件的時(shí)候,會(huì)獲取對(duì)應(yīng)事件名排好序的linster列表,按照順序依次執(zhí)行。
事件執(zhí)行
如上圖所示,當(dāng)觸發(fā)某個(gè)時(shí)間的時(shí)候,該事件名下面如果監(jiān)聽(tīng)了多個(gè)觸發(fā)動(dòng)作,他們會(huì)按照優(yōu)先級(jí)、注冊(cè)順序依次觸發(fā),觸發(fā)動(dòng)作一般是一個(gè)可執(zhí)行的“實(shí)例”(不管是類還是函數(shù),必須可以通過(guò)call_user_func_array調(diào)用),可以傳入三個(gè)參數(shù),第一個(gè)參數(shù)(必須)是一個(gè)Event實(shí)例,第二個(gè)是觸發(fā)的事件名,第三個(gè)是事件分發(fā)器實(shí)例。第一個(gè)參數(shù)會(huì)控制事件是否在該事件名下的所有觸發(fā)動(dòng)作之間繼續(xù)傳遞,比如上面的linstener_2里面將Event.propagationStopped設(shè)置為true,執(zhí)行完linstener_2后,事件就會(huì)停止傳播,linstener_2后面的動(dòng)作不會(huì)觸發(fā)。
除此之外,Event實(shí)例中還可以保存其他必要的信息,以便linstener觸發(fā)執(zhí)行的時(shí)候,獲取額外的信息。
事件訂閱者
事件訂閱者(Event subscriber),告訴dispathcer實(shí)例,他要訂閱的所有事件,不用一個(gè)個(gè)通過(guò)dispathcer實(shí)例去注冊(cè)。事件訂閱者是一個(gè)PHP類,他可以告訴dispathcer他要訂閱的具體的事件。
好處:
- 關(guān)注的事件不用一個(gè)個(gè)去注冊(cè)。
- 取消關(guān)注的事件不用一個(gè)個(gè)去移除注冊(cè)。
訂閱者內(nèi)部關(guān)注的事件是一個(gè)整體,要么全部關(guān)注要么全部不關(guān)注
實(shí)例
普通栗子
include "vendor/autoload.php"; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\Event; class UserEvent extends Event { public function name() { return "Cartman"; } public function age() { return "24"; } } $dispatcher = new EventDispatcher(); $dispatcher->addListener("user.name", function($event, $eventName, $dispatcher){ echo "My name is Cartman\n"; }); $dispatcher->addListener("user.name", function($event, $eventName, $dispatcher){ echo "My name is {$event->name()} from Event instance\n"; }, 10); $dispatcher->addListener("user.age", function($event, $eventName, $dispatcher){ echo "My age is 24\n"; }, 10); $dispatcher->addListener("user.age", function($event, $eventName, $dispatcher){ echo "My age is {$event->age()} from Event instance\n"; }, -10); $dispatcher->dispatch("user.name", new UserEvent()); $dispatcher->dispatch("user.age", new UserEvent());
上面的例子輸出
My name is Cartman from Event instance My name is Cartman My age is 24 My age is 24 from Event instance
事件訂閱者栗子
通過(guò)Subscriber注冊(cè)事件
include "vendor/autoload.php"; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\Event; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class BookEvent extends Event { public $name = self::class; } class BookSubscriber implements EventSubscriberInterface { public static function getSubscribedEvents() { return [ "chinese.name" => "chineseNameShow", "english.name" => [ ["englishNameShow", -10], ["englishNameAFter", 10], ], "math.name" => ["mathNameShow", 100] ]; } public function chineseNameShow(Event $event) { echo "我是漢語(yǔ)書(shū)籍\n"; } public function englishNameShow(Event $event) { echo "我是英文書(shū)籍\n"; } public function englishNameAFter(Event $event) { echo "我是展示之后的英文書(shū)籍[來(lái)自于Event實(shí)例{$event->name}]\n"; } public function mathNameShow(Event $event) { echo "我是展示的數(shù)學(xué)書(shū)籍\n"; } } $dispatcher = new EventDispatcher(); $subscriber = new BookSubscriber(); $dispatcher->addSubscriber($subscriber); $dispatcher->dispatch("english.name", new BookEvent()); $dispatcher->dispatch("chinese.name"); $dispatcher->removeSubscriber($subscriber); $dispatcher->dispatch("math.name");
輸出為內(nèi)容:
我是展示之后的英文書(shū)籍[來(lái)自于Event實(shí)例BookEvent] 我是英文書(shū)籍 我是漢語(yǔ)書(shū)籍
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
thinkphp 一個(gè)頁(yè)面使用2次分頁(yè)的實(shí)現(xiàn)方法
thinkphp內(nèi)置ORG.Util.Page方法分頁(yè),使分頁(yè)變得非常簡(jiǎn)單快捷。 但是如果一個(gè)頁(yè)面里需要使用2次分頁(yè),就會(huì)產(chǎn)生沖突,這里先記錄下百度來(lái)的解決辦法。需要的朋友可以參考下2013-07-07針對(duì)PHP開(kāi)發(fā)安全問(wèn)題的相關(guān)總結(jié)
今天小編就為大家分享一篇關(guān)于針對(duì)PHP開(kāi)發(fā)安全問(wèn)題的相關(guān)總結(jié),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03php將圖片文件轉(zhuǎn)換成二進(jìn)制輸出的方法
這篇文章主要介紹了php將圖片文件轉(zhuǎn)換成二進(jìn)制輸出的方法,涉及php針對(duì)圖片文件的讀取與轉(zhuǎn)換輸出技巧,需要的朋友可以參考下2015-06-06方便實(shí)用的PHP生成靜態(tài)頁(yè)面類(非smarty)
不喜歡Smarty,直接寫(xiě)了一個(gè)生成靜態(tài)的類,方便簡(jiǎn)單實(shí)用,希望大家繼續(xù)完善。2008-11-11PHP設(shè)計(jì)模式之模板方法模式實(shí)例淺析
這篇文章主要介紹了PHP設(shè)計(jì)模式之模板方法模式,結(jié)合實(shí)例形式簡(jiǎn)單分析了php設(shè)計(jì)模式中模板方法模式的概念、原理、定義、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2018-12-12PHP $O00OO0=urldecode & eval 解密,記一次商業(yè)源碼的去后門(mén)
最近看到如下的php代碼$O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");等很長(zhǎng)的代碼,這里給出破解方法,需要的朋友可以參考下2020-09-09