Symfony2 learning notes event dispenser

Source: Internet
Author: User
Tags register php


---- Eventdispatcher component usage

Introduction:

Object-Oriented Programming has gone a long way to ensure code scalability. It creates classes with specific responsibilities to make them more flexible. developers can create subclasses to change their behaviors by inheriting these classes. However, if you want to share a developer's changes with other developers who have compiled their own child classes, this Object-oriented Inheritance will not be so useful.

For example, you want to provide a plug-in system for your project. A plug-in that can be added to a method, or complete some work before and after method execution, without interfering with other plug-ins. It is not easy to accomplish this through a single inheritance, and multiple inheritance has its limitations.

The event dispathcer component of sf2 implements the mediator mode in a simple and effective way, making the implementation of these requirements possible and bringing real scalability to your project.

As an example of the httpkernel component, once a response object is created, it is very useful to modify other elements in the system before the response object is actually used. (For example, adding a cache header), The sf2 kernel achieves this through an event kernel. response.

How does it work?
A listener tells the dispatcher object that it wants to listen to the kernel. RESPONSE event:
At a certain point, the sf2 core tells the dispatcher object to allocate the kernel. RESPONSE event and deliver an event object to the allocated target object.
This event object can be used to access the response object.
The dispatcher notifies all listeners listening for the kernel. RESPONSE event and allows them to modify the response object.


If an event is to be assigned, it must have a unique name that can identify itself (for example, kernel. Response), so that any number of listeners can register the name of the listener. During the allocation process, an event instance is created and passed to all listeners. This event object usually contains some data about the allocated event.

The event name can be any string, but generally follows the following rules:
Only lowercase characters, numbers, periods, and underscores are used.
Use the namespace name and point number as the prefix.
It usually ends with a specified verb (for example, request ).

The following definitions are valid event names:
Kernel. Response
Form. pre_set_data


Event name and specific event object:
When the dispatcher notifies a listener, it will pass a real event object to these listeners. The event base class is very simple. It contains nothing except a method used to stop event passing.

Generally, the data of a specific event needs to be transmitted to the listener together with the event, so that the listener listening to the event has enough information to respond to the event. For example. in the response event, an event object is created and passed to each listener. The actual type of the event instance is filterresponseevent, which is a subclass of the event base class. This class contains methods like getresponse () and setresponse (), allowing the listener to get or even replace the response object.

The implication of this story is that when an event listener is created, the event object passed to the listener may be its specific subclass, this class has an additional method to obtain information from the event and reply to the event.


Event distributor DISPATCHER:
It is the central object of the entire event allocation system.
Generally, only a unique distributor is created, and its maintainer registers with all its listeners.
When an event is assigned by dispatcher, it notifies all registrants who register to listen to the event.

1 use Symfony\Component\EventDispatcher\EventDispatcher;2 3 $dispatcher = new EventDispatcher();

 

Register the listener to the event distributor:
To use existing events, you need to associate the event listener with the distributor so that it can notify them when allocating events.
By calling the addlistener () method on the dispatcher, any PHP Legal call can be associated with an event.

1 $listener = new AcmeListener();2 $dispatcher->addListener(‘foo.action‘, array($listener,‘onFooAction‘));

Here, the addlistener method receives three parameters:

The name string of the event to be listened on as the first parameter:
PHP call for a listener event
An optional parameter indicates the execution priority of the listener (the greater the value, the more important it is). It determines the sequence in which the listener is triggered. The default value is 0. If the two listeners have the same priority value, they are registered in the same order.


Note:: PhP callable is a PHP variable that can be used in the call_user_func () method and returns a true value when it is passed into the is_callable () method. It can be an instance of \ closure, an object implementing the _ invoke method, a string representing a function method, or an array representing an object method or a class method.

So far you know that PHP objects can be registered as listeners. You can also register PHP closure as the event listener:

