Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • SensioLabs Professional services to help you with Symfony
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by SensioLabs
  1. Home
  2. Documentation
  3. Components
  4. Event Dispatcher
  5. The EventDispatcher Component
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud

Table of Contents

  • Introduction
  • Installation
  • Usage
    • Events
    • The Dispatcher
    • Connecting Listeners
    • Creating and Dispatching an Event
    • Using Event Subscribers
    • Stopping Event Flow/Propagation
    • EventDispatcher aware Events and Listeners
    • Dispatcher Shortcuts
    • Event Name Introspection
  • Other Dispatchers

The EventDispatcher Component

Edit this page

Warning: You are browsing the documentation for Symfony 2.5, which is no longer maintained.

Read the updated version of this page for Symfony 6.2 (the current stable version).

The EventDispatcher Component

The EventDispatcher component provides tools that allow your application components to communicate with each other by dispatching events and listening to them.

Introduction

Object Oriented code has gone a long way to ensuring code extensibility. By creating classes that have well defined responsibilities, your code becomes more flexible and a developer can extend them with subclasses to modify their behaviors. But if they want to share the changes with other developers who have also made their own subclasses, code inheritance is no longer the answer.

Consider the real-world example where you want to provide a plugin system for your project. A plugin should be able to add methods, or do something before or after a method is executed, without interfering with other plugins. This is not an easy problem to solve with single inheritance, and multiple inheritance (were it possible with PHP) has its own drawbacks.

The Symfony EventDispatcher component implements the Mediator pattern in a simple and effective way to make all these things possible and to make your projects truly extensible.

Take a simple example from The HttpKernel Component. Once a Response object has been created, it may be useful to allow other elements in the system to modify it (e.g. add some cache headers) before it's actually used. To make this possible, the Symfony kernel throws an event - kernel.response. Here's how it works:

  • A listener (PHP object) tells a central dispatcher object that it wants to listen to the kernel.response event;
  • At some point, the Symfony kernel tells the dispatcher object to dispatch the kernel.response event, passing with it an Event object that has access to the Response object;
  • The dispatcher notifies (i.e. calls a method on) all listeners of the kernel.response event, allowing each of them to make modifications to the Response object.

Installation

