[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
the token classes is deprecated. To use
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;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
@ -80,10 +81,10 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
{
$listenerTag = $this->tagPrefix.'.event_listener';
$taggedListeners = $this->findAndSortTags($listenerTag, $container);
$listenerRefs = array();
foreach ($taggedListeners as $taggedListener) {
list($id, $tag) = $taggedListener;
$taggedListenerDef = $container->getDefinition($id);
if (!isset($tag['event'])) {
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])) {
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))));
}
if ($lazy = !empty($tag['lazy'])) {
$taggedListenerDef->setPublic(true);
}
$listenerRefs[$con][$id] = new Reference($id);
// 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)

View File

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