1 use symfony \ component \ eventdispatcher \ event; 2 3 $ Dispatcher-> addlistener ('foo. action ', function (event $ event) {4 // This method will be in Foo. execute 5 when action event is assigned });

Once a listener is registered with dispatcher, it will wait until the event is notified.

In the preceding example, when Foo. Action is assigned, the distributor calls the acmelistener: onfooaction method and passes in the event object as a unique parameter.

1 use symfony \ component \ eventdispatcher \ event; 2 3 class acmelistener 4 {5 //... 6 7 public function onfooaction (event $ event) 8 {9 //... related Operations 10} 11}

In many cases, some subclass of the event object is passed to the listener of the specified event. These sub-classes enable the listener to access specific information about the event through some additional methods. We usually need to check the documentation provided by sf2 or the implementation of the event to determine the class to be passed in when the event is triggered.

For example, a symfony \ component \ httpkernel \ event \ filterresponseevent is input in the kernel. Event event:

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 }

 


Let's take a look at the process of creating and allocating an event:
In addition to registering the listener to an existing event, we can also create and listen to our own events. This is useful for creating third-party class libraries or maintaining the flexibility and decoupling layers of our system components.

1. Create a static event class first:
Suppose you want to create a new event store. Order, which will be allocated each time the order is created.
To make it look more standard, we create a storeevents class to define and describe our events:

1 namespace Acme \ storebundle; 2 3 final class storeevents 4 {5/** 6 * store. the order event will throw a 7*8 * listener for this event every time the order is created. the listener will receive a 9 * Acme \ storebundle \ event \ filterorderevent instance 10*11 * @ var string12 */13 const store_order = 'store _ order '; 14}

Note that this class does not do any practical work, and its purpose is only to locate the location where the public event information is centralized. At the same time, we also noticed that a filterorderevent object is passed to the listener together in the comment.

2. Create an event object
Next, When you dispatch a new event, you need to create an event instance and pass it to the dispatcher. Dispatcher transmits the instance to each listener listening to the event. If you do not need to pass any information to these listeners, you can directly use the default symfony \ component \ eventdispatcher \ event class.
Most of the time, you need to pass some information about the event to the listener. To accomplish this, you need to create a new class that extends to the symfony \ component \ eventdispatcher \ event class.

In this example, each listener needs to use a simulated order object. Create a new event subclass to meet the following requirements:

 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 }

In this way, each listener can access the order object through the getorder method of this class.

3. Allocate events:
The dispatch () method notifies the listener of all specified events. It has two parameters: the name of the event to be allocated and the event instance to be passed to each listener.

1 use Acme \ storebundle \ storeevents; 2 Use Acme \ storebundle \ order; 3 Use Acme \ storebundle \ event \ filterorderevent; 4 5 // instantiate a required order object 6 $ order = New Order (); 7 //... 8 9 // create a filterorderevent and assign it 10 $ event = new filterorderevent ($ order); 11 $ Dispatcher-> dispatch (storeevents: store_order, $ event );

Note: a specific filterorderevent object is created and passed to all the listeners of the event. The listeners receive the object and access the order object through its getorder method.

1 // assume that some listeners are registered to the "store_order" Event 2 Use Acme \ storebundle \ event \ filterorderevent; 3 4 Public Function onstoreorder (filterorderevent $ event) 5 {6 $ order = $ event-> getorder (); 7 // process the Order 8}

 

4. Use event subscriber
The most common way is that an event listener registers an event through dispatcher. This listener can listen to one or more events and receive notifications when the event is assigned.

Another way to listen for events is through an event subscriber.
An event subscriber is a PHP class that can tell dispatcher which events should be subscribed.
Event subscriber implements the eventsubscriberinterface interface. The only static method to be implemented is getsubscribedevents.
The following example shows how an event subscriber subscribes to the kernel. Response and store. Order events:

 1 namespace Acme\StoreBundle\Event; 2  3 use Symfony\Component\EventDispatcher\EventSubscriberInterface; 4 use Symfony\Component\HttpKernel\Event\FilterResponseEvent; 5  6 class StoreSubscriber implements EventSubscriberInterface 7 { 8     public static function getSubscribedEvents() 9     {10         return array(11             ‘kernel.response‘ => array(12                 array(‘onKernelResponsePre‘, 10),13                 array(‘onKernelResponseMid‘, 5),14                 array(‘onKernelResponsePost‘, 0),15             ),16             ‘store.order‘ => array(‘onStoreOrder‘, 0),17             );18     }19 20     public function onKernelResponsePre(FilterResponseEvent $event)21     {22         // ...23     }24 25     public function onKernelResponseMid(FilterResponseEvent $event)26     {27         // ...28     }29 30     public function onKernelResponsePost(FilterResponseEvent $event)31     {32         // ...33     }34 35     public function onStoreOrder(FilterOrderEvent $event)36     {37         // ...38     }39 }                        

It is very similar to the listener class, except for the events that the dispatcher can listen.

To register a subscriber to the dispatcher, you must use the addsubscriber () method of the dispatcher.

1 use Acme\StoreBundle\Event\StoreSubscriber;2 3 $subscriber = new StoreSubscriber();4 $dispatcher->addSubscriber($subscriber);

Here, dispatcher automatically returns the events returned by the getsubscribedevents method of each subscriber. This method returns an array indexed by the event name. Its value can be either the called method name or an array combining the method name and call priority.

The above example shows how to register multiple listening methods in the subscriber class to the same event, and how to set the priority for each listening method. A method with a higher priority level is called earlier.
According to the definition in the preceding example, when the kernel. RESPONSE event is assigned, the call sequence of the listening method is as follows:
Onkernelresponsepre, onkernelresponsemid, and onkernelresponsepost.

5. Stop event stream/Transfer
In some cases, there may be a listener to prevent other listeners from being called. In other words, the listener must be able to tell dispatcher to prevent the event from being passed to the later listener. This can be achieved through the stoppropagation () method within a listener.

1 use Acme\StoreBundle\Event\FilterOrderEvent;2 3 public function onStoreOrder(FilterOrderEvent $event)4 {5     // ...6 7     $event->stopPropagation();8 }

Now, no listener for the store. Order event that has not been called will be called again.

We can use the ispropagationstopped () method to determine whether an event is blocked.

1 $dispatcher->dispatch(‘foo.event‘,$event);2 if($event->isPropagationStopped()){3     //..4 }

 


6. The event distributor knows the event and the listener.
Eventdispatcher always injects a reference to the passed event object. This means that all listeners can directly access the eventdispatcher object through the getdispatcher () method passed to their event object.

These can cause some advanced applications of eventdispatcher, including loading the events delayed by the listener to the dispatcher object.
The following describes how to delay the listener loading:

1 use symfony \ component \ eventdispatcher \ event; 2 Use Acme \ storebundle \ event \ storesubscriber; 3 4 class Foo 5 {6 private $ started = false; 7 8 Public Function mylazylistener (event $ event) 9 {10 if (false ===$ this-> started) {11 $ subscriber = new storesubscriber (); 12 $ event-> getdispatcher ()-> addsubscriber ($ subscriber); 13} 14 $ this-> started = true; 15 16 //... more code 17} 18}

 

Dispatch another event from a listener:

1 use symfony \ component \ eventdispatcher \ event; 2 3 class Foo 4 {5 Public Function myfoolistener (event $ event) 6 {7 $ event-> getdispatcher () -> dispatch ('log', $ event); 8 9 //... more code 10} 11}

 

If your application uses multiple eventdispatcher instances, you may need to inject a known eventdispatcher instance to your listener. This can be injected through the constructor or setter method:

 1 use Symfony\Component\EventDispatcher\EventDispatcherInterface; 2  3 class Foo 4 { 5     protected $dispatcher = null; 6  7     public function __construct(EventDispatcherInterface $dispatcher) 8     { 9         $this->dispatcher = $dispatcher;10     }11 }

Setter method injection:

 1 use Symfony\Component\EventDispatcher\EventDispatcherInterface; 2  3 class Foo 4 { 5     protected $dispatcher = null; 6  7     public function setEventDispatcher(EventDispatcherInterface     $dispatcher) 8     { 9         $this->dispatcher = $dispatcher;10     }11 }

Which of the above two injection methods depends on your preferences. Some may prefer constructor injection because it can be fully initialized during construction. However, when you have a long dependency list, using setter injection is an optional method, especially when dependency items are optional.


7. abbreviated use of the Allocator:
Eventdispatcher: The dispatch method always returns an event object. This gives us a lot of short-lived opportunities. For example, an event that does not require a Custom Event object can be dispatched by relying on the native event object. You do not need to input any event object to the dispatch method, it creates a default event object to use.

$dispatcher->dispatch(‘foo.event‘);

Further, eventdispatcher always returns the dispatched event object, whether it is passed in or created internally.

In this way, we can make some beautiful Abbreviations:

if(!$dispatcher->dispatch(‘foo.event‘)->isPropagationStopped()){    //....}

Or:

$barEvent = new BarEvent();$bar = $dispatcher->dispatch(‘foo.event‘,$barEvent)->getBar();

Or:

$response = $dispatcher->dispatch(‘bar.event‘, new BarEvent())->getBar();

 


8. Internal self-knowledge of the event name
Because eventdispatcher knows the event name early in the event allocation process, and the event name is injected into the event object, it can be obtained by the getname () method for event listeners.

In this way, the event name can be used (like other data contained in the Custom Event) as part of the listener's event processing process.

use Symfony\Component\EventDispatcher\Event;class Foo{    public function myEventListener(Event $event)    {        echo $event->getName();    }}

 


9. Other types of event Distributors:
The service container-aware event distributor containerawareeventdispatcher is a special event distributor implementation. It is coupled with a service container and implemented as part of the dependency injection component. It allows the service as the listener of the specified event, so that the event distributor has a strong performance.

Delayed loading of services in the container, which means that the services used as listeners are created only after an event is sent.

The installation and configuration are relatively simple. You only need to inject a containerinterface into containerawareeventdispatcher:

1 use Symfony\Component\DependencyInjection\ContainerBuilder;2 use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;3 4 $container = new ContainerBuilder();5 $dispatcher = new ContainerAwareEventDispatcher($container);

 

Add listener:
The container knows that the event distributor can either load a specific service directly or implement the eventsubscriberinterface interface.

The following example assumes that the service has loaded some services:
Note that the service must be marked as public in the container.

Add service:
Use addlistenerservice () to connect to an existing service definition. The $ callback variable here is an array:

array($serviceId, $methodName)$dispatcher->addListenerService($eventName,array(‘foo‘,‘LogListener‘));

 

Add a subscriber service:
You can use the addsubscriberservice () method to add the eventsubscribers object. The first parameter here is the subscriber service ID, and the second parameter is the service class name (this class must implement the eventsubscriberinterface Interface ):

$dispatcher->addSubscriberService(    ‘kernel.store_subscriber‘,    ‘StoreSubscriber‘);

Eventsubscriberinterface implementation:

 1 use Symfony\Component\EventDispatcher\EventSubscriberInterface; 2 // ... 3  4 class StoreSubscriber implements EventSubscriberInterface 5 { 6     public static function getSubscribedEvents() 7     { 8         return array( 9             ‘kernel.response‘ => array(10                 array(‘onKernelResponsePre‘, 10),11                 array(‘onKernelResponsePost‘, 0),12             ),13             ‘store.order‘ => array(‘onStoreOrder‘, 0),14         );15     }16 17     public function onKernelResponsePre(FilterResponseEvent $event)18     {19         // ...20     }21 22     public function onKernelResponsePost(FilterResponseEvent $event)23     {24         // ...25     }26 27     public function onStoreOrder(FilterOrderEvent $event)28     {29         // ...30     }31 }

 

10.There is also an event distributor called immutable event DISPATCHER ):
It is a fixed event distributor. It cannot register a new listener or subscriber. It uses listeners or subscribers registered with other event distributors. From this perspective, it is just an original event.
Distributor proxy.
To use it, you must first create a standard event distributor (eventdispatcher or containerawareeventdispatcher) and register some listeners or event subscribers for it.

use Symfony\Component\EventDispatcher\EventDispatcher;$dispatcher = new EventDispatcher();$dispatcher->addListener(‘foo.action‘, function ($event) {    // ...});// ...

Then inject the standard event distributor into an immutableeventdispatcher:

use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;// ...$immutableDispatcher = new ImmutableEventDispatcher($dispatcher);

From now on, you need to use this new event distributor.

The advantage of using this proxy event distributor is that if you execute a method to modify the dispatcher (for example, using its addlistener method), a badmethodcallexception exception will be thrown.

 

11. Finally, let's take a look at the general event object)
When we call the dispatch method of dispatcher, if we do not input a Custom Event object to it, dispatcher will automatically create a default event object. These event base classes are provided by the event dispatcher component and are API-specific objects specially designed according to the object-oriented method. It provides more elegant and readable code for complex applications.

Genericevent is a convenient scenario for those who want to use only one event object in the entire application. It is suitable for most out-of-the-box targets because it follows the observer mode. In this mode, the event object encapsulates an event topic "subject" and some additional optional extension parameters.

In addition to its basic class events, genericevent also has a simple API:
The _ construct () constructor can receive event topics and any parameters.
Getsubject ()
Setargument () uses a key to set a parameter
Setarguments () sets a parameter Array
Getargument () obtains a parameter value through the key
Getarguments () Get all parameter values
Hasargument () returns true if a key value exists.

Genericevent also implements arrayaccess In the parameter set, so it is very convenient to pass in additional parameters.
The following example assumes that the event listener has been added to the dispatcher.

 1 use Symfony\Component\EventDispatcher\GenericEvent; 2  3 $event = new GenericEvent($subject); 4 $dispatcher->dispatch(‘foo‘, $event); 5  6 class FooListener 7 { 8     public function handler(GenericEvent $event) 9     {10         if ($event->getSubject() instanceof Foo) {11             // ...12         }13     }14 }

Use the arrayaccess API to pass in and process event parameters:

 1 use Symfony\Component\EventDispatcher\GenericEvent; 2  3 $event = new GenericEvent( 4     $subject, 5     array(‘type‘ => ‘foo‘, ‘counter‘ => 0) 6 ); 7 $dispatcher->dispatch(‘foo‘, $event); 8  9 echo $event[‘counter‘];10 11 class FooListener12 {13     public function handler(GenericEvent $event)14     {15         if (isset($event[‘type‘]) && $event[‘type‘] === ‘foo‘) {16             // ... do something17         }18 19         $event[‘counter‘]++;20     }21 }

Filter data:

 1 use Symfony\Component\EventDispatcher\GenericEvent; 2  3 $event = new GenericEvent($subject, array(‘data‘ => ‘foo‘)); 4 $dispatcher->dispatch(‘foo‘, $event); 5  6 echo $event[‘data‘]; 7  8 class FooListener 9 {10     public function filter(GenericEvent $event)11     {12         strtolower($event[‘data‘]);13      }14 }

We can directly use this genericevent object in many places.

 

Link: http://symfony.com/doc/current/components/event_dispatcher/introduction.html

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.