Symfony2 EventDispatcher組件

        一個插件系統中,A插件在不影響其它插件的前提下,添加新的方法,或者在一個方法運行前作一些準備工做,經過繼承來實現擴展是很不容易的,因爲插件之間的關聯關係,A插件的改變也會使得關聯的插件被動的修改。
        Symfony2的EventDispatcher組件實現了中介者(mediator)模式,實現了插件之間的解耦和關聯的關係。
 
        舉個栗子,在HttpKernel組件中,一旦Response被建立了,在Response發送給客戶端的時候,容許系統的其它模塊修改它是頗有必要和頗有用的(例如:在頭部添加緩存字段)。Symfony2內核調度kernel.response事件,那麼監聽該事件的監聽器就能夠修改Response對象了:
        a) 監聽器(PHP對象)告訴一個特定的dispatcher類,它要監聽kernel.response事件;
 
        b) 在某一時刻,Symfony就會告訴dispatcher調度kernel.response事件,把一個包含Response對象的Event對象傳入事件調度方法EventDispatcher::dispatch()
 
        c) dispatcher通知全部監聽kernel.response事件的監聽器運行,容許這些監聽器修改Response對象。
 
使用方法
 
Events
 
        當一個事件被調度,該事件會有一個惟一的名稱(例如:kernel.response),能夠被任意多個監聽器監聽。同時實例化一個Event對象,並傳入到全部的監聽器中。在後面會看到,Event對象包含了被調度事件的數據。
 
命名規範
        事件名能夠是任意的字符串,可是能夠遵循如下幾點命名規範:
 
        * 只使用小寫字符、數字、點、下劃線;
 
        * 前綴名使用帶有點的命名;
 
        * 結束部分的名字使用一個能表達當前事件行爲的動詞;
 
事件名和事件對象
 
        dispatcher調度事件通知監聽器時候,會把一個Event對象做爲參數傳入到監聽器中。基類的Event很是簡單,只有一箇中止事件傳遞給下一個監聽器的方法,沒有太多別的了。
        一般,一個特定事件的數據都會保存到Event對象中,方便把數據傳遞給監聽器。在kernel.response事件中,傳入到監聽器的Event對象是子類FliterResponseEvent的對象,FliterResponseEvent是Event的子類。FliterResponseEvent包含getReponse和setResponse方法,容許監聽器得到或者修改Response對象。
        總的來講:建立一個監聽特定事件的監聽器的時候,Event的一個子類會傳入到監聽器中,監聽器能夠經過該子類的方法得到和修改信息。
 
事件調度器(dispatcher)
 
        dispatcher是事件調度系統的核心,通常來講,一個dispatcher內包含一個監聽器的列表,當一個事件須要被調度的時候,dispatcher就會通知它包含的全部監聽該事件的監聽器。
1 use Symfony\Component\EventDispatcher\EventDispatcher;
2 
3 $dispatcher = new EventDispatcher();
 
關聯監聽器
 
        把監聽器添加到dispatcher上,監聽特定的事件,那麼該事件被調度的時候,dispatcher就會通知監聽器工做了。dispatcher使用addListener方法把一個監聽器(PHP callable)添加到一個事件上。
1 $listener = new AcmeListener();
2 $dispatcher->addListener('foo.action', array($listener, 'onFooAction'));
 
addListener方法有三個參數:
        * 監聽器須要監聽是的事件的名稱;
        * 監聽器(一個PHP callable);
        * 一個可選的表示優先級的整數(數值越高優先級越高,監聽器就會更早的被觸發),默認爲0,若是優先級同樣,那麼誰先添加就先觸發;
 
PHP callable是指能做爲參數傳入call_user_func()或者傳入is_callable()函數執行後返回true的PHP 變量。PHP callable能夠是 \Closure實例,一個實現了__invoke方法的對象,或者是表示一個函數的字符串,或者一個表示對象方法或者類方法的數組。
 
到目前爲止,咱們看過把一個PHP對象做爲監聽器,咱們也能夠把Closure對象做爲監聽器。
1 use Symfony\Component\EventDispatcher\Event;
2 
3 $dispatcher->addListener('foo.action', function (Event $event) {
4     // will be executed when the foo.action event is dispatched
5 });
     在上面的例子中,foo.action事件被調度,dispatcher就調用AcmeListener::onFooAction方法,並把Event對象做爲惟一的參數傳入方法中。
 1 use Symfony\Component\EventDispatcher\Event;
 2 
 3 class AcmeListener
 4 {
 5     // ...
 6 
 7     public function onFooAction(Event $event)
 8     {
 9         // ... do something
10     }
11 }

     在實際使用中,都是傳入一個特定的Event子類的對象到監聽器,例如FilterResponseEvent:數組

 
1 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
2 
3 public function onKernelResponse(FilterResponseEvent $event)
4 {
5     $response = $event->getResponse();
6     $request = $event->getRequest();
7 
8     // ...
9 }
建立和調度事件
 
        除了系統內置的事件,咱們也能夠建立和調度自定義的事件。這是頗有好處的,當咱們使用第三方類庫的時,還有可使不一樣的組件之間解耦,使系統更靈活健壯。
 
靜態的Events類
 
        假如咱們要建立一個事件——store.order——當訂單被建立的時候就會被觸發。
namespace Acme\StoreBundle;

