Skip to content

Commit

Permalink
Implementation of kernel.event_subscriber tag for services.
Browse files Browse the repository at this point in the history
  • Loading branch information
jalliot committed Sep 29, 2011
1 parent 02b30d9 commit 8b240d4
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 0 deletions.
Expand Up @@ -21,6 +21,7 @@
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bernhard.schussek@symfony.com>
* @author Jordan Alliot <jordan.alliot@gmail.com>
*/
class ContainerAwareEventDispatcher extends EventDispatcher
{
Expand Down Expand Up @@ -103,6 +104,32 @@ public function getListeners($eventName = null)
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}
*
Expand Down
Expand Up @@ -43,5 +43,18 @@ public function process(ContainerBuilder $container)
$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));
}
}
}
Expand Up @@ -14,6 +14,7 @@
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
Expand All @@ -39,6 +40,36 @@ public function testAddAListenerService()
$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();
Expand Down Expand Up @@ -174,3 +205,16 @@ function onEvent(Event $e)
{
}
}

class SubscriberService implements EventSubscriberInterface
{
static function getSubscribedEvents() {
return array(
'onEvent' => 'onEvent',
);
}

function onEvent(Event $e)
{
}
}
@@ -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() {}
}

0 comments on commit 8b240d4

Please sign in to comment.