You can install the component in 2 different ways:

  • Install it via Composer (symfony/event-dispatcher on Packagist);
  • Use the official Git repository (https://github.com/symfony/EventDispatcher).

Usage

Events

When an event is dispatched, it's identified by a unique name (e.g. kernel.response), which any number of listeners might be listening to. An Event instance is also created and passed to all of the listeners. As you'll see later, the Event object itself often contains data about the event being dispatched.

Naming Conventions

The unique event name can be any string, but optionally follows a few simple naming conventions:

  • use only lowercase letters, numbers, dots (.), and underscores (_);
  • prefix names with a namespace followed by a dot (e.g. kernel.);
  • end names with a verb that indicates what action is being taken (e.g. request).

Here are some examples of good event names:

  • kernel.response
  • form.pre_set_data

Event Names and Event Objects

When the dispatcher notifies listeners, it passes an actual Event object to those listeners. The base Event class is very simple: it contains a method for stopping event propagation, but not much else.

Often times, data about a specific event needs to be passed along with the Event object so that the listeners have needed information. In the case of the kernel.response event, the Event object that's created and passed to each listener is actually of type FilterResponseEvent, a subclass of the base Event object. This class contains methods such as getResponse and setResponse, allowing listeners to get or even replace the Response object.

The moral of the story is this: When creating a listener to an event, the Event object that's passed to the listener may be a special subclass that has additional methods for retrieving information from and responding to the event.

The Dispatcher

The dispatcher is the central object of the event dispatcher system. In general, a single dispatcher is created, which maintains a registry of listeners. When an event is dispatched via the dispatcher, it notifies all listeners registered with that event:

1
2
3
use Symfony\Component\EventDispatcher\EventDispatcher;

$dispatcher = new EventDispatcher();

Connecting Listeners

To take advantage of an existing event, you need to connect a listener to the dispatcher so that it can be notified when the event is dispatched. A call to the dispatcher's addListener() method associates any valid PHP callable to an event:

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

The addListener() method takes up to three arguments:

  • The event name (string) that this listener wants to listen to;
  • A PHP callable that will be notified when an event is thrown that it listens to;
  • An optional priority integer (higher equals more important, and therefore that the listener will be triggered earlier) that determines when a listener is triggered versus other listeners (defaults to 0). If two listeners have the same priority, they are executed in the order that they were added to the dispatcher.

Note

A PHP callable is a PHP variable that can be used by the call_user_func() function and returns true when passed to the is_callable() function. It can be a \Closure instance, an object implementing an __invoke method (which is what closures are in fact), a string representing a function, or an array representing an object method or a class method.

So far, you've seen how PHP objects can be registered as listeners. You can also register PHP Closures as event listeners:

1
2
3
4
5
use Symfony\Component\EventDispatcher\Event;

$dispatcher->addListener('foo.action', function (Event $event) {
    // will be executed when the foo.action event is dispatched
});

Once a listener is registered with the dispatcher, it waits until the event is notified. In the above example, when the foo.action event is dispatched, the dispatcher calls the AcmeListener::onFooAction method and passes the Event object as the single argument:

1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\EventDispatcher\Event;

class AcmeListener
{
    // ...

    public function onFooAction(Event $event)
    {
        // ... do something
    }
}

In many cases, a special Event subclass that's specific to the given event is passed to the listener. This gives the listener access to special information about the event. Check the documentation or implementation of each event to determine the exact Symfony\Component\EventDispatcher\Event instance that's being passed. For example, the kernel.response event passes an instance of Symfony\Component\HttpKernel\Event\FilterResponseEvent:

1
2
3
4
5
6
7
8
9
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;

public function onKernelResponse(FilterResponseEvent $event)
{
    $response = $event->getResponse();
    $request = $event->getRequest();

    // ...
}

Registering Event Listeners in the Service Container

When you are using the ContainerAwareEventDispatcher and the DependencyInjection component, you can use the RegisterListenersPass from the HttpKernel component to tag services as event listeners:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass;

$containerBuilder = new ContainerBuilder(new ParameterBag());
$containerBuilder->addCompilerPass(new RegisterListenersPass());

// register the event dispatcher service
$containerBuilder->setDefinition('event_dispatcher', new Definition(
    'Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher',
    array(new Reference('service_container'))
));

// register your event listener service
$listener = new Definition('AcmeListener');
$listener->addTag('kernel.event_listener', array(
    'event' => 'foo.action',
    'method' => 'onFooAction',
));
$containerBuilder->setDefinition('listener_service_id', $listener);

// register an event subscriber
$subscriber = new Definition('AcmeSubscriber');
$subscriber->addTag('kernel.event_subscriber');
$containerBuilder->setDefinition('subscriber_service_id', $subscriber);

By default, the listeners pass assumes that the event dispatcher's service id is event_dispatcher, that event listeners are tagged with the kernel.event_listener tag and that event subscribers are tagged with the kernel.event_subscriber tag. You can change these default values by passing custom values to the constructor of RegisterListenersPass.

Creating and Dispatching an Event

In addition to registering listeners with existing events, you can create and dispatch your own events. This is useful when creating third-party libraries and also when you want to keep different components of your own system flexible and decoupled.

The Static Events Class

Suppose you want to create a new Event - store.order - that is dispatched each time an order is created inside your application. To keep things organized, start by creating a StoreEvents class inside your application that serves to define and document your event:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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';
}

Notice that this class doesn't actually do anything. The purpose of the StoreEvents class is just to be a location where information about common events can be centralized. Notice also that a special FilterOrderEvent class will be passed to each listener of this event.

Creating an Event Object