final class StoreEvents
{
    /**
     * The store.order event is thrown each time an order is created
     * in the system.
     *
     * The event listener receives an
     * Acme\StoreBundle\Event\FilterOrderEvent instance.
     *
     * @var string
     */
    const STORE_ORDER = 'store.order';
}
        這個類並無什麼方法,也不作什麼操做,只是定義了事件名稱,方便管理和組織事件。監聽這個事件的監聽器都會被傳入一個FilterOrderEvent對象。
 
建立一個Event對象
 
        接着,當你調度這個新的事件的時候,會建立一個Event對象傳如到dispatcher的dispatch()方法,dispatcher就把這個Event對象傳給全部的監聽該事件的監聽器。若是咱們不須要向監聽器傳入任何信息,那麼可使用系統默認的Symfony\Component\EventDispatcher\Event  類。然而,不少時候,咱們都須要傳入特定的信息到監聽器,那麼咱們能夠建立一個類繼承Symfony\Component\EventDispatcher\Event。
 
        例如,咱們須要在全部的監聽器中傳入order對象:
 1 namespace Acme\StoreBundle\Event;
 2 
 3 use Symfony\Component\EventDispatcher\Event;
 4 use Acme\StoreBundle\Order;
 5 
 6 class FilterOrderEvent extends Event
 7 {
 8     protected $order;
 9 
10     public function __construct(Order $order)
11     {
12         $this->order = $order;
13     }
14 
15     public function getOrder()
16     {
17         return $this->order;
18     }
19 }
        全部監聽器均可以經過FilterOrderEvent的getOrder方法得到order對象。
 
調度事件
 
        dispatcher的dispatch()方法通知監聽給定的事件的全部監聽器,有兩個參數,一個是須要調度的事件名,另外一個就是傳給全部監聽器的Event對象。
 1 use Acme\StoreBundle\StoreEvents;
 2 use Acme\StoreBundle\Order;
 3 use Acme\StoreBundle\Event\FilterOrderEvent;
 4 
 5 // the order is somehow created or retrieved
 6 $order = new Order();
 7 // ...
 8 
 9 // create the FilterOrderEvent and dispatch it
10 $event = new FilterOrderEvent($order);
11 $dispatcher->dispatch(StoreEvents::STORE_ORDER, $event);

    FilterOrderEvent對象做爲參數傳入到dispatch方法,如今,任何監聽store.order事件的監聽器都會接收到FilterOrderEvent對象,並經過調用getOrder方法得到order對象。緩存

1 // some listener class that's been registered for "store.order" event
2 use Acme\StoreBundle\Event\FilterOrderEvent;
3 
4 public function onStoreOrder(FilterOrderEvent $event)
5 {
6     $order = $event->getOrder();
7     // do something to or with the order
8 }
Event Subscribers  
 
        最廣泛的監聽事件的方法是註冊一個監聽器到dispatcher中,一個監聽器能夠監聽一個或者多個事件。
 
        還有另外一種監聽事件的方法是使用Event SubScriber,Event SubScriber是一個PHP類,可以準確的告訴dispatcher它訂閱了那些事件。實現EventSubscriberInterface接口,該接口有一個靜態的方法getSubscriberdEvents。
namespace Acme\StoreBundle\Event;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;

class StoreSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            'kernel.response' => array(
                array('onKernelResponsePre', 10),
                array('onKernelResponseMid', 5),
                array('onKernelResponsePost', 0),
            ),
            'store.order'     => array('onStoreOrder', 0),
        );
    }

    public function onKernelResponsePre(FilterResponseEvent $event)
    {
        // ...
    }

    public function onKernelResponseMid(FilterResponseEvent $event)
    {
        // ...
    }

    public function onKernelResponsePost(FilterResponseEvent $event)
    {
        // ...
    }

    public function onStoreOrder(FilterOrderEvent $event)
    {
        // ...
    }
}

這個監聽器類很簡單,告訴了dispatcher監聽了什麼事件,還有監聽的事件觸發的方法。addSubscriber()方法把subscriber註冊到dispatcher。函數

1 use Acme\StoreBundle\Event\StoreSubscriber;
2 
3 $subscriber = new StoreSubscriber();
4 $dispatcher->addSubscriber($subscriber);
        dispatcher準確的把Subscriber註冊到EventSubscriberInterface::getSubscriberdEvents()返回的事件裏,EventSubscriberInterface::getSubscriberdEvents()方法返回一個數組,數組的鍵對應Subscriber監聽的事件,值對應這Subscriber處理該事件調用的一個方法或者一組方法。上面的例子中,一組監聽器的方法對應這一個事件,同時咱們也能夠設置優先級來控制這組方法的執行前後順序。當kernel.response事件被觸 onKernelResponsePre onKernelResponseMid , 和  onKernelResponsePost三個方法就會前後執行。
 
 
中止事件的傳遞
 
        在一些狀況下,監聽器能夠中止事件傳遞下去,防止後續的監聽器被調用,換句話說,監聽器必須通知dispatcher中止傳遞事件給後續的監聽器。在監聽器裏面實現stopPropagation()方法:
1 use Acme\StoreBundle\Event\FilterOrderEvent;
2 
3 public function onStoreOrder(FilterOrderEvent $event)
4 {
5     // ...
6 
7     $event->stopPropagation();
8 }
        那麼,監聽了store.order事件的尚未執行的監聽器就不會在被執行。
        經過isPropagationStopped()方法能夠判斷一個事件是否被中止。
1 $dispatcher->dispatch('foo.event', $event);
2 if ($event->isPropagationStopped()) {
3     // ...
4 }
相關文章
相關標籤/搜索