Don't Ever use Symfony Listeners

Another anti-pattern that deserves more attention than it has. I often see this in Symfony projects I consult and when I ask the dev why did he or she choose listener over subscriber, they don't really know - "it was in the Symfony documentation, you can read it there".

Not good enough. So why you should never use a listener?

When we look into Symfony EventDispatcher documentation, this is the first YAML code we see:

# config/services.yaml
            - { name: kernel.event_listener, event: kernel.exception }

If I'd be in the process of learning Symfony, this would be my thoughts:

  • "I see that this option is the first one in the official Symfony documentation"
  • "Right above something called Subscriber"
  • "I guess I should use Listeners by default then. Why? For some unknown reason that only Symfony seniors know."

Btw, there is not even "Subscriber" in the main headline!

That's how you write a manipulative text if you wanted people to never use subscribers :).

What's Wrong With Listeners?

  • Juniors will use Listeners by default (everywhere they can) ❌
  • YAML configs will get fat with listener configuration for basically no advantage ❌
    • Since PSR-4 autodiscovery this hurts config readability more then ever
    • You have to remember the YAML syntax for right registration
    • Do you know you have to tag it with name & event?
    • Will _autoconfigure: true help you here?
    • What's the name of event - kernel_exception or kernel.error? Well, neither
  • What will you do if name of Kernel event will change? ❌
  • How do you analyse it with PHPStan? ❌
  • How do you upgrade it with Rector, when Symfony will create a BC break change? ❌
  • What if you decide to migrate to Laravel or the new best framework X later? ❌

All these problems will shoot you or your colleague in the back in the future. You've just opened doors for 6 more possible bugs and problems to come to your project #carpeyolodiem.

Most of these problems are a result of config programming - that just sucks.

Why You Should Always use Event Subscriber?

Listeners have only one valid use case - it's a 3rd party code located in your /vendor and someone else wants you to use it with event of your choice in config, e.g.:

# config/services.yaml
            - { name: kernel.event_listener, event: kernel.exception }
            - { name: kernel.event_listener, event: kernel.view }

If it would be a subscriber, it would be very similar to this:


namespace App;

use Vendor\ThirdPartyProject\Listener\UseMeListener;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class YourListener extends UseMeListener implements EventSubscriberInterface
    public static function getSubscribedEvents()
        return [
            KernelEvents::EXCEPTION => ['onKernelException'],
            KernelEvents::VIEW => ['onKernelView'],

What's wrong with this code? First, UseMeListener should be final, so you cannot break SOLID like this.

Let's take part by part.

1. Validated PHP over Config Typos

$myEvents = [
    KernelEvents::EXCEPTION => ['onKernelException'],
    KernelEvents::VIEW => ['onKernelView'],
  • Using KernelEvents::EXCEPTION constant is a big win! Instead of some string a config that we cannot analyze nor refactor, we have a constant. If you create a typo like KernelEvents::EXCEPTON, you'll know. If you make a typo in a config? Good luck!

  • How is 'onKernelView' string made? I have no idea. It's a convention name, that is somehow resolved from tag name in the config to a protected/public? local method that is called. We don't need that magic, right Mr. Potter?

2. Explicit Services instead of Circular-Coupled Subscriber/Listener

If someone has created a listener that you can re-use, it's an anti-pattern already.

  • Would you create a controller, that someone should call in another controller?
  • Or a command, that someone should use in their listener or controller?

People actually do that, the StackOverflow has dozens of questions like "how to call command in a controller" or "how to controller in a command".

3. Don't Rape! Delegate

Command, Controller, EventSubscriber, Listener - they all should be only delegating code to a model layer service. If you need to mutually call or inherit one in another, you're creating a code smell. That's a sign that you should decouple common logic to a service and pass it via constructor to both.

So instead of giving people the option to use your code wrong way, give them a service, they can call in e.g. the EventSubscriber.

4. Let Interface take the Responsibility

EventSubscriber has own interface, that guides you:


use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class MyCustomEventSubscriber implements EventSubscriberInterface
    public static function getSubscribedEvents()

There is still a bit of magic... what should be in getSubscribedEvents() method? Honestly, I have no idea. I don't want to remember what's written in code. So I'll use PHPStorm:

What's Better with Event Subscriber?

  • No config coding and all the related possible bugs ✅
  • Adding new subscribers mean 0-work in config
  • Adding new subscribed event mean 0-work in config
  • No option to miss-use delegators ✅
  • Easy to statically analyse ✅
  • Easy to instantly upgrade ✅
  • Any Symfony BC break will be easy to discover due to unused constant in exact line of code ✅

The trade-off worth the change

Final Unrelated Tip: Constants over Strings

If you use KernelEvents::VIEW constants within PHP code, you make the code also easier to debug.

Where is KernelEvents::VIEW event actually dispatched? Just search KernelEvents::VIEW (or better dispatch(KernelEvents::VIEW)) in /vendor and PHPStorm will show you the exact line. If you'd look for a string, it will lead to a false source of KernelEvents (just a reference list of all Kernel events).

Also, when the event name is changed in a constant to view_event, you don't mind. If you have view in the config, good luck!

This makes using constants so fun. My rule of thumb is:

When the same string is used at 2 different classes,
it's worth creating a constant to make it typo-proof.

Imagine you have some code like:

# in class A

# in class B

Now you need to get this resource somewhere else. Was it "source", "sources", "resource" or "directory"? You don't care, constant autocomplete in PHPStorm tells you:

Don't remember what you don't need to.

Happy coding!

Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors. I'd really appreciate it!