Skip to content

Commit 38949cd

Browse files
committed
Merge pull request #1461 from ricardclau/2.0
first draft cookbook before/after listeners (issue #649)
2 parents 604172d + 08db41d commit 38949cd

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
.. index::
2+
single: Event Dispatcher
3+
4+
How to setup before and after filters
5+
=====================================
6+
7+
It is quite common in web applications development to need some logic to be executed just before
8+
or just after our controller actions acting as filters or hooks.
9+
10+
In Symfony1, this was achieved with the preExecute and postExecute methods, most major frameworks have similar
11+
methods but there is no such thing in Symfony2. Good news is that there is a much better way to interfere our
12+
Request -> Response process with the EventDispatcher component.
13+
14+
Token validation example
15+
========================
16+
17+
Imagine that we need to develop an API where some controllers are public but some others are restricted
18+
to one or some clients. For this private features, we provide a token to our clients to identify themselves.
19+
20+
So, before executing our controller action, we need to check if the action is restricted or not.
21+
And if it is restricted, we need to validate the provided token.
22+
23+
.. note::
24+
25+
Please note that for simplicity in the recipe, tokens will be defined in config
26+
and neither database setup nor authentication provider via Security component will be used
27+
28+
Creating a before filter with a controller.request event
29+
========================================================
30+
31+
Basic Setup
32+
-----------
33+
34+
We can add basic tokens configuration using config.yml and parameters key
35+
36+
.. configuration-block::
37+
38+
.. code-block:: yaml
39+
40+
# app/config/config.yml
41+
parameters:
42+
tokens:
43+
client1: pass1
44+
client2: pass2
45+
46+
.. code-block:: xml
47+
48+
<!-- app/config/config.xml -->
49+
<parameters>
50+
<parameter key="tokens" type="collection">
51+
<parameter key="client1">pass1</parameter>
52+
<parameter key="client2">pass2</parameter>
53+
</parameter>
54+
</parameters>
55+
56+
.. code-block:: php
57+
58+
// app/config/config.php
59+
$container->setParameter('tokens', array(
60+
'client1' => 'pass1',
61+
'client2' => 'pass2'
62+
));
63+
64+
Tag controllers to be checked
65+
-----------------------------
66+
67+
A kernel.controller listener gets executed at every request, so we need some way to identify
68+
if the controller that matches the request needs a token validation.
69+
70+
A clean and easy way is to create an empty interface and make the controllers implement it
71+
72+
.. code-block:: php
73+
74+
namespace Acme\DemoBundle\Controller;
75+
76+
interface TokenAuthenticatedController
77+
{
78+
// Nothing here
79+
}
80+
81+
class FooController implements TokenAuthenticatedController
82+
{
83+
// Your actions that need authentication
84+
}
85+
86+
Creating an Event Listener
87+
--------------------------
88+
89+
.. code-block:: php
90+
91+
namespace Acme\DemoBundle\EventListener;
92+
93+
use Acme\DemoBundle\Controller\TokenAuthenticatedController;
94+
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
95+
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
96+
97+
class BeforeListener
98+
{
99+
private $tokens;
100+
101+
public function __contruct($tokens)
102+
{
103+
$this->tokens = $tokens;
104+
}
105+
106+
public function onKernelController(FilterControllerEvent $event)
107+
{
108+
$controller = $event->getController();
109+
110+
/*
111+
* $controller passed can be either a class or a Closure. This is not usual in Symfony2 but it may happen.
112+
* If it is a class, it comes in array format
113+
*/
114+
if (!is_array($controller)) {
115+
return;
116+
}
117+
118+
if($controller[0] instanceof TokenAuthenticatedController) {
119+
$token = $event->getRequest()->get('token');
120+
if (!in_array($token, $this->tokens)) {
121+
throw new AccessDeniedHttpException('This action needs a valid token!');
122+
}
123+
}
124+
}
125+
}
126+
127+
Registering the listener
128+
------------------------
129+
130+
.. configuration-block::
131+
132+
.. code-block:: yaml
133+
134+
# app/config/config.yml (or inside or your services.yml)
135+
services:
136+
demo.tokens.action_listener:
137+
class: Acme\DemoBundle\EventListener\BeforeListener
138+
arguments: [ %tokens% ]
139+
tags:
140+
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
141+
142+
.. code-block:: xml
143+
144+
<service id="demo.tokens.action_listener" class="Acme\DemoBundle\EventListener\BeforeListener">
145+
<argument>%tokens%</argument>
146+
<tag name="kernel.event_listener" event="kernel.controller" method="onKernelController" />
147+
</service>
148+
149+
.. code-block:: php
150+
151+
use Symfony\Component\DependencyInjection\Definition;
152+
153+
$listener = new Definition('Acme\DemoBundle\EventListener\BeforeListener', array('%tokens%'));
154+
$listener->addTag('kernel.event_listener', array('event' => 'kernel.controller', 'method' => 'onKernelController'));
155+
$container->setDefinition('demo.tokens.action_listener', $listener);
156+

0 commit comments

Comments
 (0)