made all event listeners lazy loaded

* The register() method on all listeners has been removed
 * Instead, the information is now put directly in the DIC tag

For instance, a listener on core.request had this method:

   public function register(EventDispatcher $dispatcher, $priority = 0)
   {
       $dispatcher->connect('core.response', array($this, 'filter'), $priority);
   }

And this tag in the DIC configuration:

  <tag name="kernel.listener" />

Now, it only has the following configuration:

  <tag name="kernel.listener" event="core.response" method="filter" priority="0" />

The event and method attributes are now mandatory.
This commit is contained in:
Fabien Potencier 2011-01-23 18:02:16 +01:00
parent 9310eea57a
commit 1c11d81611
23 changed files with 105 additions and 141 deletions

View File

@ -23,6 +23,8 @@ use Symfony\Component\EventDispatcher\Event;
* Converts \ReflectionParameters for Controller actions into Objects if the \ReflectionParameter have a class
* (Typehinted).
*
* The filterController method must be connected to the core.controller event.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.org>
* @author Henrik Bjornskov <hb@peytz.dk>
*/
@ -41,15 +43,6 @@ class ParamConverterListener
$this->manager = $manager;
}
/**
* @param EventDispatcher $dispatcher
* @param integer $priority = 0
*/
public function register(EventDispatcher $dispatcher, $priority = 0)
{
$dispatcher->connect('core.controller', array($this, 'filterController'), $priority);
}
/**
* @param Event $event
* @param mixed $controller

View File

@ -16,6 +16,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpKernel\Debug\EventDispatcherTraceableInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* EventDispatcher extends the original EventDispatcher class to add some debugging tools.
@ -30,10 +31,13 @@ class EventDispatcher extends BaseEventDispatcher implements EventDispatcherTrac
/**
* Constructor.
*
* @param LoggerInterface $logger A LoggerInterface instance
* @param ContainerInterface $container A ContainerInterface instance
* @param LoggerInterface $logger A LoggerInterface instance
*/
public function __construct(LoggerInterface $logger = null)
public function __construct(ContainerInterface $container, LoggerInterface $logger = null)
{
parent::__construct($container);
$this->logger = $logger;
$this->called = array();
}

View File

@ -24,14 +24,25 @@ class RegisterKernelListenersPass implements CompilerPassInterface
}
$listeners = array();
foreach ($container->findTaggedServiceIds('kernel.listener') as $id => $attributes) {
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
foreach ($container->findTaggedServiceIds('kernel.listener') as $id => $events) {
foreach ($events as $event) {
$priority = isset($event['priority']) ? $event['priority'] : 0;
if (!isset($event['event'])) {
throw new \InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "kernel.listener" tags.', $id));
}
if (!isset($event['method'])) {
throw new \InvalidArgumentException(sprintf('Service "%s" must define the "method" attribute on "kernel.listener" tags.', $id));
}
if (!isset($listeners[$priority])) {
$listeners[$priority] = array();
if (!isset($listeners[$event['event']][$priority])) {
if (!isset($listeners[$event['event']])) {
$listeners[$event['event']] = array();
}
$listeners[$event['event']][$priority] = array();
}
$listeners[$event['event']][$priority][$id] = $event['method'];
}
$listeners[$priority][] = new Reference($id);
}
$container
@ -39,4 +50,4 @@ class RegisterKernelListenersPass implements CompilerPassInterface
->addMethodCall('registerKernelListeners', array($listeners))
;
}
}
}

View File

