[DoctrineBridge] always load event listeners lazy via ServiceLocator

This commit is contained in:
David Maicher 2018-06-21 19:43:49 +02:00 committed by Fabien Potencier
parent 6064cfe016
commit 130ec0525d
3 changed files with 71 additions and 27 deletions

View File

@ -27,3 +27,9 @@ SecurityBundle
`security.authentication.trust_resolver.rememberme_class` parameters to define `security.authentication.trust_resolver.rememberme_class` parameters to define
the token classes is deprecated. To use the token classes is deprecated. To use
custom tokens extend the existing AnonymousToken and RememberMeToken. custom tokens extend the existing AnonymousToken and RememberMeToken.
DoctrineBridge
--------------
* The `lazy` attribute on `doctrine.event_listener` tags was removed.
Listeners are now lazy by default. So any `lazy` attributes can safely be removed from those tags.

View File

@ -12,6 +12,7 @@
namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass; namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Exception\RuntimeException;
@ -80,10 +81,10 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
{ {
$listenerTag = $this->tagPrefix.'.event_listener'; $listenerTag = $this->tagPrefix.'.event_listener';
$taggedListeners = $this->findAndSortTags($listenerTag, $container); $taggedListeners = $this->findAndSortTags($listenerTag, $container);
$listenerRefs = array();
foreach ($taggedListeners as $taggedListener) { foreach ($taggedListeners as $taggedListener) {
list($id, $tag) = $taggedListener; list($id, $tag) = $taggedListener;
$taggedListenerDef = $container->getDefinition($id);
if (!isset($tag['event'])) { if (!isset($tag['event'])) {
throw new InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id)); throw new InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id));
} }
@ -93,15 +94,19 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
if (!isset($this->connections[$con])) { if (!isset($this->connections[$con])) {
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)))); 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))));
} }
$listenerRefs[$con][$id] = new Reference($id);
if ($lazy = !empty($tag['lazy'])) {
$taggedListenerDef->setPublic(true);
}
// we add one call per event per service so we have the correct order // we add one call per event per service so we have the correct order
$this->getEventManagerDef($container, $con)->addMethodCall('addEventListener', array(array($tag['event']), $lazy ? $id : new Reference($id))); $this->getEventManagerDef($container, $con)->addMethodCall('addEventListener', array(array($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, $name) private function getEventManagerDef(ContainerBuilder $container, $name)

View File

@ -13,9 +13,11 @@ namespace Symfony\Bridge\Doctrine\Tests\DependencyInjection\CompilerPass;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass; use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass;
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ServiceLocator;
class RegisterEventListenersAndSubscribersPassTest extends TestCase class RegisterEventListenersAndSubscribersPassTest extends TestCase
{ {
@ -68,7 +70,6 @@ class RegisterEventListenersAndSubscribersPassTest extends TestCase
->addTag('doctrine.event_listener', array( ->addTag('doctrine.event_listener', array(
'event' => 'foo_bar', 'event' => 'foo_bar',
'priority' => 3, 'priority' => 3,
'lazy' => true,
)) ))
; ;
$container $container
@ -86,25 +87,30 @@ class RegisterEventListenersAndSubscribersPassTest extends TestCase
; ;
$this->process($container); $this->process($container);
$methodCalls = $container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls(); $eventManagerDef = $container->getDefinition('doctrine.dbal.default_connection.event_manager');
$methodCalls = $eventManagerDef->getMethodCalls();
$this->assertEquals( $this->assertEquals(
array( array(
array('addEventListener', array(array('foo_bar'), new Reference('c'))), array('addEventListener', array(array('foo_bar'), 'c')),
array('addEventListener', array(array('foo_bar'), new Reference('a'))), array('addEventListener', array(array('foo_bar'), 'a')),
array('addEventListener', array(array('bar'), new Reference('a'))), array('addEventListener', array(array('bar'), 'a')),
array('addEventListener', array(array('foo'), new Reference('b'))), array('addEventListener', array(array('foo'), 'b')),
array('addEventListener', array(array('foo'), new Reference('a'))), array('addEventListener', array(array('foo'), 'a')),
), ),
$methodCalls $methodCalls
); );
// not lazy so must be reference $serviceLocatorDef = $container->getDefinition((string) $eventManagerDef->getArgument(0));
$this->assertInstanceOf('Symfony\Component\DependencyInjection\Reference', $methodCalls[0][1][1]); $this->assertSame(ServiceLocator::class, $serviceLocatorDef->getClass());
$this->assertEquals(
// lazy so id instead of reference and must mark service public array(
$this->assertSame('a', $methodCalls[1][1][1]); 'c' => new ServiceClosureArgument(new Reference('c')),
$this->assertTrue($container->getDefinition('a')->isPublic()); 'a' => new ServiceClosureArgument(new Reference('a')),
'b' => new ServiceClosureArgument(new Reference('b')),
),
$serviceLocatorDef->getArgument(0)
);
} }
public function testProcessEventListenersWithMultipleConnections() public function testProcessEventListenersWithMultipleConnections()
@ -136,20 +142,45 @@ class RegisterEventListenersAndSubscribersPassTest extends TestCase
$this->process($container); $this->process($container);
$eventManagerDef = $container->getDefinition('doctrine.dbal.default_connection.event_manager');
// first connection
$this->assertEquals( $this->assertEquals(
array( array(
array('addEventListener', array(array('onFlush'), new Reference('a'))), array('addEventListener', array(array('onFlush'), 'a')),
array('addEventListener', array(array('onFlush'), new Reference('b'))), array('addEventListener', array(array('onFlush'), 'b')),
), ),
$container->getDefinition('doctrine.dbal.default_connection.event_manager')->getMethodCalls() $eventManagerDef->getMethodCalls()
); );
$serviceLocatorDef = $container->getDefinition((string) $eventManagerDef->getArgument(0));
$this->assertSame(ServiceLocator::class, $serviceLocatorDef->getClass());
$this->assertEquals( $this->assertEquals(
array( array(
array('addEventListener', array(array('onFlush'), new Reference('a'))), 'a' => new ServiceClosureArgument(new Reference('a')),
array('addEventListener', array(array('onFlush'), new Reference('c'))), 'b' => new ServiceClosureArgument(new Reference('b')),
), ),
$container->getDefinition('doctrine.dbal.second_connection.event_manager')->getMethodCalls() $serviceLocatorDef->getArgument(0)
);
// second connection
$secondEventManagerDef = $container->getDefinition('doctrine.dbal.second_connection.event_manager');
$this->assertEquals(
array(
array('addEventListener', array(array('onFlush'), 'a')),
array('addEventListener', array(array('onFlush'), 'c')),
),
$secondEventManagerDef->getMethodCalls()
);
$serviceLocatorDef = $container->getDefinition((string) $secondEventManagerDef->getArgument(0));
$this->assertSame(ServiceLocator::class, $serviceLocatorDef->getClass());
$this->assertEquals(
array(
'a' => new ServiceClosureArgument(new Reference('a')),
'c' => new ServiceClosureArgument(new Reference('c')),
),
$serviceLocatorDef->getArgument(0)
); );
} }
@ -269,11 +300,13 @@ class RegisterEventListenersAndSubscribersPassTest extends TestCase
$connections = array('default' => 'doctrine.dbal.default_connection'); $connections = array('default' => 'doctrine.dbal.default_connection');
$container->register('doctrine.dbal.default_connection.event_manager', 'stdClass'); $container->register('doctrine.dbal.default_connection.event_manager', 'stdClass')
->addArgument(new Reference('service_container'));
$container->register('doctrine.dbal.default_connection', 'stdClass'); $container->register('doctrine.dbal.default_connection', 'stdClass');
if ($multipleConnections) { if ($multipleConnections) {
$container->register('doctrine.dbal.second_connection.event_manager', 'stdClass'); $container->register('doctrine.dbal.second_connection.event_manager', 'stdClass')
->addArgument(new Reference('service_container'));
$container->register('doctrine.dbal.second_connection', 'stdClass'); $container->register('doctrine.dbal.second_connection', 'stdClass');
$connections['second'] = 'doctrine.dbal.second_connection'; $connections['second'] = 'doctrine.dbal.second_connection';
} }