Later, when you dispatch this new event, you'll create an Event instance and pass it to the dispatcher. The dispatcher then passes this same instance to each of the listeners of the event. If you don't need to pass any information to your listeners, you can use the default Symfony\Component\EventDispatcher\Event class. Most of the time, however, you will need to pass information about the event to each listener. To accomplish this, you'll create a new class that extends Symfony\Component\EventDispatcher\Event.

In this example, each listener will need access to some pretend Order object. Create an Event class that makes this possible:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
namespace Acme\StoreBundle\Event;

use Symfony\Component\EventDispatcher\Event;
use Acme\StoreBundle\Order;

class FilterOrderEvent extends Event
{
    protected $order;

    public function __construct(Order $order)
    {
        $this->order = $order;
    }

    public function getOrder()
    {
        return $this->order;
    }
}

Each listener now has access to the Order object via the getOrder method.

Dispatch the Event

The dispatch() method notifies all listeners of the given event. It takes two arguments: the name of the event to dispatch and the Event instance to pass to each listener of that event:

1
2
3
4
5
6
7
8
9
10
11
use Acme\StoreBundle\StoreEvents;
use Acme\StoreBundle\Order;
use Acme\StoreBundle\Event\FilterOrderEvent;

// the order is somehow created or retrieved
$order = new Order();
// ...

// create the FilterOrderEvent and dispatch it
$event = new FilterOrderEvent($order);
$dispatcher->dispatch(StoreEvents::STORE_ORDER, $event);

Notice that the special FilterOrderEvent object is created and passed to the dispatch method. Now, any listener to the store.order event will receive the FilterOrderEvent and have access to the Order object via the getOrder method:

1
2
3
4
5
6
7
8
// some listener class that's been registered for "store.order" event
use Acme\StoreBundle\Event\FilterOrderEvent;

public function onStoreOrder(FilterOrderEvent $event)
{
    $order = $event->getOrder();
    // do something to or with the order
}

Using Event Subscribers

The most common way to listen to an event is to register an event listener with the dispatcher. This listener can listen to one or more events and is notified each time those events are dispatched.

Another way to listen to events is via an event subscriber. An event subscriber is a PHP class that's able to tell the dispatcher exactly which events it should subscribe to. It implements the EventSubscriberInterface interface, which requires a single static method called getSubscribedEvents. Take the following example of a subscriber that subscribes to the kernel.response and store.order events:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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)
    {
        // ...
    }
}

This is very similar to a listener class, except that the class itself can tell the dispatcher which events it should listen to. To register a subscriber with the dispatcher, use the addSubscriber() method:

1
2
3
4
use Acme\StoreBundle\Event\StoreSubscriber;

$subscriber = new StoreSubscriber();
$dispatcher->addSubscriber($subscriber);

The dispatcher will automatically register the subscriber for each event returned by the getSubscribedEvents method. This method returns an array indexed by event names and whose values are either the method name to call or an array composed of the method name to call and a priority. The example above shows how to register several listener methods for the same event in subscriber and also shows how to pass the priority of each listener method. The higher the priority, the earlier the method is called. In the above example, when the kernel.response event is triggered, the methods onKernelResponsePre, onKernelResponseMid, and onKernelResponsePost are called in that order.

Stopping Event Flow/Propagation

In some cases, it may make sense for a listener to prevent any other listeners from being called. In other words, the listener needs to be able to tell the dispatcher to stop all propagation of the event to future listeners (i.e. to not notify any more listeners). This can be accomplished from inside a listener via the stopPropagation() method:

1
2
3
4
5
6
7
8
use Acme\StoreBundle\Event\FilterOrderEvent;

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

    $event->stopPropagation();
}

Now, any listeners to store.order that have not yet been called will not be called.

It is possible to detect if an event was stopped by using the isPropagationStopped() method which returns a boolean value:

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

EventDispatcher aware Events and Listeners

2.4

Since Symfony 2.4, the current event name and the EventDispatcher itself are passed to the listeners as additional arguments.

