vendor/symfony/doctrine-bridge/ContainerAwareEventManager.php line 143

  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <[email protected]>
  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\Bridge\Doctrine;
  11. use Doctrine\Common\EventArgs;
  12. use Doctrine\Common\EventManager;
  13. use Doctrine\Common\EventSubscriber;
  14. use Psr\Container\ContainerInterface;
  15. /**
  16. * Allows lazy loading of listener and subscriber services.
  17. *
  18. * @author Johannes M. Schmitt <[email protected]>
  19. */
  20. class ContainerAwareEventManager extends EventManager
  21. {
  22. /**
  23. * Map of registered listeners.
  24. *
  25. * <event> => <listeners>
  26. */
  27. private array $listeners = [];
  28. private array $initialized = [];
  29. private bool $initializedSubscribers = false;
  30. private array $initializedHashMapping = [];
  31. private array $methods = [];
  32. private ContainerInterface $container;
  33. /**
  34. * @param list<array{string[], string|object}> $listeners List of [events, listener] tuples
  35. */
  36. public function __construct(ContainerInterface $container, array $listeners = [])
  37. {
  38. $this->container = $container;
  39. $this->listeners = $listeners;
  40. }
  41. public function dispatchEvent($eventName, ?EventArgs $eventArgs = null): void
  42. {
  43. if (!$this->initializedSubscribers) {
  44. $this->initializeSubscribers();
  45. }
  46. if (!isset($this->listeners[$eventName])) {
  47. return;
  48. }
  49. $eventArgs ??= EventArgs::getEmptyInstance();
  50. if (!isset($this->initialized[$eventName])) {
  51. $this->initializeListeners($eventName);
  52. }
  53. foreach ($this->listeners[$eventName] as $hash => $listener) {
  54. $listener->{$this->methods[$eventName][$hash]}($eventArgs);
  55. }
  56. }
  57. public function getListeners($event = null): array
  58. {
  59. if (null === $event) {
  60. trigger_deprecation('symfony/doctrine-bridge', '6.2', 'Calling "%s()" without an event name is deprecated. Call "getAllListeners()" instead.', __METHOD__);
  61. return $this->getAllListeners();
  62. }
  63. if (!$this->initializedSubscribers) {
  64. $this->initializeSubscribers();
  65. }
  66. if (!isset($this->initialized[$event])) {
  67. $this->initializeListeners($event);
  68. }
  69. return $this->listeners[$event];
  70. }
  71. public function getAllListeners(): array
  72. {
  73. if (!$this->initializedSubscribers) {
  74. $this->initializeSubscribers();
  75. }
  76. foreach ($this->listeners as $event => $listeners) {
  77. if (!isset($this->initialized[$event])) {
  78. $this->initializeListeners($event);
  79. }
  80. }
  81. return $this->listeners;
  82. }
  83. public function hasListeners($event): bool
  84. {
  85. if (!$this->initializedSubscribers) {
  86. $this->initializeSubscribers();
  87. }
  88. return isset($this->listeners[$event]) && $this->listeners[$event];
  89. }
  90. public function addEventListener($events, $listener): void
  91. {
  92. if (!$this->initializedSubscribers) {
  93. $this->initializeSubscribers();
  94. }
  95. $hash = $this->getHash($listener);
  96. foreach ((array) $events as $event) {
  97. // Overrides listener if a previous one was associated already
  98. // Prevents duplicate listeners on same event (same instance only)
  99. $this->listeners[$event][$hash] = $listener;
  100. if (\is_string($listener)) {
  101. unset($this->initialized[$event]);
  102. unset($this->initializedHashMapping[$event][$hash]);
  103. } else {
  104. $this->methods[$event][$hash] = $this->getMethod($listener, $event);
  105. }
  106. }
  107. }
  108. public function removeEventListener($events, $listener): void
  109. {
  110. if (!$this->initializedSubscribers) {
  111. $this->initializeSubscribers();
  112. }
  113. $hash = $this->getHash($listener);
  114. foreach ((array) $events as $event) {
  115. if (isset($this->initializedHashMapping[$event][$hash])) {
  116. $hash = $this->initializedHashMapping[$event][$hash];
  117. unset($this->initializedHashMapping[$event][$hash]);
  118. }
  119. // Check if we actually have this listener associated
  120. if (isset($this->listeners[$event][$hash])) {
  121. unset($this->listeners[$event][$hash]);
  122. }
  123. if (isset($this->methods[$event][$hash])) {
  124. unset($this->methods[$event][$hash]);
  125. }
  126. }
  127. }
  128. public function addEventSubscriber(EventSubscriber $subscriber): void
  129. {
  130. if (!$this->initializedSubscribers) {
  131. $this->initializeSubscribers();
  132. }
  133. parent::addEventSubscriber($subscriber);
  134. }
  135. public function removeEventSubscriber(EventSubscriber $subscriber): void
  136. {
  137. if (!$this->initializedSubscribers) {
  138. $this->initializeSubscribers();
  139. }
  140. parent::removeEventSubscriber($subscriber);
  141. }
  142. private function initializeListeners(string $eventName): void
  143. {
  144. $this->initialized[$eventName] = true;
  145. // We'll refill the whole array in order to keep the same order
  146. $listeners = [];
  147. foreach ($this->listeners[$eventName] as $hash => $listener) {
  148. if (\is_string($listener)) {
  149. $listener = $this->container->get($listener);
  150. $newHash = $this->getHash($listener);
  151. $this->initializedHashMapping[$eventName][$hash] = $newHash;
  152. $listeners[$newHash] = $listener;
  153. $this->methods[$eventName][$newHash] = $this->getMethod($listener, $eventName);
  154. } else {
  155. $listeners[$hash] = $listener;
  156. }
  157. }
  158. $this->listeners[$eventName] = $listeners;
  159. }
  160. private function initializeSubscribers(): void
  161. {
  162. $this->initializedSubscribers = true;
  163. $listeners = $this->listeners;
  164. $this->listeners = [];
  165. foreach ($listeners as $listener) {
  166. if (\is_array($listener)) {
  167. $this->addEventListener(...$listener);
  168. continue;
  169. }
  170. if (\is_string($listener)) {
  171. $listener = $this->container->get($listener);
  172. }
  173. // throw new \InvalidArgumentException(sprintf('Using Doctrine subscriber "%s" is not allowed. Register it as a listener instead, using e.g. the #[AsDoctrineListener] or #[AsDocumentListener] attribute.', \is_object($listener) ? $listener::class : $listener));
  174. trigger_deprecation('symfony/doctrine-bridge', '6.3', 'Registering "%s" as a Doctrine subscriber is deprecated. Register it as a listener instead, using e.g. the #[AsDoctrineListener] or #[AsDocumentListener] attribute.', \is_object($listener) ? get_debug_type($listener) : $listener);
  175. parent::addEventSubscriber($listener);
  176. }
  177. }
  178. private function getHash(string|object $listener): string
  179. {
  180. if (\is_string($listener)) {
  181. return '_service_'.$listener;
  182. }
  183. return spl_object_hash($listener);
  184. }
  185. private function getMethod(object $listener, string $event): string
  186. {
  187. if (!method_exists($listener, $event) && method_exists($listener, '__invoke')) {
  188. return '__invoke';
  189. }
  190. return $event;
  191. }
  192. }