diff --git a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php index 67ba6abf3d740..8415ee14b63cb 100644 --- a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php +++ b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php @@ -16,7 +16,7 @@ use Psr\Container\ContainerInterface; /** - * Allows lazy loading of listener services. + * Allows lazy loading of listener and subscriber services. * * @author Johannes M. Schmitt */ @@ -28,13 +28,16 @@ class ContainerAwareEventManager extends EventManager * => */ private $listeners = []; + private $subscribers; private $initialized = []; + private $initializedSubscribers = false; private $methods = []; private $container; - public function __construct(ContainerInterface $container) + public function __construct(ContainerInterface $container, array $subscriberIds = []) { $this->container = $container; + $this->subscribers = $subscriberIds; } /** @@ -44,6 +47,9 @@ public function __construct(ContainerInterface $container) */ public function dispatchEvent($eventName, EventArgs $eventArgs = null) { + if (!$this->initializedSubscribers) { + $this->initializeSubscribers(); + } if (!isset($this->listeners[$eventName])) { return; } @@ -66,6 +72,9 @@ public function dispatchEvent($eventName, EventArgs $eventArgs = null) */ public function getListeners($event = null) { + if (!$this->initializedSubscribers) { + $this->initializeSubscribers(); + } if (null !== $event) { if (!isset($this->initialized[$event])) { $this->initializeListeners($event); @@ -90,6 +99,10 @@ public function getListeners($event = null) */ public function hasListeners($event) { + if (!$this->initializedSubscribers) { + $this->initializeSubscribers(); + } + return isset($this->listeners[$event]) && $this->listeners[$event]; } @@ -138,6 +151,7 @@ public function removeEventListener($events, $listener) private function initializeListeners(string $eventName) { + $this->initialized[$eventName] = true; foreach ($this->listeners[$eventName] as $hash => $listener) { if (\is_string($listener)) { $this->listeners[$eventName][$hash] = $listener = $this->container->get($listener); @@ -145,7 +159,16 @@ private function initializeListeners(string $eventName) $this->methods[$eventName][$hash] = $this->getMethod($listener, $eventName); } } - $this->initialized[$eventName] = true; + } + + private function initializeSubscribers() + { + $this->initializedSubscribers = true; + foreach ($this->subscribers as $id => $subscriber) { + if (\is_string($subscriber)) { + parent::addEventSubscriber($this->subscribers[$id] = $this->container->get($subscriber)); + } + } } /** diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php index 4f9137bbe59f3..3c220b707542e 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php @@ -11,6 +11,8 @@ namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass; +use Symfony\Bridge\Doctrine\ContainerAwareEventManager; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -55,15 +57,24 @@ public function process(ContainerBuilder $container) } $this->connections = $container->getParameter($this->connections); - $this->addTaggedSubscribers($container); - $this->addTaggedListeners($container); + $listenerRefs = []; + $this->addTaggedSubscribers($container, $listenerRefs); + $this->addTaggedListeners($container, $listenerRefs); + + // replace service container argument of event managers with smaller service locator + // so services can even remain private + foreach ($listenerRefs as $connection => $refs) { + $this->getEventManagerDef($container, $connection) + ->replaceArgument(0, ServiceLocatorTagPass::register($container, $refs)); + } } - private function addTaggedSubscribers(ContainerBuilder $container) + private function addTaggedSubscribers(ContainerBuilder $container, array &$listenerRefs) { $subscriberTag = $this->tagPrefix.'.event_subscriber'; $taggedSubscribers = $this->findAndSortTags($subscriberTag, $container); + $managerDefs = []; foreach ($taggedSubscribers as $taggedSubscriber) { [$id, $tag] = $taggedSubscriber; $connections = isset($tag['connection']) ? [$tag['connection']] : array_keys($this->connections); @@ -72,16 +83,33 @@ private function addTaggedSubscribers(ContainerBuilder $container) throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: "%s".', $con, $id, implode('", "', array_keys($this->connections)))); } - $this->getEventManagerDef($container, $con)->addMethodCall('addEventSubscriber', [new Reference($id)]); + if (!isset($managerDefs[$con])) { + $managerDef = $parentDef = $this->getEventManagerDef($container, $con); + while ($parentDef instanceof ChildDefinition) { + $parentDef = $container->findDefinition($parentDef->getParent()); + } + $managerClass = $container->getParameterBag()->resolveValue($parentDef->getClass()); + $managerDefs[$con] = [$managerDef, $managerClass]; + } else { + [$managerDef, $managerClass] = $managerDefs[$con]; + } + + if (ContainerAwareEventManager::class === $managerClass) { + $listenerRefs[$con][$id] = new Reference($id); + $refs = $managerDef->getArguments()[1] ?? []; + $refs[] = $id; + $managerDef->setArgument(1, $refs); + } else { + $managerDef->addMethodCall('addEventSubscriber', [new Reference($id)]); + } } } } - private function addTaggedListeners(ContainerBuilder $container) + private function addTaggedListeners(ContainerBuilder $container, array &$listenerRefs) { $listenerTag = $this->tagPrefix.'.event_listener'; $taggedListeners = $this->findAndSortTags($listenerTag, $container); - $listenerRefs = []; foreach ($taggedListeners as $taggedListener) { [$id, $tag] = $taggedListener; @@ -100,13 +128,6 @@ private function addTaggedListeners(ContainerBuilder $container) $this->getEventManagerDef($container, $con)->addMethodCall('addEventListener', [[$tag['event']], $id]); } } - - // replace service container argument of event managers with smaller service locator - // so services can even remain private - foreach ($listenerRefs as $connection => $refs) { - $this->getEventManagerDef($container, $connection) - ->replaceArgument(0, ServiceLocatorTagPass::register($container, $refs)); - } } private function getEventManagerDef(ContainerBuilder $container, string $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php index 80eefd200a280..01baa46dc93dc 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\Doctrine\Tests; +use Doctrine\Common\EventSubscriber; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\ContainerAwareEventManager; use Symfony\Component\DependencyInjection\Container; @@ -28,6 +29,8 @@ protected function setUp(): void public function testDispatchEvent() { + $this->evm = new ContainerAwareEventManager($this->container, ['lazy4']); + $this->container->set('lazy1', $listener1 = new MyListener()); $this->evm->addEventListener('foo', 'lazy1'); $this->evm->addEventListener('foo', $listener2 = new MyListener()); @@ -37,10 +40,18 @@ public function testDispatchEvent() $this->container->set('lazy3', $listener5 = new MyListener()); $this->evm->addEventListener('foo', $listener5 = new MyListener()); $this->evm->addEventListener('bar', $listener5); + $this->container->set('lazy4', $subscriber1 = new MySubscriber(['foo'])); + $this->evm->addEventSubscriber($subscriber2 = new MySubscriber(['bar'])); + + $this->assertSame(0, $subscriber1->calledSubscribedEventsCount); + $this->assertSame(1, $subscriber2->calledSubscribedEventsCount); $this->evm->dispatchEvent('foo'); $this->evm->dispatchEvent('bar'); + $this->assertSame(1, $subscriber1->calledSubscribedEventsCount); + $this->assertSame(1, $subscriber2->calledSubscribedEventsCount); + $this->assertSame(0, $listener1->calledByInvokeCount); $this->assertSame(1, $listener1->calledByEventNameCount); $this->assertSame(0, $listener2->calledByInvokeCount); @@ -51,10 +62,16 @@ public function testDispatchEvent() $this->assertSame(0, $listener4->calledByEventNameCount); $this->assertSame(1, $listener5->calledByInvokeCount); $this->assertSame(1, $listener5->calledByEventNameCount); + $this->assertSame(0, $subscriber1->calledByInvokeCount); + $this->assertSame(1, $subscriber1->calledByEventNameCount); + $this->assertSame(1, $subscriber2->calledByInvokeCount); + $this->assertSame(0, $subscriber2->calledByEventNameCount); } - public function testAddEventListenerAfterDispatchEvent() + public function testAddEventListenerAndSubscriberAfterDispatchEvent() { + $this->evm = new ContainerAwareEventManager($this->container, ['lazy7']); + $this->container->set('lazy1', $listener1 = new MyListener()); $this->evm->addEventListener('foo', 'lazy1'); $this->evm->addEventListener('foo', $listener2 = new MyListener()); @@ -64,10 +81,18 @@ public function testAddEventListenerAfterDispatchEvent() $this->container->set('lazy3', $listener5 = new MyListener()); $this->evm->addEventListener('foo', $listener5 = new MyListener()); $this->evm->addEventListener('bar', $listener5); + $this->container->set('lazy7', $subscriber1 = new MySubscriber(['foo'])); + $this->evm->addEventSubscriber($subscriber2 = new MySubscriber(['bar'])); + + $this->assertSame(0, $subscriber1->calledSubscribedEventsCount); + $this->assertSame(1, $subscriber2->calledSubscribedEventsCount); $this->evm->dispatchEvent('foo'); $this->evm->dispatchEvent('bar'); + $this->assertSame(1, $subscriber1->calledSubscribedEventsCount); + $this->assertSame(1, $subscriber2->calledSubscribedEventsCount); + $this->container->set('lazy4', $listener6 = new MyListener()); $this->evm->addEventListener('foo', 'lazy4'); $this->evm->addEventListener('foo', $listener7 = new MyListener()); @@ -77,10 +102,19 @@ public function testAddEventListenerAfterDispatchEvent() $this->container->set('lazy6', $listener10 = new MyListener()); $this->evm->addEventListener('foo', $listener10 = new MyListener()); $this->evm->addEventListener('bar', $listener10); + $this->evm->addEventSubscriber($subscriber3 = new MySubscriber(['bar'])); + + $this->assertSame(1, $subscriber1->calledSubscribedEventsCount); + $this->assertSame(1, $subscriber2->calledSubscribedEventsCount); + $this->assertSame(1, $subscriber3->calledSubscribedEventsCount); $this->evm->dispatchEvent('foo'); $this->evm->dispatchEvent('bar'); + $this->assertSame(1, $subscriber1->calledSubscribedEventsCount); + $this->assertSame(1, $subscriber2->calledSubscribedEventsCount); + $this->assertSame(1, $subscriber3->calledSubscribedEventsCount); + $this->assertSame(0, $listener1->calledByInvokeCount); $this->assertSame(2, $listener1->calledByEventNameCount); $this->assertSame(0, $listener2->calledByInvokeCount); @@ -91,6 +125,10 @@ public function testAddEventListenerAfterDispatchEvent() $this->assertSame(0, $listener4->calledByEventNameCount); $this->assertSame(2, $listener5->calledByInvokeCount); $this->assertSame(2, $listener5->calledByEventNameCount); + $this->assertSame(0, $subscriber1->calledByInvokeCount); + $this->assertSame(2, $subscriber1->calledByEventNameCount); + $this->assertSame(2, $subscriber2->calledByInvokeCount); + $this->assertSame(0, $subscriber2->calledByEventNameCount); $this->assertSame(0, $listener6->calledByInvokeCount); $this->assertSame(1, $listener6->calledByEventNameCount); @@ -102,6 +140,8 @@ public function testAddEventListenerAfterDispatchEvent() $this->assertSame(0, $listener9->calledByEventNameCount); $this->assertSame(1, $listener10->calledByInvokeCount); $this->assertSame(1, $listener10->calledByEventNameCount); + $this->assertSame(1, $subscriber3->calledByInvokeCount); + $this->assertSame(0, $subscriber3->calledByEventNameCount); } public function testGetListenersForEvent() @@ -166,3 +206,21 @@ public function foo() ++$this->calledByEventNameCount; } } + +class MySubscriber extends MyListener implements EventSubscriber +{ + public $calledSubscribedEventsCount = 0; + private $listenedEvents; + + public function __construct(array $listenedEvents) + { + $this->listenedEvents = $listenedEvents; + } + + public function getSubscribedEvents(): array + { + ++$this->calledSubscribedEventsCount; + + return $this->listenedEvents; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php index d5f1755ff6ffc..28b983324e55d 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPassTest.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Doctrine\Tests\DependencyInjection\CompilerPass; use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Doctrine\ContainerAwareEventManager; use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -209,20 +210,46 @@ public function testProcessEventSubscribersWithMultipleConnections() $this->process($container); + $eventManagerDef = $container->getDefinition('doctrine.dbal.default_connection.event_manager'); + + // first connection + $this->assertEquals( + [ + 'a', + 'b', + ], + $eventManagerDef->getArgument(1) + ); + + $serviceLocatorDef = $container->getDefinition((string) $eventManagerDef->getArgument(0)); + $this->assertSame(ServiceLocator::class, $serviceLocatorDef->getClass()); + $this->assertEquals( + [ + 'a' => new ServiceClosureArgument(new Reference('a')), + 'b' => new ServiceClosureArgument(new Reference('b')), + ], + $serviceLocatorDef->getArgument(0) + ); + + $eventManagerDef = $container->getDefinition('doctrine.dbal.second_connection.event_manager'); + + // second connection $this->assertEquals( [ - ['addEventSubscriber', [new Reference('a')]], - ['addEventSubscriber', [new Reference('b')]], + 'a', + 'c', ], - $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls() + $eventManagerDef->getArgument(1) ); + $serviceLocatorDef = $container->getDefinition((string) $eventManagerDef->getArgument(0)); + $this->assertSame(ServiceLocator::class, $serviceLocatorDef->getClass()); $this->assertEquals( [ - ['addEventSubscriber', [new Reference('a')]], - ['addEventSubscriber', [new Reference('c')]], + 'a' => new ServiceClosureArgument(new Reference('a')), + 'c' => new ServiceClosureArgument(new Reference('c')), ], - $container->getDefinition('doctrine.dbal.second_connection.event_manager')->getMethodCalls() + $serviceLocatorDef->getArgument(0) ); } @@ -261,15 +288,30 @@ public function testProcessEventSubscribersWithPriorities() $this->process($container); + $eventManagerDef = $container->getDefinition('doctrine.dbal.default_connection.event_manager'); + $this->assertEquals( [ - ['addEventSubscriber', [new Reference('c')]], - ['addEventSubscriber', [new Reference('d')]], - ['addEventSubscriber', [new Reference('e')]], - ['addEventSubscriber', [new Reference('b')]], - ['addEventSubscriber', [new Reference('a')]], + 'c', + 'd', + 'e', + 'b', + 'a', ], - $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls() + $eventManagerDef->getArgument(1) + ); + + $serviceLocatorDef = $container->getDefinition((string) $eventManagerDef->getArgument(0)); + $this->assertSame(ServiceLocator::class, $serviceLocatorDef->getClass()); + $this->assertEquals( + [ + 'a' => new ServiceClosureArgument(new Reference('a')), + 'b' => new ServiceClosureArgument(new Reference('b')), + 'c' => new ServiceClosureArgument(new Reference('c')), + 'd' => new ServiceClosureArgument(new Reference('d')), + 'e' => new ServiceClosureArgument(new Reference('e')), + ], + $serviceLocatorDef->getArgument(0) ); } @@ -296,12 +338,12 @@ private function createBuilder($multipleConnections = false) $connections = ['default' => 'doctrine.dbal.default_connection']; - $container->register('doctrine.dbal.default_connection.event_manager', 'stdClass') + $container->register('doctrine.dbal.default_connection.event_manager', ContainerAwareEventManager::class) ->addArgument(new Reference('service_container')); $container->register('doctrine.dbal.default_connection', 'stdClass'); if ($multipleConnections) { - $container->register('doctrine.dbal.second_connection.event_manager', 'stdClass') + $container->register('doctrine.dbal.second_connection.event_manager', ContainerAwareEventManager::class) ->addArgument(new Reference('service_container')); $container->register('doctrine.dbal.second_connection', 'stdClass'); $connections['second'] = 'doctrine.dbal.second_connection'; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy