Symfony
sponsored by SensioLabs
Menu
  • About
  • Documentation
  • Screencasts
  • Cloud
  • Certification
  • Community
  • Businesses
  • News
  • Download
  1. Home
  2. Documentation
  3. Event Dispatcher
  4. How to Set Up Before and After Filters
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud
Search by Algolia

Table of Contents

  • Token Validation Example
  • Before Filters with the kernel.controller Event
    • Tag Controllers to Be Checked
    • Creating an Event Subscriber
  • After Filters with the kernel.response Event

How to Set Up Before and After Filters

Edit this page

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

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

How to Set Up Before and After Filters

It is quite common in web application development to need some logic to be performed right before or directly after your controller actions acting as filters or hooks.

Some web frameworks define methods like preExecute() and postExecute(), but there is no such thing in Symfony. The good news is that there is a much better way to interfere with the Request -> Response process using the EventDispatcher component.

Token Validation Example

Imagine that you need to develop an API where some controllers are public but some others are restricted to one or some clients. For these private features, you might provide a token to your clients to identify themselves.

So, before executing your controller action, you need to check if the action is restricted or not. If it is restricted, you need to validate the provided token.

Note

Please note that for simplicity in this recipe, tokens will be defined in config and neither database setup nor authentication via the Security component will be used.

Before Filters with the kernel.controller Event

First, define some token configuration as parameters:

  • YAML
  • XML
  • PHP
1
2
3
4
5
# config/services.yaml
parameters:
    tokens:
        client1: pass1
        client2: pass2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <parameters>
        <parameter key="tokens" type="collection">
            <parameter key="client1">pass1</parameter>
            <parameter key="client2">pass2</parameter>
        </parameter>
    </parameters>
</container>
1
2
3
4
5
// config/services.php
$container->setParameter('tokens', [
    'client1' => 'pass1',
    'client2' => 'pass2',
]);

Tag Controllers to Be Checked

A kernel.controller (aka KernelEvents::CONTROLLER) listener gets notified on every request, right before the controller is executed. So, first, you need some way to identify if the controller that matches the request needs token validation.

A clean and easy way is to create an empty interface and make the controllers implement it:

1
2
3
4
5
6
namespace App\Controller;

interface TokenAuthenticatedController
{
    // ...
}

A controller that implements this interface looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace App\Controller;

use App\Controller\TokenAuthenticatedController;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class FooController extends AbstractController implements TokenAuthenticatedController
{
    // An action that needs authentication
    public function bar()
    {
        // ...
    }
}

Creating an Event Subscriber

Next, you'll need to create an event subscriber, which will hold the logic that you want to be executed before your controllers. If you're not familiar with event subscribers, you can learn more about them at Events and 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// src/EventSubscriber/TokenSubscriber.php
namespace App\EventSubscriber;

use App\Controller\TokenAuthenticatedController;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\KernelEvents;

class TokenSubscriber implements EventSubscriberInterface
{
    private $tokens;

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

    public function onKernelController(ControllerEvent $event)
    {
        $controller = $event->getController();

        // when a controller class defines multiple action methods, the controller
        // is returned as [$controllerInstance, 'methodName']
        if (is_array($controller)) {
            $controller = $controller[0];
        }

        if ($controller instanceof TokenAuthenticatedController) {
            $token = $event->getRequest()->query->get('token');
            if (!in_array($token, $this->tokens)) {
                throw new AccessDeniedHttpException('This action needs a valid token!');
            }
        }
    }

    public static function getSubscribedEvents()
    {
        return [
            KernelEvents::CONTROLLER => 'onKernelController',
        ];
    }
}

That's it! Your services.yaml file should already be setup to load services from the EventSubscriber directory. Symfony takes care of the rest. Your TokenSubscriber onKernelController() method will be executed on each request. If the controller that is about to be executed implements TokenAuthenticatedController, token authentication is applied. This lets you have a "before" filter on any controller you want.

Tip

If your subscriber is not called on each request, double-check that you're loading services from the EventSubscriber directory and have autoconfigure enabled. You can also manually add the kernel.event_subscriber tag.

After Filters with the kernel.response Event

In addition to having a "hook" that's executed before your controller, you can also add a hook that's executed after your controller. For this example, imagine that you want to add a sha1 hash (with a salt using that token) to all responses that have passed this token authentication.

Another core Symfony event - called kernel.response (aka KernelEvents::RESPONSE) - is notified on every request, but after the controller returns a Response object. To create an "after" listener, create a listener class and register it as a service on this event.

For example, take the TokenSubscriber from the previous example and first record the authentication token inside the request attributes. This will serve as a basic flag that this request underwent token authentication:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function onKernelController(ControllerEvent $event)
{
    // ...

    if ($controller instanceof TokenAuthenticatedController) {
        $token = $event->getRequest()->query->get('token');
        if (!in_array($token, $this->tokens)) {
            throw new AccessDeniedHttpException('This action needs a valid token!');
        }

        // mark the request as having passed token authentication
        $event->getRequest()->attributes->set('auth_token', $token);
    }
}

Now, configure the subscriber to listen to another event and add onKernelResponse(). This will look for the auth_token flag on the request object and set a custom header on the response if it's found:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// add the new use statement at the top of your file
use Symfony\Component\HttpKernel\Event\ResponseEvent;

public function onKernelResponse(ResponseEvent $event)
{
    // check to see if onKernelController marked this as a token "auth'ed" request
    if (!$token = $event->getRequest()->attributes->get('auth_token')) {
        return;
    }

    $response = $event->getResponse();

    // create a hash and set it as a response header
    $hash = sha1($response->getContent().$token);
    $response->headers->set('X-CONTENT-HASH', $hash);
}

public static function getSubscribedEvents()
{
    return [
        KernelEvents::CONTROLLER => 'onKernelController',
        KernelEvents::RESPONSE => 'onKernelResponse',
    ];
}

That's it! The TokenSubscriber is now notified before every controller is executed (onKernelController()) and after every controller returns a response (onKernelResponse()). By making specific controllers implement the TokenAuthenticatedController interface, your listener knows which controllers it should take action on. And by storing a value in the request's "attributes" bag, the onKernelResponse() method knows to add the extra header. Have fun!

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
We stand with Ukraine.
Version:

Symfony 5.3 is backed by

Measure & Improve Symfony Code Performance

Measure & Improve Symfony Code Performance

Check Code Performance in Dev, Test, Staging & Production

Check Code Performance in Dev, Test, Staging & Production

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

Avatar of Manuel Reinhard, a Symfony contributor

Thanks Manuel Reinhard (@sprain) for being a Symfony contributor

12 commits • 666 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
Search by Algolia