Implementation of kernel.event_subscriber tag for services.

This commit is contained in:
Jordan Alliot 2011-08-23 20:35:38 +01:00
parent 02b30d929b
commit 8b240d4c22
4 changed files with 173 additions and 0 deletions

View File

@ -21,6 +21,7 @@ use Symfony\Component\EventDispatcher\Event;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
* @author Jordan Alliot <jordan.alliot@gmail.com>
*/
class ContainerAwareEventDispatcher extends EventDispatcher
{
@ -103,6 +104,32 @@ class ContainerAwareEventDispatcher extends EventDispatcher
return parent::getListeners($eventName);
}
/**
* Adds a service as event subscriber
*
* If this service is created by a factory, its class value must be correctly filled.
* The service's class must implement Symfony\Component\EventDispatcher\EventSubscriberInterface.
*
* @param string $serviceId The service ID of the subscriber service
* @param string $class The service's class name
*/
public function addSubscriberService($serviceId, $class)
{
$refClass = new \ReflectionClass($class);
$interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
if (!$refClass->implementsInterface($interface)) {
throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $serviceId, $interface));
}
foreach ($class::getSubscribedEvents() as $eventName => $params) {
if (is_string($params)) {
$this->listenerIds[$eventName][] = array($serviceId, $params, 0);
} else {
$this->listenerIds[$eventName][] = array($serviceId, $params[0], $params[1]);
}
}
}
/**
* {@inheritDoc}
*

View File

@ -43,5 +43,18 @@ class RegisterKernelListenersPass implements CompilerPassInterface
$definition->addMethodCall('addListenerService', array($event['event'], array($id, $event['method']), $priority));
}
}
foreach ($container->findTaggedServiceIds('kernel.event_subscriber') as $id => $attributes) {
// We must assume that the class value has been correcly filled, even if the service is created by a factory
$class = $container->getDefinition($id)->getClass();
$refClass = new \ReflectionClass($class);
$interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
if (!$refClass->implementsInterface($interface)) {
throw new \InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, $interface));
}
$definition->addMethodCall('addSubscriberService', array($id, $class));
}
}
}

View File

@ -14,6 +14,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Bundle\FrameworkBundle\ContainerAwareEventDispatcher;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\DependencyInjection\Scope;
class ContainerAwareEventDispatcherTest extends \PHPUnit_Framework_TestCase
@ -39,6 +40,36 @@ class ContainerAwareEventDispatcherTest extends \PHPUnit_Framework_TestCase
$dispatcher->dispatch('onEvent', $event);
}
public function testAddASubscriberService()
{
$event = new Event();
$service = $this->getMock('Symfony\Bundle\FrameworkBundle\Tests\SubscriberService');
$service
->expects($this->once())
->method('onEvent')
->with($event)
;
$container = new Container();
$container->set('service.subscriber', $service);
$dispatcher = new ContainerAwareEventDispatcher($container);
$dispatcher->addSubscriberService('service.subscriber', 'Symfony\Bundle\FrameworkBundle\Tests\SubscriberService');
$dispatcher->dispatch('onEvent', $event);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testTriggerASubscriberDoesntImplementInterface()
{
$dispatcher = new ContainerAwareEventDispatcher(new Container());
$dispatcher->addSubscriberService('service.subscriber', 'Symfony\Bundle\FrameworkBundle\Tests\Service');
}
public function testPreventDuplicateListenerService()
{
$event = new Event();
@ -174,3 +205,16 @@ class Service
{
}
}
class SubscriberService implements EventSubscriberInterface
{
static function getSubscribedEvents() {
return array(
'onEvent' => 'onEvent',
);
}
function onEvent(Event $e)
{
}
}

View File

@ -0,0 +1,89 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RegisterKernelListenersPass;
class RegisterKernelListenersPassTest extends \PHPUnit_Framework_TestCase
{
/**
* Tests that event subscribers not implementing EventSubscriberInterface
* trigger an exception.
*
* @expectedException \InvalidArgumentException
*/
public function testEventSubscriberWithoutInterface()
{
// one service, not implementing any interface
$services = array(
'my_event_subscriber' => array(0 => array()),
);
$definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
$definition->expects($this->atLeastOnce())
->method('getClass')
->will($this->returnValue('stdClass'));
$builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
$builder->expects($this->any())
->method('hasDefinition')
->will($this->returnValue(true));
// We don't test kernel.event_listener here
$builder->expects($this->atLeastOnce())
->method('findTaggedServiceIds')
->will($this->onConsecutiveCalls(array(), $services));
$builder->expects($this->atLeastOnce())
->method('getDefinition')
->will($this->returnValue($definition));
$registerListenersPass = new RegisterKernelListenersPass();
$registerListenersPass->process($builder);
}
public function testValidEventSubscriber()
{
$services = array(
'my_event_subscriber' => array(0 => array()),
);
$definition = $this->getMock('Symfony\Component\DependencyInjection\Definition');
$definition->expects($this->atLeastOnce())
->method('getClass')
->will($this->returnValue('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\SubscriberService'));
$builder = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
$builder->expects($this->any())
->method('hasDefinition')
->will($this->returnValue(true));
// We don't test kernel.event_listener here
$builder->expects($this->atLeastOnce())
->method('findTaggedServiceIds')
->will($this->onConsecutiveCalls(array(), $services));
$builder->expects($this->atLeastOnce())
->method('getDefinition')
->will($this->returnValue($definition));
$registerListenersPass = new RegisterKernelListenersPass();
$registerListenersPass->process($builder);
}
}
class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface
{
static function getSubscribedEvents() {}
}