vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php line 41

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\EventDispatcher\DependencyInjection;
  11. use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
  12. use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
  13. use Symfony\Component\DependencyInjection\ContainerBuilder;
  14. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  15. use Symfony\Component\DependencyInjection\Reference;
  16. use Symfony\Component\EventDispatcher\EventDispatcher;
  17. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  18. use Symfony\Contracts\EventDispatcher\Event;
  19. /**
  20.  * Compiler pass to register tagged services for an event dispatcher.
  21.  */
  22. class RegisterListenersPass implements CompilerPassInterface
  23. {
  24.     protected $dispatcherService;
  25.     protected $listenerTag;
  26.     protected $subscriberTag;
  27.     protected $eventAliasesParameter;
  28.     private $hotPathEvents = [];
  29.     private $hotPathTagName 'container.hot_path';
  30.     private $noPreloadEvents = [];
  31.     private $noPreloadTagName 'container.no_preload';
  32.     public function __construct(string $dispatcherService 'event_dispatcher'string $listenerTag 'kernel.event_listener'string $subscriberTag 'kernel.event_subscriber'string $eventAliasesParameter 'event_dispatcher.event_aliases')
  33.     {
  34.         if (< \func_num_args()) {
  35.             trigger_deprecation('symfony/event-dispatcher''5.3''Configuring "%s" is deprecated.'__CLASS__);
  36.         }
  37.         $this->dispatcherService $dispatcherService;
  38.         $this->listenerTag $listenerTag;
  39.         $this->subscriberTag $subscriberTag;
  40.         $this->eventAliasesParameter $eventAliasesParameter;
  41.     }
  42.     /**
  43.      * @return $this
  44.      */
  45.     public function setHotPathEvents(array $hotPathEvents)
  46.     {
  47.         $this->hotPathEvents array_flip($hotPathEvents);
  48.         if (< \func_num_args()) {
  49.             trigger_deprecation('symfony/event-dispatcher''5.4''Configuring "$tagName" in "%s" is deprecated.'__METHOD__);
  50.             $this->hotPathTagName func_get_arg(1);
  51.         }
  52.         return $this;
  53.     }
  54.     /**
  55.      * @return $this
  56.      */
  57.     public function setNoPreloadEvents(array $noPreloadEvents): self
  58.     {
  59.         $this->noPreloadEvents array_flip($noPreloadEvents);
  60.         if (< \func_num_args()) {
  61.             trigger_deprecation('symfony/event-dispatcher''5.4''Configuring "$tagName" in "%s" is deprecated.'__METHOD__);
  62.             $this->noPreloadTagName func_get_arg(1);
  63.         }
  64.         return $this;
  65.     }
  66.     public function process(ContainerBuilder $container)
  67.     {
  68.         if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
  69.             return;
  70.         }
  71.         $aliases = [];
  72.         if ($container->hasParameter($this->eventAliasesParameter)) {
  73.             $aliases $container->getParameter($this->eventAliasesParameter);
  74.         }
  75.         $globalDispatcherDefinition $container->findDefinition($this->dispatcherService);
  76.         foreach ($container->findTaggedServiceIds($this->listenerTagtrue) as $id => $events) {
  77.             $noPreload 0;
  78.             foreach ($events as $event) {
  79.                 $priority $event['priority'] ?? 0;
  80.                 if (!isset($event['event'])) {
  81.                     if ($container->getDefinition($id)->hasTag($this->subscriberTag)) {
  82.                         continue;
  83.                     }
  84.                     $event['method'] = $event['method'] ?? '__invoke';
  85.                     $event['event'] = $this->getEventFromTypeDeclaration($container$id$event['method']);
  86.                 }
  87.                 $event['event'] = $aliases[$event['event']] ?? $event['event'];
  88.                 if (!isset($event['method'])) {
  89.                     $event['method'] = 'on'.preg_replace_callback([
  90.                         '/(?<=\b|_)[a-z]/i',
  91.                         '/[^a-z0-9]/i',
  92.                     ], function ($matches) { return strtoupper($matches[0]); }, $event['event']);
  93.                     $event['method'] = preg_replace('/[^a-z0-9]/i'''$event['method']);
  94.                     if (null !== ($class $container->getDefinition($id)->getClass()) && ($r $container->getReflectionClass($classfalse)) && !$r->hasMethod($event['method']) && $r->hasMethod('__invoke')) {
  95.                         $event['method'] = '__invoke';
  96.                     }
  97.                 }
  98.                 $dispatcherDefinition $globalDispatcherDefinition;
  99.                 if (isset($event['dispatcher'])) {
  100.                     $dispatcherDefinition $container->getDefinition($event['dispatcher']);
  101.                 }
  102.                 $dispatcherDefinition->addMethodCall('addListener', [$event['event'], [new ServiceClosureArgument(new Reference($id)), $event['method']], $priority]);
  103.                 if (isset($this->hotPathEvents[$event['event']])) {
  104.                     $container->getDefinition($id)->addTag($this->hotPathTagName);
  105.                 } elseif (isset($this->noPreloadEvents[$event['event']])) {
  106.                     ++$noPreload;
  107.                 }
  108.             }
  109.             if ($noPreload && \count($events) === $noPreload) {
  110.                 $container->getDefinition($id)->addTag($this->noPreloadTagName);
  111.             }
  112.         }
  113.         $extractingDispatcher = new ExtractingEventDispatcher();
  114.         foreach ($container->findTaggedServiceIds($this->subscriberTagtrue) as $id => $tags) {
  115.             $def $container->getDefinition($id);
  116.             // We must assume that the class value has been correctly filled, even if the service is created by a factory
  117.             $class $def->getClass();
  118.             if (!$r $container->getReflectionClass($class)) {
  119.                 throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.'$class$id));
  120.             }
  121.             if (!$r->isSubclassOf(EventSubscriberInterface::class)) {
  122.                 throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".'$idEventSubscriberInterface::class));
  123.             }
  124.             $class $r->name;
  125.             $dispatcherDefinitions = [];
  126.             foreach ($tags as $attributes) {
  127.                 if (!isset($attributes['dispatcher']) || isset($dispatcherDefinitions[$attributes['dispatcher']])) {
  128.                     continue;
  129.                 }
  130.                 $dispatcherDefinitions[$attributes['dispatcher']] = $container->getDefinition($attributes['dispatcher']);
  131.             }
  132.             if (!$dispatcherDefinitions) {
  133.                 $dispatcherDefinitions = [$globalDispatcherDefinition];
  134.             }
  135.             $noPreload 0;
  136.             ExtractingEventDispatcher::$aliases $aliases;
  137.             ExtractingEventDispatcher::$subscriber $class;
  138.             $extractingDispatcher->addSubscriber($extractingDispatcher);
  139.             foreach ($extractingDispatcher->listeners as $args) {
  140.                 $args[1] = [new ServiceClosureArgument(new Reference($id)), $args[1]];
  141.                 foreach ($dispatcherDefinitions as $dispatcherDefinition) {
  142.                     $dispatcherDefinition->addMethodCall('addListener'$args);
  143.                 }
  144.                 if (isset($this->hotPathEvents[$args[0]])) {
  145.                     $container->getDefinition($id)->addTag($this->hotPathTagName);
  146.                 } elseif (isset($this->noPreloadEvents[$args[0]])) {
  147.                     ++$noPreload;
  148.                 }
  149.             }
  150.             if ($noPreload && \count($extractingDispatcher->listeners) === $noPreload) {
  151.                 $container->getDefinition($id)->addTag($this->noPreloadTagName);
  152.             }
  153.             $extractingDispatcher->listeners = [];
  154.             ExtractingEventDispatcher::$aliases = [];
  155.         }
  156.     }
  157.     private function getEventFromTypeDeclaration(ContainerBuilder $containerstring $idstring $method): string
  158.     {
  159.         if (
  160.             null === ($class $container->getDefinition($id)->getClass())
  161.             || !($r $container->getReflectionClass($classfalse))
  162.             || !$r->hasMethod($method)
  163.             || > ($m $r->getMethod($method))->getNumberOfParameters()
  164.             || !($type $m->getParameters()[0]->getType()) instanceof \ReflectionNamedType
  165.             || $type->isBuiltin()
  166.             || Event::class === ($name $type->getName())
  167.         ) {
  168.             throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.'$id$this->listenerTag));
  169.         }
  170.         return $name;
  171.     }
  172. }
  173. /**
  174.  * @internal
  175.  */
  176. class ExtractingEventDispatcher extends EventDispatcher implements EventSubscriberInterface
  177. {
  178.     public $listeners = [];
  179.     public static $aliases = [];
  180.     public static $subscriber;
  181.     public function addListener(string $eventName$listenerint $priority 0)
  182.     {
  183.         $this->listeners[] = [$eventName$listener[1], $priority];
  184.     }
  185.     public static function getSubscribedEvents(): array
  186.     {
  187.         $events = [];
  188.         foreach ([self::$subscriber'getSubscribedEvents']() as $eventName => $params) {
  189.             $events[self::$aliases[$eventName] ?? $eventName] = $params;
  190.         }
  191.         return $events;
  192.     }
  193. }