4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\EventDispatcher\DependencyInjection;
14 use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
15 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
16 use Symfony\Component\DependencyInjection\ContainerBuilder;
17 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
18 use Symfony\Component\DependencyInjection\Reference;
19 use Symfony\Component\EventDispatcher\EventDispatcher;
20 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
23 * Compiler pass to register tagged services for an event dispatcher.
25 class RegisterListenersPass implements CompilerPassInterface
27 protected $dispatcherService;
28 protected $listenerTag;
29 protected $subscriberTag;
31 private $hotPathEvents = array();
32 private $hotPathTagName;
35 * @param string $dispatcherService Service name of the event dispatcher in processed container
36 * @param string $listenerTag Tag name used for listener
37 * @param string $subscriberTag Tag name used for subscribers
39 public function __construct($dispatcherService = 'event_dispatcher', $listenerTag = 'kernel.event_listener', $subscriberTag = 'kernel.event_subscriber')
41 $this->dispatcherService = $dispatcherService;
42 $this->listenerTag = $listenerTag;
43 $this->subscriberTag = $subscriberTag;
46 public function setHotPathEvents(array $hotPathEvents, $tagName = 'container.hot_path')
48 $this->hotPathEvents = array_flip($hotPathEvents);
49 $this->hotPathTagName = $tagName;
54 public function process(ContainerBuilder $container)
56 if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
60 $definition = $container->findDefinition($this->dispatcherService);
62 foreach ($container->findTaggedServiceIds($this->listenerTag, true) as $id => $events) {
63 foreach ($events as $event) {
64 $priority = isset($event['priority']) ? $event['priority'] : 0;
66 if (!isset($event['event'])) {
67 throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
70 if (!isset($event['method'])) {
71 $event['method'] = 'on'.preg_replace_callback(array(
74 ), function ($matches) { return strtoupper($matches[0]); }, $event['event']);
75 $event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
78 $definition->addMethodCall('addListener', array($event['event'], array(new ServiceClosureArgument(new Reference($id)), $event['method']), $priority));
80 if (isset($this->hotPathEvents[$event['event']])) {
81 $container->getDefinition($id)->addTag($this->hotPathTagName);
86 $extractingDispatcher = new ExtractingEventDispatcher();
88 foreach ($container->findTaggedServiceIds($this->subscriberTag, true) as $id => $attributes) {
89 $def = $container->getDefinition($id);
91 // We must assume that the class value has been correctly filled, even if the service is created by a factory
92 $class = $def->getClass();
94 if (!$r = $container->getReflectionClass($class)) {
95 throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
97 if (!$r->isSubclassOf(EventSubscriberInterface::class)) {
98 throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EventSubscriberInterface::class));
102 ExtractingEventDispatcher::$subscriber = $class;
103 $extractingDispatcher->addSubscriber($extractingDispatcher);
104 foreach ($extractingDispatcher->listeners as $args) {
105 $args[1] = array(new ServiceClosureArgument(new Reference($id)), $args[1]);
106 $definition->addMethodCall('addListener', $args);
108 if (isset($this->hotPathEvents[$args[0]])) {
109 $container->getDefinition($id)->addTag('container.hot_path');
112 $extractingDispatcher->listeners = array();
120 class ExtractingEventDispatcher extends EventDispatcher implements EventSubscriberInterface
122 public $listeners = array();
124 public static $subscriber;
126 public function addListener($eventName, $listener, $priority = 0)
128 $this->listeners[] = array($eventName, $listener[1], $priority);
131 public static function getSubscribedEvents()
133 $callback = array(self::$subscriber, 'getSubscribedEvents');