vendor/symfony/event-dispatcher/Debug/WrappedListener.php line 94

  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\Debug;
  11. use Psr\EventDispatcher\StoppableEventInterface;
  12. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  13. use Symfony\Component\Stopwatch\Stopwatch;
  14. use Symfony\Component\VarDumper\Caster\ClassStub;
  15. /**
  16.  * @author Fabien Potencier <fabien@symfony.com>
  17.  */
  18. final class WrappedListener
  19. {
  20.     private string|array|object $listener;
  21.     private ?\Closure $optimizedListener;
  22.     private string $name;
  23.     private bool $called false;
  24.     private bool $stoppedPropagation false;
  25.     private Stopwatch $stopwatch;
  26.     private ?EventDispatcherInterface $dispatcher;
  27.     private string $pretty;
  28.     private string $callableRef;
  29.     private ClassStub|string $stub;
  30.     private ?int $priority null;
  31.     private static bool $hasClassStub;
  32.     public function __construct(callable|array $listener, ?string $nameStopwatch $stopwatchEventDispatcherInterface $dispatcher nullint $priority null)
  33.     {
  34.         $this->listener $listener;
  35.         $this->optimizedListener $listener instanceof \Closure $listener : (\is_callable($listener) ? $listener(...) : null);
  36.         $this->stopwatch $stopwatch;
  37.         $this->dispatcher $dispatcher;
  38.         $this->priority $priority;
  39.         if (\is_array($listener)) {
  40.             [$this->name$this->callableRef] = $this->parseListener($listener);
  41.             $this->pretty $this->name.'::'.$listener[1];
  42.             $this->callableRef .= '::'.$listener[1];
  43.         } elseif ($listener instanceof \Closure) {
  44.             $r = new \ReflectionFunction($listener);
  45.             if (str_contains($r->name'{closure}')) {
  46.                 $this->pretty $this->name 'closure';
  47.             } elseif ($class \PHP_VERSION_ID >= 80111 $r->getClosureCalledClass() : $r->getClosureScopeClass()) {
  48.                 $this->name $class->name;
  49.                 $this->pretty $this->name.'::'.$r->name;
  50.             } else {
  51.                 $this->pretty $this->name $r->name;
  52.             }
  53.         } elseif (\is_string($listener)) {
  54.             $this->pretty $this->name $listener;
  55.         } else {
  56.             $this->name get_debug_type($listener);
  57.             $this->pretty $this->name.'::__invoke';
  58.             $this->callableRef $listener::class.'::__invoke';
  59.         }
  60.         if (null !== $name) {
  61.             $this->name $name;
  62.         }
  63.         self::$hasClassStub ??= class_exists(ClassStub::class);
  64.     }
  65.     public function getWrappedListener(): callable|array
  66.     {
  67.         return $this->listener;
  68.     }
  69.     public function wasCalled(): bool
  70.     {
  71.         return $this->called;
  72.     }
  73.     public function stoppedPropagation(): bool
  74.     {
  75.         return $this->stoppedPropagation;
  76.     }
  77.     public function getPretty(): string
  78.     {
  79.         return $this->pretty;
  80.     }
  81.     public function getInfo(string $eventName): array
  82.     {
  83.         $this->stub ??= self::$hasClassStub ? new ClassStub($this->pretty.'()'$this->callableRef ?? $this->listener) : $this->pretty.'()';
  84.         return [
  85.             'event' => $eventName,
  86.             'priority' => $this->priority ??= $this->dispatcher?->getListenerPriority($eventName$this->listener),
  87.             'pretty' => $this->pretty,
  88.             'stub' => $this->stub,
  89.         ];
  90.     }
  91.     public function __invoke(object $eventstring $eventNameEventDispatcherInterface $dispatcher): void
  92.     {
  93.         $dispatcher $this->dispatcher ?: $dispatcher;
  94.         $this->called true;
  95.         $this->priority ??= $dispatcher->getListenerPriority($eventName$this->listener);
  96.         $e $this->stopwatch->start($this->name'event_listener');
  97.         try {
  98.             ($this->optimizedListener ?? $this->listener)($event$eventName$dispatcher);
  99.         } finally {
  100.             if ($e->isStarted()) {
  101.                 $e->stop();
  102.             }
  103.         }
  104.         if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
  105.             $this->stoppedPropagation true;
  106.         }
  107.     }
  108.     private function parseListener(array $listener): array
  109.     {
  110.         if ($listener[0] instanceof \Closure) {
  111.             foreach ((new \ReflectionFunction($listener[0]))->getAttributes(\Closure::class) as $attribute) {
  112.                 if ($name $attribute->getArguments()['name'] ?? false) {
  113.                     return [$name$attribute->getArguments()['class'] ?? $name];
  114.                 }
  115.             }
  116.         }
  117.         if (\is_object($listener[0])) {
  118.             return [get_debug_type($listener[0]), \get_class($listener[0])];
  119.         }
  120.         return [$listener[0], $listener[0]];
  121.     }
  122. }