The EventDispatcher always passes the dispatched event, the event's name and a reference to itself to the listeners. This can be used in some advanced usages of the EventDispatcher like dispatching other events in listeners, event chaining or even lazy loading of more listeners into the dispatcher object as shown in the following examples.

Lazy loading listeners:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Acme\StoreBundle\Event\StoreSubscriber;

class Foo
{
    private $started = false;

    public function myLazyListener(
        Event $event,
        $eventName,
        EventDispatcherInterface $dispatcher
    ) {
        if (false === $this->started) {
            $subscriber = new StoreSubscriber();
            $dispatcher->addSubscriber($subscriber);
        }

        $this->started = true;

        // ... more code
    }
}

Dispatching another event from within a listener:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class Foo
{
    public function myFooListener(
        Event $event,
        $eventName,
        EventDispatcherInterface $dispatcher
    ) {
        $dispatcher->dispatch('log', $event);

        // ... more code
    }
}

While this above is sufficient for most uses, if your application uses multiple EventDispatcher instances, you might need to specifically inject a known instance of the EventDispatcher into your listeners. This could be done using constructor or setter injection as follows:

Constructor injection:

1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class Foo
{
    protected $dispatcher = null;

    public function __construct(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }
}

Or setter injection:

1
2
3
4
5
6
7
8
9
10
11
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class Foo
{
    protected $dispatcher = null;

    public function setEventDispatcher(EventDispatcherInterface $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }
}

Choosing between the two is really a matter of taste. Many tend to prefer the constructor injection as the objects are fully initialized at construction time. But when you have a long list of dependencies, using setter injection can be the way to go, especially for optional dependencies.

Dispatcher Shortcuts

The EventDispatcher::dispatch method always returns an Event object. This allows for various shortcuts. For example, if one does not need a custom event object, one can simply rely on a plain Event object. You do not even need to pass this to the dispatcher as it will create one by default unless you specifically pass one:

1
$dispatcher->dispatch('foo.event');

Moreover, the EventDispatcher always returns whichever event object that was dispatched, i.e. either the event that was passed or the event that was created internally by the dispatcher. This allows for nice shortcuts:

1
2
3
if (!$dispatcher->dispatch('foo.event')->isPropagationStopped()) {
    // ...
}

Or:

1
2
$barEvent = new BarEvent();
$bar = $dispatcher->dispatch('bar.event', $barEvent)->getBar();

Or:

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

and so on...

Event Name Introspection

2.4

Before Symfony 2.4, the event name and the event dispatcher had to be requested from the Event instance. These methods are now deprecated.

The EventDispatcher instance, as well as the name of the event that is dispatched, are passed as arguments to the listener:

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class Foo
{
    public function myEventListener(Event $event, $eventName, EventDispatcherInterface $dispatcher)
    {
        echo $eventName;
    }
}

Other Dispatchers

Besides the commonly used EventDispatcher, the component comes with 2 other dispatchers:

  • The Container Aware Event Dispatcher
  • The Immutable Event Dispatcher
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    We stand with Ukraine.
    Version:
    Online exam, become Sylius certified today

    Online exam, become Sylius certified today

    Check Code Performance in Dev, Test, Staging & Production

    Check Code Performance in Dev, Test, Staging & Production

    Symfony footer

    ↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

    Avatar of Mohammad, a Symfony contributor

    Thanks Mohammad for being a Symfony contributor

    1 commit • 2 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • Symfony at a Glance
      • Symfony Components
      • Case Studies
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • SymfonyConnect
      • Support
      • How to be Involved
      • Code of Conduct
      • Events & Meetups
      • Projects using Symfony
      • Downloads Stats
      • Contributors
      • Backers
    • Blog

      • Events & Meetups
      • A week of symfony
      • Case studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Documentation
      • Living on the edge
      • Releases
      • Security Advisories
      • SymfonyInsight
      • Twig
      • SensioLabs
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Deployed on

    Follow Symfony