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

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\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 $name, Stopwatch $stopwatch, ?EventDispatcherInterface $dispatcher = null, ?int $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 $event, string $eventName, EventDispatcherInterface $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]), $listener[0]::class];
  119. }
  120. return [$listener[0], $listener[0]];
  121. }
  122. }