@ -12,6 +12,7 @@
namespace Symfony\Bundle\FrameworkBundle;
use Symfony\Component\EventDispatcher\EventDispatcher as BaseEventDispatcher;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* This EventDispatcher automatically gets the kernel listeners injected
@ -20,12 +21,42 @@ use Symfony\Component\EventDispatcher\EventDispatcher as BaseEventDispatcher;
*/
class EventDispatcher extends BaseEventDispatcher
{
public function registerKernelListeners(array $kernelListeners)
protected $container;
protected $ids;
/**
* Constructor.
*
* @param ContainerInterface $container A ContainerInterface instance
*/
public function __construct(ContainerInterface $container)
{
foreach ($kernelListeners as $priority => $listeners) {
foreach ($listeners as $listener) {
$listener->register($this, $priority);
$this->container = $container;
}
public function registerKernelListeners(array $ids)
{
$this->ids = $ids;
}
/**
* {@inheritdoc}
*/
public function getListeners($name)
{
if (!isset($this->ids[$name])) {
return array();
}
$listeners = array();
$all = $this->ids[$name];
krsort($all);
foreach ($all as $l) {
foreach ($l as $id => $method) {
$listeners[] = array($this->container->get($id), $method);
}
}
return $listeners;
}
}

View File

@ -21,6 +21,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* RequestListener.
*
* The handle method must be connected to the core.request event.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class RequestListener
@ -36,17 +38,6 @@ class RequestListener
$this->logger = $logger;
}
/**
* Registers a core.request listener.
*
* @param EventDispatcher $dispatcher An EventDispatcher instance
* @param integer $priority The priority
*/
public function register(EventDispatcher $dispatcher, $priority = 0)
{
$dispatcher->connect('core.request', array($this, 'handle'), $priority);
}
public function handle(Event $event)
{
$request = $event->get('request');

View File

@ -10,6 +10,7 @@
<services>
<service id="debug.event_dispatcher" class="%debug.event_dispatcher.class%">
<argument type="service" id="service_container" />
<argument type="service" id="logger" on-invalid="null" />
</service>
</services>

View File

@ -12,8 +12,8 @@
<services>
<service id="esi" class="%esi.class%" public="false" />
<service id="esi_listener" class="%esi_listener.class%" public="false">
<tag name="kernel.listener" />
<service id="esi_listener" class="%esi_listener.class%">
<tag name="kernel.listener" event="core.response" method="filter" />
<argument type="service" id="esi" on-invalid="ignore" />
</service>
</services>

View File

@ -14,8 +14,8 @@
<service id="request.param_converter.manager" class="%request.param_converter.manager.class%" public="false" />
<!-- ParamConverterListener -->
<service id="request.param_converter.listener" class="%request.param_converter.listener.class%" public="false">
<tag name="kernel.listener" />
<service id="request.param_converter.listener" class="%request.param_converter.listener.class%">
<tag name="kernel.listener" event="core.controller" method="filterController" />
<argument type="service" id="request.param_converter.manager" />
</service>
</services>

View File

@ -24,8 +24,9 @@
<argument>%profiler.storage.lifetime%</argument>
</service>
<service id="profiler_listener" class="%profiler_listener.class%" public="false">
<tag name="kernel.listener" />
<service id="profiler_listener" class="%profiler_listener.class%">
<tag name="kernel.listener" event="core.response" method="handleResponse" />
<tag name="kernel.listener" event="core.exception" method="handleException" />
<argument type="service" id="profiler" />
<argument type="service" id="profiler.request_matcher" on-invalid="null" />
<argument>%profiler_listener.only_exceptions%</argument>

View File

@ -154,15 +154,17 @@
<argument type="service" id="security.role_hierarchy" />
</service>
<service id="security.firewall" class="%security.firewall.class%" public="false">
<tag name="kernel.listener" priority="-128" />
<service id="security.firewall" class="%security.firewall.class%">
<tag name="kernel.listener" event="core.request" method="handle" priority="-128" />
<argument type="service" id="security.firewall.map" />
<argument type="service" id="event_dispatcher" />
</service>
<service id="security.firewall.map" class="%security.firewall.map.class%" public="false">
<argument type="service" id="service_container" />
<argument type="collection" />
</service>
<service id="security.context_listener" class="%security.context_listener.class%" public="false">
<argument type="service" id="security.context" />
<argument type="collection"></argument>

View File

@ -15,6 +15,7 @@
<services>
<service id="event_dispatcher" class="%event_dispatcher.class%">
<argument type="service" id="service_container" />
</service>
<service id="error_handler" class="%error_handler.class%" public="false">

View File

@ -25,19 +25,19 @@
<argument type="service" id="logger" on-invalid="ignore" />
</service>
<service id="request_listener" class="%request_listener.class%" public="false">
<tag name="kernel.listener" />
<service id="request_listener" class="%request_listener.class%">
<tag name="kernel.listener" event="core.request" method="handle" />
<argument type="service" id="service_container" />
<argument type="service" id="router" />
<argument type="service" id="logger" on-invalid="ignore" />
</service>
<service id="response_listener" class="%response_listener.class%" public="false">
<tag name="kernel.listener" />
<service id="response_listener" class="%response_listener.class%">
<tag name="kernel.listener" event="core.response" method="filter" />
</service>
<service id="exception_listener" class="%exception_listener.class%" public="false">
<tag name="kernel.listener" priority="-128" />
<service id="exception_listener" class="%exception_listener.class%">
<tag name="kernel.listener" event="core.exception" method="handle" priority="-128" />
<argument>%exception_listener.controller%</argument>
<argument type="service" id="logger" on-invalid="null" />
</service>

View File

@ -10,8 +10,8 @@
</parameters>
<services>
<service id="debug.toolbar" class="%debug.toolbar.class%" public="false">
<tag name="kernel.listener" priority="-128" />
<service id="debug.toolbar" class="%debug.toolbar.class%">
<tag name="kernel.listener" event="core.response" method="handle" priority="-128" />
<argument type="service" id="http_kernel" />
<argument>%debug.toolbar.intercept_redirects%</argument>
</service>

View File

@ -21,6 +21,8 @@ use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
* WebDebugToolbarListener injects the Web Debug Toolbar.
*
* The handle method must be connected to the core.response event.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class WebDebugToolbarListener
@ -34,17 +36,6 @@ class WebDebugToolbarListener
$this->interceptRedirects = $interceptRedirects;
}
/**
* Registers a core.response listener.
*
* @param EventDispatcher $dispatcher An EventDispatcher instance
* @param integer $priority The priority
*/
public function register(EventDispatcher $dispatcher, $priority = 0)
{
$dispatcher->connect('core.response', array($this, 'handle'), $priority);
}
public function handle(Event $event, Response $response)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->get('request_type')) {

View File

@ -18,6 +18,8 @@ use Symfony\Component\EventDispatcher\EventDispatcher;
/**
* EsiListener adds a Surrogate-Control HTTP header when the Response needs to be parsed for ESI.
*
* The filter method must be connected to the core.response event.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class EsiListener
@ -35,20 +37,6 @@ class EsiListener
$this->esi = $esi;
}
/**
* Registers a core.response listener to add the Surrogate-Control header to a Response when needed.
*
* @param EventDispatcher $dispatcher An EventDispatcher instance
* @param integer $priority The priority
*/
public function register(EventDispatcher $dispatcher, $priority = 0)
{
if (null !== $this->esi)
{
$dispatcher->connect('core.response', array($this, 'filter'), $priority);
}
}
/**
* Filters the Response.
*
@ -57,7 +45,7 @@ class EsiListener
*/
public function filter($event, Response $response)
{
if (HttpKernelInterface::MASTER_REQUEST !== $event->get('request_type')) {
if (HttpKernelInterface::MASTER_REQUEST !== $event->get('request_type') || null === $this->esi) {
return $response;
}

View File

@ -22,6 +22,8 @@ use Symfony\Component\HttpFoundation\Request;
/**
* ExceptionListener.
*
* The handle method must be connected to the core.exception event.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ExceptionListener
@ -35,17 +37,6 @@ class ExceptionListener
$this->logger = $logger;
}
/**
* Registers a core.exception listener.
*
* @param EventDispatcher $dispatcher An EventDispatcher instance
* @param integer $priority The priority
*/
public function register(EventDispatcher $dispatcher, $priority = 0)
{
$dispatcher->connect('core.exception', array($this, 'handle'), $priority);
}
public function handle(Event $event)
{
static $handling;

View File

@ -20,6 +20,9 @@ use Symfony\Component\HttpFoundation\RequestMatcherInterface;
/**
* ProfilerListener collects data for the current request by listening to the core.response event.
*
* The handleException method must be connected to the core.exception event.
* The handleResponse method must be connected to the core.response event.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ProfilerListener
@ -43,18 +46,6 @@ class ProfilerListener
$this->onlyException = $onlyException;
}
/**
* Registers a core.response and core.exception listeners.
*
* @param EventDispatcher $dispatcher An EventDispatcher instance
* @param integer $priority The priority
*/
public function register(EventDispatcher $dispatcher, $priority = 0)
{
$dispatcher->connect('core.exception', array($this, 'handleException'), $priority);
$dispatcher->connect('core.response', array($this, 'handleResponse'), $priority);
}
/**
* Handles the core.exception event.
*

View File

@ -27,7 +27,6 @@ ClassCollectionLoader::load(array(
'Symfony\\Component\\HttpKernel\\Bundle\\BundleInterface',
'Symfony\\Component\\HttpKernel\\Debug\\ErrorHandler',
'Symfony\\Component\\HttpKernel\\ClassCollectionLoader',
'Symfony\\Component\\HttpKernel\\Debug\\ExceptionListener',
'Symfony\\Component\\DependencyInjection\\Container',
'Symfony\\Component\\DependencyInjection\\ContainerAwareInterface',

View File

@ -18,21 +18,12 @@ use Symfony\Component\HttpFoundation\Response;
/**
* ResponseListener fixes the Response Content-Type.
*
* The filter method must be connected to the core.response event.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ResponseListener
{
/**
* Registers a core.response listener to change the Content-Type header based on the Request format.
*
* @param EventDispatcher $dispatcher An EventDispatcher instance
* @param integer $priority The priority
*/
public function register(EventDispatcher $dispatcher, $priority = 0)
{
$dispatcher->connect('core.response', array($this, 'filter'), $priority);
}
/**
* Filters the Response.
*

View File

@ -24,6 +24,8 @@ use Symfony\Component\HttpFoundation\Request;
* (a Basic authentication for the /api, and a web based authentication for
* everything else for instance).
*
* The handle method must be connected to the core.request event.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Firewall
@ -37,22 +39,11 @@ class Firewall
*
* @param FirewallMap $map A FirewallMap instance
*/
public function __construct(FirewallMapInterface $map)
public function __construct(FirewallMapInterface $map, EventDispatcher $dispatcher)
{
$this->map = $map;
$this->currentListeners = array();
}
/**
* Registers a core.request listener to enforce security.
*
* @param EventDispatcher $dispatcher An EventDispatcher instance
* @param integer $priority The priority
*/
public function register(EventDispatcher $dispatcher, $priority = 0)
{
$dispatcher->connect('core.request', array($this, 'handle'), $priority);
$this->dispatcher = $dispatcher;
$this->currentListeners = array();
}
/**

View File

@ -232,10 +232,6 @@ class ExceptionListener
$this->controller = $controller;
$this->logger = $logger;
}
public function register(EventDispatcher $dispatcher, $priority = 0)
{
$dispatcher->connect('core.exception', array($this, 'handle'), $priority);
}
public function handle(Event $event)
{
static $handling;

View File

@ -24,7 +24,7 @@ class EsiListenerTest extends \PHPUnit_Framework_TestCase
{
$dispatcher = new EventDispatcher();
$listener = new EsiListener(new Esi());
$listener->register($dispatcher);
$dispatcher->connect('core.response', array($listener, 'filter'));
$event = new Event(null, 'core.response', array('request_type' => HttpKernelInterface::SUB_REQUEST));
$dispatcher->filter($event, $response = new Response('foo <esi:include src="" />'));
@ -32,20 +32,11 @@ class EsiListenerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('', $response->headers->get('Surrogate-Control'));
}
public function testNothingIsRegisteredIfEsiIsNull()
{
$dispatcher = new EventDispatcher();
$listener = new EsiListener();
$listener->register($dispatcher);
$this->assertEquals(array(), $dispatcher->getListeners('core.response'));
}
public function testFilterWhenThereIsSomeEsiIncludes()
{
$dispatcher = new EventDispatcher();
$listener = new EsiListener(new Esi());
$listener->register($dispatcher);
$dispatcher->connect('core.response', array($listener, 'filter'));
$event = new Event(null, 'core.response', array('request_type' => HttpKernelInterface::MASTER_REQUEST));
$dispatcher->filter($event, $response = new Response('foo <esi:include src="" />'));
@ -57,7 +48,7 @@ class EsiListenerTest extends \PHPUnit_Framework_TestCase
{
$dispatcher = new EventDispatcher();
$listener = new EsiListener(new Esi());
$listener->register($dispatcher);
$dispatcher->connect('core.response', array($listener, 'filter'));
$event = new Event(null, 'core.response', array('request_type' => HttpKernelInterface::MASTER_REQUEST));
$dispatcher->filter($event, $response = new Response('foo'));

View File

@ -60,7 +60,7 @@ class ResponseListenerTest extends \PHPUnit_Framework_TestCase
{
$dispatcher = new EventDispatcher();
$listener = new ResponseListener();
$listener->register($dispatcher);
$dispatcher->connect('core.response', array($listener, 'filter'));
return $dispatcher;
}