diff --git a/src/Symfony/Component/EventDispatcher/CompiledEventDispatcher.php b/src/Symfony/Component/EventDispatcher/CompiledEventDispatcher.php
new file mode 100644
index 0000000000000..63407768d39cf
--- /dev/null
+++ b/src/Symfony/Component/EventDispatcher/CompiledEventDispatcher.php
@@ -0,0 +1,249 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher;
+
+use Symfony\Component\DependencyInjection\IntrospectableContainerInterface;
+
+/**
+ * A performance optimized container aware event dispatcher.
+ *
+ * This version of the event dispatcher contains the following optimizations
+ * in comparison to the Symfony event dispatcher component:
+ *
+ *
+ * - Faster instantiation of the event dispatcher service
+ * -
+ * Instead of calling
addSubscriberService
once for each
+ * subscriber, a precompiled array of listener definitions is passed
+ * directly to the constructor. This is faster by an order of magnitude.
+ * The listeners are collected and prepared using a compiler
+ * pass.
+ *
+ * - Lazy instantiation of listeners
+ * -
+ * Services are only retrieved from the container just before invocation.
+ * Especially when dispatching the KernelEvents::REQUEST event, this leads
+ * to a more timely invocation of the first listener. Overall dispatch
+ * runtime is not affected by this change though.
+ *
+ *
+ */
+class CompiledEventDispatcher implements EventDispatcherInterface
+{
+ /**
+ * The service container.
+ *
+ * @var IntrospectableContainerInterface
+ */
+ private $container;
+
+ /**
+ * Listener definitions.
+ *
+ * A nested array of listener definitions keyed by event name and priority.
+ * A listener definition is an associative array with one of the following key
+ * value pairs:
+ * - callable: A callable listener
+ * - service: An array of the form
+ * array('id' => service id, 'method' => method name)
+ *
+ * A service entry will be resolved to a callable only just before its
+ * invocation.
+ *
+ * @var array
+ */
+ private $listeners;
+
+ /**
+ * Whether listeners need to be sorted prior to dispatch, keyed by event name.
+ *
+ * @var array
+ */
+ private $unsorted = array();
+
+ /**
+ * Constructs a container aware event dispatcher.
+ *
+ * @param IntrospectableContainerInterface $container
+ * The service container.
+ * @param array $listeners
+ * A nested array of listener definitions keyed by event name and priority.
+ * The array is expected to be ordered by priority. A listener definition is
+ * an associative array with one of the following key value pairs:
+ * - callable: A callable listener
+ * - service: An array of the form
+ * array('id' => service id, 'method' => method name)
+ * A service entry will be resolved to a callable only just before its
+ * invocation.
+ */
+ public function __construct(IntrospectableContainerInterface $container, array $listeners = array())
+ {
+ $this->container = $container;
+ $this->listeners = $listeners;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function dispatch($eventName, Event $event = null)
+ {
+ if (null === $event) {
+ $event = new Event();
+ }
+
+ $event->setDispatcher($this);
+ $event->setName($eventName);
+
+ if (isset($this->listeners[$eventName])) {
+ // Sort listeners if necessary.
+ if (isset($this->unsorted[$eventName])) {
+ krsort($this->listeners[$eventName]);
+ unset($this->unsorted[$eventName]);
+ }
+
+ // Invoke listeners and resolve callables if necessary.
+ foreach ($this->listeners[$eventName] as &$definitions) {
+ foreach ($definitions as &$definition) {
+ if (!isset($definition['callable'])) {
+ $definition['callable'] = array($this->container->get($definition['service']['id']), $definition['service']['method']);
+ }
+
+ call_user_func($definition['callable'], $event, $eventName, $this);
+ if ($event->isPropagationStopped()) {
+ return $event;
+ }
+ }
+ }
+ }
+
+ return $event;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getListeners($eventName = null)
+ {
+ $result = array();
+
+ if (null === $eventName) {
+ // If event name was omitted, collect all listeners of all events.
+ foreach (array_keys($this->listeners) as $eventName) {
+ $listeners = $this->getListeners($eventName);
+ if (!empty($listeners)) {
+ $result[$eventName] = $listeners;
+ }
+ }
+
+ return $result;
+ }
+
+ if (!isset($this->listeners[$eventName])) {
+ return $result;
+ }
+
+ // Sort listeners if necessary.
+ if (isset($this->unsorted[$eventName])) {
+ krsort($this->listeners[$eventName]);
+ unset($this->unsorted[$eventName]);
+ }
+
+ // Collect listeners and resolve callables if necessary.
+ foreach ($this->listeners[$eventName] as &$definitions) {
+ foreach ($definitions as &$definition) {
+ if (!isset($definition['callable'])) {
+ $definition['callable'] = array($this->container->get($definition['service']['id']), $definition['service']['method']);
+ }
+
+ $result[] = $definition['callable'];
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasListeners($eventName = null)
+ {
+ return (bool) count($this->getListeners($eventName));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addListener($eventName, $listener, $priority = 0)
+ {
+ $this->listeners[$eventName][$priority][] = array('callable' => $listener);
+ $this->unsorted[$eventName] = true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeListener($eventName, $listener)
+ {
+ if (!isset($this->listeners[$eventName])) {
+ return;
+ }
+
+ foreach ($this->listeners[$eventName] as $priority => $definitions) {
+ foreach ($definitions as $key => $definition) {
+ if (!isset($definition['callable'])) {
+ if (!$this->container->initialized($definition['service']['id'])) {
+ continue;
+ }
+ $definition['callable'] = array($this->container->get($definition['service']['id']), $definition['service']['method']);
+ }
+
+ if ($definition['callable'] === $listener) {
+ unset($this->listeners[$eventName][$priority][$key]);
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addSubscriber(EventSubscriberInterface $subscriber)
+ {
+ foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
+ if (is_string($params)) {
+ $this->addListener($eventName, array($subscriber, $params));
+ } elseif (is_string($params[0])) {
+ $this->addListener($eventName, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0);
+ } else {
+ foreach ($params as $listener) {
+ $this->addListener($eventName, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0);
+ }
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeSubscriber(EventSubscriberInterface $subscriber)
+ {
+ foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
+ if (is_array($params) && is_array($params[0])) {
+ foreach ($params as $listener) {
+ $this->removeListener($eventName, array($subscriber, $listener[0]));
+ }
+ } else {
+ $this->removeListener($eventName, array($subscriber, is_string($params) ? $params : $params[0]));
+ }
+ }
+ }
+}
diff --git a/src/Symfony/Component/EventDispatcher/DependencyInjection/CompiledRegisterListenersPass.php b/src/Symfony/Component/EventDispatcher/DependencyInjection/CompiledRegisterListenersPass.php
new file mode 100644
index 0000000000000..81dbd0c80982a
--- /dev/null
+++ b/src/Symfony/Component/EventDispatcher/DependencyInjection/CompiledRegisterListenersPass.php
@@ -0,0 +1,141 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+
+/**
+ * Compiler pass to register tagged services for a compiled event dispatcher.
+ */
+class CompiledRegisterListenersPass implements CompilerPassInterface
+{
+ /**
+ * Service name of the event dispatcher in processed container.
+ *
+ * @var string
+ */
+ private $dispatcherService;
+
+ /**
+ * Tag name used for listeners.
+ *
+ * @var string
+ */
+ private $listenerTag;
+
+ /**
+ * Tag name used for subscribers.
+ *
+ * @var string
+ */
+ private $subscriberTag;
+
+ /**
+ * Constructor.
+ *
+ * @param string $dispatcherService Service name of the event dispatcher in processed container
+ * @param string $listenerTag Tag name used for listeners
+ * @param string $subscriberTag Tag name used for subscribers
+ */
+ public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber')
+ {
+ $this->dispatcherService = $dispatcherService;
+ $this->listenerTag = $listenerTag;
+ $this->subscriberTag = $subscriberTag;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
+ return;
+ }
+
+ $definition = $container->findDefinition($this->dispatcherService);
+
+ $listeners = array();
+
+ foreach ($container->findTaggedServiceIds($this->listenerTag) as $id => $events) {
+ $def = $container->getDefinition($id);
+ if (!$def->isPublic()) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event listeners are lazy-loaded.', $id));
+ }
+
+ if ($def->isAbstract()) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event listeners are lazy-loaded.', $id));
+ }
+
+ 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 "%s" tags.', $id, $this->listenerTag));
+ }
+
+ if (!isset($event['method'])) {
+ $event['method'] = 'on'.preg_replace_callback(array(
+ '/(?<=\b)[a-z]/i',
+ '/[^a-z0-9]/i',
+ ), function ($matches) { return strtoupper($matches[0]); }, $event['event']);
+ $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
+ }
+
+ $listeners[$event['event']][$priority][] = array('service' => array('id' => $id, 'method' => $event['method']));
+ }
+ }
+
+ foreach ($container->findTaggedServiceIds($this->subscriberTag) as $id => $attributes) {
+ $def = $container->getDefinition($id);
+ if (!$def->isPublic()) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id));
+ }
+
+ if ($def->isAbstract()) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id));
+ }
+
+ // We must assume that the class value has been correctly filled, even if the service is created by a factory
+ $class = $container->getParameterBag()->resolveValue($def->getClass());
+
+ $refClass = new \ReflectionClass($class);
+ $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface';
+ if (!$refClass->implementsInterface($interface)) {
+ throw new \InvalidArgumentException(sprintf('The service "%s" must implement interface "%s".', $id, $interface));
+ }
+
+ // Get all subscribed events.
+ foreach ($class::getSubscribedEvents() as $eventName => $params) {
+ if (is_string($params)) {
+ $priority = 0;
+ $listeners[$eventName][$priority][] = array('service' => array('id' => $id, 'method' => $params));
+ } elseif (is_string($params[0])) {
+ $priority = isset($params[1]) ? $params[1] : 0;
+ $listeners[$eventName][$priority][] = array('service' => array('id' => $id, 'method' => $params[0]));
+ } else {
+ foreach ($params as $listener) {
+ $priority = isset($listener[1]) ? $listener[1] : 0;
+ $listeners[$eventName][$priority][] = array('service' => array('id' => $id, 'method' => $listener[0]));
+ }
+ }
+ }
+ }
+
+ foreach (array_keys($listeners) as $eventName) {
+ krsort($listeners[$eventName]);
+ }
+
+ $definition->addArgument($listeners);
+ }
+}
diff --git a/src/Symfony/Component/EventDispatcher/Tests/CompiledEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/CompiledEventDispatcherTest.php
new file mode 100644
index 0000000000000..c5c89d2279a0d
--- /dev/null
+++ b/src/Symfony/Component/EventDispatcher/Tests/CompiledEventDispatcherTest.php
@@ -0,0 +1,166 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Tests;
+
+use Symfony\Component\DependencyInjection\Container;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\EventDispatcher\CompiledEventDispatcher;
+
+class CompiledEventDispatcherTest extends AbstractEventDispatcherTest
+{
+ protected function createEventDispatcher()
+ {
+ $container = new Container();
+
+ return new CompiledEventDispatcher($container);
+ }
+
+ public function testGetListenersWithCallables()
+ {
+ // When passing in callables exclusively as listeners into the event
+ // dispatcher constructor, the event dispatcher must not attempt to
+ // resolve any services.
+ $container = $this->getMock('Symfony\Component\DependencyInjection\IntrospectableContainerInterface');
+ $container->expects($this->never())->method($this->anything());
+
+ $firstListener = new CallableClass();
+ $secondListener = function () {};
+ $thirdListener = array(new TestEventListener(), 'preFoo');
+ $listeners = array(
+ 'test_event' => array(
+ 0 => array(
+ array('callable' => $firstListener),
+ array('callable' => $secondListener),
+ array('callable' => $thirdListener),
+ ),
+ ),
+ );
+
+ $dispatcher = new CompiledEventDispatcher($container, $listeners);
+ $actualListeners = $dispatcher->getListeners();
+
+ $expectedListeners = array(
+ 'test_event' => array(
+ $firstListener,
+ $secondListener,
+ $thirdListener,
+ ),
+ );
+
+ $this->assertSame($expectedListeners, $actualListeners);
+ }
+
+ public function testDispatchWithCallables()
+ {
+ // When passing in callables exclusively as listeners into the event
+ // dispatcher constructor, the event dispatcher must not attempt to
+ // resolve any services.
+ $container = $this->getMock('Symfony\Component\DependencyInjection\IntrospectableContainerInterface');
+ $container->expects($this->never())->method($this->anything());
+
+ $firstListener = new CallableClass();
+ $secondListener = function () {};
+ $thirdListener = array(new TestEventListener(), 'preFoo');
+ $listeners = array(
+ 'test_event' => array(
+ 0 => array(
+ array('callable' => $firstListener),
+ array('callable' => $secondListener),
+ array('callable' => $thirdListener),
+ ),
+ ),
+ );
+
+ $dispatcher = new CompiledEventDispatcher($container, $listeners);
+ $dispatcher->dispatch('test_event');
+
+ $this->assertTrue($thirdListener[0]->preFooInvoked);
+ }
+
+ public function testGetListenersWithServices()
+ {
+ $container = new ContainerBuilder();
+ $container->register('listener_service', 'Symfony\Component\EventDispatcher\Tests\TestEventListener');
+
+ $listeners = array(
+ 'test_event' => array(
+ 0 => array(
+ array('service' => array('id' => 'listener_service', 'method' => 'preFoo')),
+ ),
+ ),
+ );
+
+ $dispatcher = new CompiledEventDispatcher($container, $listeners);
+ $actualListeners = $dispatcher->getListeners();
+
+ $listenerService = $container->get('listener_service');
+ $expectedListeners = array(
+ 'test_event' => array(
+ array($listenerService, 'preFoo'),
+ ),
+ );
+
+ $this->assertSame($expectedListeners, $actualListeners);
+ }
+
+ public function testDispatchWithServices()
+ {
+ $container = new ContainerBuilder();
+ $container->register('listener_service', 'Symfony\Component\EventDispatcher\Tests\TestEventListener');
+
+ $listeners = array(
+ 'test_event' => array(
+ 0 => array(
+ array('service' => array('id' => 'listener_service', 'method' => 'preFoo')),
+ ),
+ ),
+ );
+
+ $dispatcher = new CompiledEventDispatcher($container, $listeners);
+
+ $dispatcher->dispatch('test_event');
+
+ $listenerService = $container->get('listener_service');
+ $this->assertTrue($listenerService->preFooInvoked);
+ }
+
+ public function testRemoveService()
+ {
+ $container = new ContainerBuilder();
+ $container->register('listener_service', 'Symfony\Component\EventDispatcher\Tests\TestEventListener');
+ $container->register('other_listener_service', 'Symfony\Component\EventDispatcher\Tests\TestEventListener');
+
+ $listeners = array(
+ 'test_event' => array(
+ 0 => array(
+ array('service' => array('id' => 'listener_service', 'method' => 'preFoo')),
+ array('service' => array('id' => 'other_listener_service', 'method' => 'preFoo')),
+ ),
+ ),
+ );
+
+ $dispatcher = new CompiledEventDispatcher($container, $listeners);
+
+ $listenerService = $container->get('listener_service');
+ $dispatcher->removeListener('test_event', array($listenerService, 'preFoo'));
+
+ // Ensure that other service was not initialized during removal of the
+ // listener service.
+ $this->assertFalse($container->initialized('other_listener_service'));
+
+ $dispatcher->dispatch('test_event');
+
+ $this->assertFalse($listenerService->preFooInvoked);
+ $otherService = $container->get('other_listener_service');
+ $this->assertTrue($otherService->preFooInvoked);
+ }
+}
diff --git a/src/Symfony/Component/EventDispatcher/Tests/DependencyInjection/CompiledRegisterListenersPassTest.php b/src/Symfony/Component/EventDispatcher/Tests/DependencyInjection/CompiledRegisterListenersPassTest.php
new file mode 100644
index 0000000000000..d46ffb74b6b97
--- /dev/null
+++ b/src/Symfony/Component/EventDispatcher/Tests/DependencyInjection/CompiledRegisterListenersPassTest.php
@@ -0,0 +1,246 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Tests\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\EventDispatcher\DependencyInjection\CompiledRegisterListenersPass;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+class CompiledRegisterListenersPassTest extends \PHPUnit_Framework_TestCase
+{
+ public function testPassAddsConstructorArgument()
+ {
+ $container = new ContainerBuilder();
+ $definition = $container->register('event_dispatcher', 'stdClass')
+ ->setArguments(array('foo', 'bar'));
+
+ $registerListenersPass = new CompiledRegisterListenersPass();
+ $registerListenersPass->process($container);
+
+ $expected_arguments = array('foo', 'bar', array());
+ $this->assertSame($expected_arguments, $definition->getArguments());
+ }
+
+ public function testPassAddsTaggedListenersAndSubscribers()
+ {
+ $container = new ContainerBuilder();
+ $definition = $container->register('event_dispatcher', 'stdClass');
+
+ $container->register('test_subscriber', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\CompiledSubscriberService')
+ ->addTag('kernel.event_subscriber');
+
+ $container->register('test_listener', 'stdObject')
+ ->addTag('kernel.event_listener', array(
+ 'event' => 'test_event.multiple_listeners',
+ 'method' => 'methodWithMediumPriority',
+ 'priority' => 32,
+ ));
+
+ $registerListenersPass = new CompiledRegisterListenersPass();
+ $registerListenersPass->process($container);
+
+ $expected_listeners = array(
+ 'test_event.multiple_listeners' => array(
+ 128 => array(
+ array(
+ 'service' => array('id' => 'test_subscriber', 'method' => 'methodWithHighestPriority'),
+ ),
+ ),
+ 32 => array(
+ array(
+ 'service' => array('id' => 'test_listener', 'method' => 'methodWithMediumPriority'),
+ ),
+ ),
+ 0 => array(
+ array(
+ 'service' => array('id' => 'test_subscriber', 'method' => 'methodWithoutPriority'),
+ ),
+ ),
+ ),
+ 'test_event.single_listener_with_priority' => array(
+ 64 => array(
+ array(
+ 'service' => array('id' => 'test_subscriber', 'method' => 'methodWithHighPriority'),
+ ),
+ ),
+ ),
+ 'test_event.single_listener_without_priority' => array(
+ 0 => array(
+ array(
+ 'service' => array('id' => 'test_subscriber', 'method' => 'methodWithoutPriority'),
+ ),
+ ),
+ ),
+ );
+ $this->assertSame(array($expected_listeners), $definition->getArguments());
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage The service "foo" must implement interface "Symfony\Component\EventDispatcher\EventSubscriberInterface".
+ */
+ public function testEventSubscriberWithoutInterface()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'stdClass')->addTag('kernel.event_subscriber', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new CompiledRegisterListenersPass();
+ $registerListenersPass->process($container);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage The service "foo" must be public as event listeners are lazy-loaded.
+ */
+ public function testPrivateEventListener()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_listener', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new CompiledRegisterListenersPass();
+ $registerListenersPass->process($container);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage The service "foo" must be public as event subscribers are lazy-loaded.
+ */
+ public function testPrivateEventSubscriber()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'stdClass')->setPublic(false)->addTag('kernel.event_subscriber', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new CompiledRegisterListenersPass();
+ $registerListenersPass->process($container);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage The service "foo" must not be abstract as event listeners are lazy-loaded.
+ */
+ public function testAbstractEventListener()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_listener', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new CompiledRegisterListenersPass();
+ $registerListenersPass->process($container);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage The service "foo" must not be abstract as event subscribers are lazy-loaded.
+ */
+ public function testAbstractEventSubscriber()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_subscriber', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new CompiledRegisterListenersPass();
+ $registerListenersPass->process($container);
+ }
+
+ public function testEventSubscriberResolvableClassName()
+ {
+ $container = new ContainerBuilder();
+
+ $container->setParameter('subscriber.class', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\CompiledSubscriberService');
+ $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new CompiledRegisterListenersPass();
+ $registerListenersPass->process($container);
+
+ $definition = $container->getDefinition('event_dispatcher');
+ $expected_arguments = array(
+ array(
+ 'test_event.multiple_listeners' => array(
+ 128 => array(
+ array(
+ 'service' => array(
+ 'id' => 'foo',
+ 'method' => 'methodWithHighestPriority',
+ ),
+ ),
+ ),
+ 0 => array(
+ array(
+ 'service' => array(
+ 'id' => 'foo',
+ 'method' => 'methodWithoutPriority',
+ ),
+ ),
+ ),
+ ),
+ 'test_event.single_listener_with_priority' => array(
+ 64 => array(
+ array(
+ 'service' => array(
+ 'id' => 'foo',
+ 'method' => 'methodWithHighPriority',
+ ),
+ ),
+ ),
+ ),
+ 'test_event.single_listener_without_priority' => array(
+ 0 => array(
+ array(
+ 'service' => array(
+ 'id' => 'foo',
+ 'method' => 'methodWithoutPriority',
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+ $this->assertSame($expected_arguments, $definition->getArguments());
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage You have requested a non-existent parameter "subscriber.class"
+ */
+ public function testEventSubscriberUnresolvableClassName()
+ {
+ $container = new ContainerBuilder();
+ $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array());
+ $container->register('event_dispatcher', 'stdClass');
+
+ $registerListenersPass = new CompiledRegisterListenersPass();
+ $registerListenersPass->process($container);
+ }
+}
+
+class CompiledSubscriberService implements EventSubscriberInterface
+{
+ public static function getSubscribedEvents()
+ {
+ return array(
+ 'test_event.multiple_listeners' => array(
+ array('methodWithHighestPriority', 128),
+ array('methodWithoutPriority'),
+ ),
+ 'test_event.single_listener_with_priority' => array(
+ array('methodWithHighPriority', 64),
+ ),
+ 'test_event.single_listener_without_priority' => array(
+ array('methodWithoutPriority'),
+ ),
+ );
+ }
+}