Contributed by Nicolas Grekas in #66012 and #66022

If you've ever needed to run an event listener before one provided by Symfony or a third-party bundle, you had to find its priority, pick a slightly higher number and hope that priority doesn't change in a future update.

In Symfony 8.2, you can solve this in a better way: the new before and after options let you name the event listeners or tagged services that yours should run before or after.

Ordering Event Listeners

Suppose your listener sets the _locale request attribute. It needs to run before Symfony's LocaleListener reads that attribute to set the request's locale. You can now declare that directly on your listener:

// src/EventListener/RequestLocaleListener.php
namespace App\EventListener;

use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\EventListener\LocaleListener;

#[AsEventListener(before: LocaleListener::class.'::onKernelRequest')]
final class RequestLocaleListener
{
    public function __invoke(RequestEvent $event): void
    {
        // ...
    }
}

Both options accept a service ID or class name, or an array of them if you need to refer to several listeners. The ::onKernelRequest suffix selects a specific method. This matters here because LocaleListener has two methods that listen to kernel.request. Without the suffix, your listener would run before both of them.

Event subscribers registered as services can use these options too. Symfony 8.2 also lets you use named keys such as method and priority in getSubscribedEvents(), so you can write:

public static function getSubscribedEvents(): array
{
    return [
        KernelEvents::REQUEST => [
            'method' => 'onKernelRequest',
            'before' => LocaleListener::class.'::onKernelRequest',
        ],
    ];
}

A few things to keep in mind:

  • Ordering is resolved when compiling the container, with no extra runtime work;
  • Leave out the priority to let Symfony choose it. If you set one, constraints can only reorder listeners within that priority;
  • References to listeners missing from the event are ignored (this way, you can support e.g. optional bundles);
  • Conflicting priorities, circular constraints and invalid methods cause compilation errors.

Ordering Tagged Services

The same options are available when ordering tagged services. Suppose an import bundle provides CSV and XLSX handlers with the same priority, and you want to add your own TSV handler between them. You can express both requirements with AsTaggedItem or in the service's tag configuration:

PHP YAML
// src/Handler/TsvHandler.php
namespace App\Handler;

use Acme\ImportBundle\Handler\CsvHandler;
use Acme\ImportBundle\Handler\XlsxHandler;
use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;

#[AsTaggedItem(after: CsvHandler::class, before: XlsxHandler::class)]
class TsvHandler
{
    // ...
}
# config/services.yaml
services:
    App\Handler\TsvHandler:
        tags:
            - name: 'app.handler'
              after: 'Acme\ImportBundle\Handler\CsvHandler'
              before: 'Acme\ImportBundle\Handler\XlsxHandler'

In the PHP example, the handler must already have the tag used by the import bundle, for example through autoconfiguration. AsTaggedItem configures its order; it doesn't add the tag itself.

For more examples, see the docs on ordering event listeners and ordering tagged services.

Sponsor the Symfony project.