Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/2.4.x' into 2.5.x
Browse files Browse the repository at this point in the history
# Conflicts:
#	DependencyInjection/Configuration.php
#	Tests/DependencyInjection/AbstractDoctrineExtensionTest.php
  • Loading branch information
ostrolucky committed Jun 5, 2021
2 parents a112868 + 4202ce6 commit afe36ab
Show file tree
Hide file tree
Showing 7 changed files with 342 additions and 97 deletions.
88 changes: 63 additions & 25 deletions DependencyInjection/Compiler/CacheCompatibilityPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

use function array_keys;
use function assert;
use function is_a;
use function trigger_deprecation;

Expand All @@ -29,44 +31,80 @@ public function process(ContainerBuilder $container): void
{
foreach (array_keys($container->findTaggedServiceIds(self::CONFIGURATION_TAG)) as $id) {
foreach ($container->getDefinition($id)->getMethodCalls() as $methodCall) {
if ($methodCall[0] === 'setSecondLevelCacheConfiguration') {
$this->updateSecondLevelCache($container, $methodCall[1][0]);
continue;
}

if (! isset(self::CACHE_METHODS_PSR6_SUPPORT_MAP[$methodCall[0]])) {
continue;
}

$aliasId = (string) $methodCall[1][0];
$definitionId = (string) $container->getAlias($aliasId);
$definition = $container->getDefinition($definitionId);
$shouldBePsr6 = self::CACHE_METHODS_PSR6_SUPPORT_MAP[$methodCall[0]];

while (! $definition->getClass() && $definition instanceof ChildDefinition) {
$definition = $container->findDefinition($definition->getParent());
}
$this->wrapIfNecessary($container, $aliasId, $definitionId, $shouldBePsr6);
}
}
}

if ($shouldBePsr6 === is_a($definition->getClass(), CacheItemPoolInterface::class, true)) {
continue;
}
private function updateSecondLevelCache(ContainerBuilder $container, Definition $slcConfigDefinition): void
{
foreach ($slcConfigDefinition->getMethodCalls() as $methodCall) {
if ($methodCall[0] !== 'setCacheFactory') {
continue;
}

$targetClass = CacheProvider::class;
$targetFactory = DoctrineProvider::class;
$factoryDefinition = $methodCall[1][0];
assert($factoryDefinition instanceof Definition);
$aliasId = (string) $factoryDefinition->getArgument(1);
$this->wrapIfNecessary($container, $aliasId, (string) $container->getAlias($aliasId), false);
break;
}
}

if ($shouldBePsr6) {
$targetClass = CacheItemPoolInterface::class;
$targetFactory = CacheAdapter::class;
private function createCompatibilityLayerDefinition(ContainerBuilder $container, string $definitionId, bool $shouldBePsr6): ?Definition
{
$definition = $container->getDefinition($definitionId);

trigger_deprecation(
'doctrine/doctrine-bundle',
'2.4',
'Configuring doctrine/cache is deprecated. Please update the cache service "%s" to use a PSR-6 cache.',
$definitionId
);
}
while (! $definition->getClass() && $definition instanceof ChildDefinition) {
$definition = $container->findDefinition($definition->getParent());
}

$compatibilityLayerId = $definitionId . '.compatibility_layer';
$container->setAlias($aliasId, $compatibilityLayerId);
$container->register($compatibilityLayerId, $targetClass)
->setFactory([$targetFactory, 'wrap'])
->addArgument(new Reference($definitionId));
}
if ($shouldBePsr6 === is_a($definition->getClass(), CacheItemPoolInterface::class, true)) {
return null;
}

$targetClass = CacheProvider::class;
$targetFactory = DoctrineProvider::class;

if ($shouldBePsr6) {
$targetClass = CacheItemPoolInterface::class;
$targetFactory = CacheAdapter::class;

trigger_deprecation(
'doctrine/doctrine-bundle',
'2.4',
'Configuring doctrine/cache is deprecated. Please update the cache service "%s" to use a PSR-6 cache.',
$definitionId
);
}

return (new Definition($targetClass))
->setFactory([$targetFactory, 'wrap'])
->addArgument(new Reference($definitionId));
}

private function wrapIfNecessary(ContainerBuilder $container, string $aliasId, string $definitionId, bool $shouldBePsr6): void
{
$compatibilityLayer = $this->createCompatibilityLayerDefinition($container, $definitionId, $shouldBePsr6);
if ($compatibilityLayer === null) {
return;
}

$compatibilityLayerId = $definitionId . '.compatibility_layer';
$container->setAlias($aliasId, $compatibilityLayerId);
$container->setDefinition($compatibilityLayerId, $compatibilityLayer);
}
}
39 changes: 11 additions & 28 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection;

use Doctrine\ORM\EntityManager;
use InvalidArgumentException;
use ReflectionClass;
use Symfony\Component\Config\Definition\BaseNode;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
Expand All @@ -13,6 +12,7 @@
use Symfony\Component\DependencyInjection\Exception\LogicException;

use function array_intersect_key;
use function array_key_exists;
use function array_keys;
use function array_pop;
use function assert;
Expand Down Expand Up @@ -72,15 +72,10 @@ private function addDbalSection(ArrayNodeDefinition $node): void
->children()
->arrayNode('dbal')
->beforeNormalization()
->always(static function (array $v): array {
static $hasExplicitlyDefinedConnectionsAtLeastOnce = false;

if (isset($v['connections']) || isset($v['connection'])) {
$hasExplicitlyDefinedConnectionsAtLeastOnce = true;

return $v;
}

->ifTrue(static function ($v) {
return is_array($v) && ! array_key_exists('connections', $v) && ! array_key_exists('connection', $v);
})
->then(static function ($v) {
// Key that should not be rewritten to the connection config
$excludedKeys = ['default_connection' => true, 'types' => true, 'type' => true];
$connection = [];
Expand All @@ -93,10 +88,6 @@ private function addDbalSection(ArrayNodeDefinition $node): void
unset($v[$key]);
}

if ($connection && $hasExplicitlyDefinedConnectionsAtLeastOnce) {
throw new InvalidArgumentException('Seems like you have configured multiple "dbal" connections. You need to use the long configuration syntax in every doctrine configuration file, or in none of them.');
}

$v['default_connection'] = isset($v['default_connection']) ? (string) $v['default_connection'] : 'default';
$v['connections'] = [$v['default_connection'] => $connection];

Expand Down Expand Up @@ -380,19 +371,15 @@ private function addOrmSection(ArrayNodeDefinition $node): void
->children()
->arrayNode('orm')
->beforeNormalization()
->always(static function (array $v): array {
if ($v && ! class_exists(EntityManager::class)) {
->ifTrue(static function ($v) {
if (! empty($v) && ! class_exists(EntityManager::class)) {
throw new LogicException('The doctrine/orm package is required when the doctrine.orm config is set.');
}

static $hasExplicitlyDefinedEntityManagersAtLeastOnce = false;

if (isset($v['entity_managers']) || isset($v['entity_manager'])) {
$hasExplicitlyDefinedEntityManagersAtLeastOnce = true;

return $v;
}

return $v === null || (is_array($v) && ! array_key_exists('entity_managers', $v) && ! array_key_exists('entity_manager', $v));
})
->then(static function ($v) {
$v = (array) $v;
// Key that should not be rewritten to the connection config
$excludedKeys = [
'default_entity_manager' => true,
Expand All @@ -412,10 +399,6 @@ private function addOrmSection(ArrayNodeDefinition $node): void
unset($v[$key]);
}

if ($entityManager && $hasExplicitlyDefinedEntityManagersAtLeastOnce) {
throw new InvalidArgumentException('Seems like you have configured multiple "entity_managers". You need to use the long configuration syntax in every doctrine configuration file, or in none of them.');
}

$v['default_entity_manager'] = isset($v['default_entity_manager']) ? (string) $v['default_entity_manager'] : 'default';
$v['entity_managers'] = [$v['default_entity_manager'] => $entityManager];

Expand Down
130 changes: 116 additions & 14 deletions Tests/DependencyInjection/AbstractDoctrineExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Doctrine\DBAL\Connections\MasterSlaveConnection;
use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection;
use Doctrine\DBAL\DriverManager;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Generator;
use InvalidArgumentException;
Expand Down Expand Up @@ -216,18 +217,6 @@ class_exists(PrimaryReadReplicaConnection::class) ?
$this->assertEquals(['engine' => 'InnoDB'], $param['defaultTableOptions']);
}

public function testMixedUseOfSimplifiedAndMultipleConnectionsStyleIsInvalid(): void
{
$this->expectExceptionObject(new InvalidArgumentException('Seems like you have configured multiple "dbal" connections. You need to use the long configuration syntax in every doctrine configuration file, or in none of them.'));
$this->loadContainer('dbal_service_{single,multiple}_connectio{n,ns}');
}

public function testMixedUseOfSimplifiedAndMultipleEntityManagersStyleIsInvalid(): void
{
$this->expectExceptionObject(new InvalidArgumentException('Seems like you have configured multiple "entity_managers". You need to use the long configuration syntax in every doctrine configuration file, or in none of them.'));
$this->loadContainer('orm_service_{simple_single,multiple}_entity_manage{r,rs}');
}

public function testDbalLoadPoolShardingConnection(): void
{
$container = $this->loadContainer('dbal_service_pool_sharding_connection');
Expand Down Expand Up @@ -693,6 +682,120 @@ public function testSetQuoteStrategy(): void
$this->assertDICDefinitionMethodCallOnce($def2, 'setQuoteStrategy', [0 => new Reference('doctrine.orm.quote_strategy.ansi')]);
}

/**
* @dataProvider cacheConfigProvider
* @group legacy
*/
public function testCacheConfig(?string $expectedClass, string $entityManagerName, ?string $cacheGetter): void
{
if (! interface_exists(EntityManagerInterface::class)) {
self::markTestSkipped('This test requires ORM');
}

$container = $this->loadContainer('orm_caches');

$entityManagerId = sprintf('doctrine.orm.%s_entity_manager', $entityManagerName);

$em = $container->get($entityManagerId);
assert($em instanceof EntityManager);

$this->assertInstanceOf(EntityManagerInterface::class, $em);

if ($cacheGetter === null) {
return;
}

$configuration = $em->getConfiguration();
$cache = $configuration->$cacheGetter();

if ($expectedClass === null) {
$this->assertNull($cache);
} else {
$this->assertInstanceOf($expectedClass, $cache);
}
}

public static function cacheConfigProvider(): Generator
{
yield 'metadata_cache_none' => [
'expectedClass' => PhpArrayAdapter::class,
'entityManagerName' => 'metadata_cache_none',
'cacheGetter' => 'getMetadataCache',
];

yield 'metadata_cache_pool' => [
'expectedClass' => ArrayAdapter::class,
'entityManagerName' => 'metadata_cache_pool',
'cacheGetter' => 'getMetadataCache',
];

yield 'metadata_cache_service_psr6' => [
'expectedClass' => ArrayAdapter::class,
'entityManagerName' => 'metadata_cache_service_psr6',
'cacheGetter' => 'getMetadataCache',
];

yield 'metadata_cache_service_doctrine' => [
'expectedClass' => ArrayAdapter::class,
'entityManagerName' => 'metadata_cache_service_doctrine',
'cacheGetter' => 'getMetadataCache',
];

yield 'query_cache_pool' => [
'expectedClass' => DoctrineProvider::class,
'entityManagerName' => 'query_cache_pool',
'cacheGetter' => 'getQueryCacheImpl',
];

yield 'query_cache_service_psr6' => [
'expectedClass' => DoctrineProvider::class,
'entityManagerName' => 'query_cache_service_psr6',
'cacheGetter' => 'getQueryCacheImpl',
];

yield 'query_cache_service_doctrine' => [
'expectedClass' => DoctrineProvider::class,
'entityManagerName' => 'query_cache_service_doctrine',
'cacheGetter' => 'getQueryCacheImpl',
];

yield 'result_cache_pool' => [
'expectedClass' => DoctrineProvider::class,
'entityManagerName' => 'result_cache_pool',
'cacheGetter' => 'getResultCacheImpl',
];

yield 'result_cache_service_psr6' => [
'expectedClass' => DoctrineProvider::class,
'entityManagerName' => 'result_cache_service_psr6',
'cacheGetter' => 'getResultCacheImpl',
];

yield 'result_cache_service_doctrine' => [
'expectedClass' => DoctrineProvider::class,
'entityManagerName' => 'result_cache_service_doctrine',
'cacheGetter' => 'getResultCacheImpl',
];

yield 'second_level_cache_pool' => [
'expectedClass' => null,
'entityManagerName' => 'second_level_cache_pool',
'cacheGetter' => null,
];

yield 'second_level_cache_service_psr6' => [
'expectedClass' => null,
'entityManagerName' => 'second_level_cache_service_psr6',
'cacheGetter' => null,
];

yield 'second_level_cache_service_doctrine' => [
'expectedClass' => null,
'entityManagerName' => 'second_level_cache_service_doctrine',
'cacheGetter' => null,
];
}

public function testSecondLevelCache(): void
{
if (! interface_exists(EntityManagerInterface::class)) {
Expand Down Expand Up @@ -735,7 +838,7 @@ public function testSecondLevelCache(): void
$this->assertDICDefinitionClass($myEntityRegionDef, '%doctrine.orm.second_level_cache.default_region.class%');
$this->assertDICDefinitionClass($loggerChainDef, '%doctrine.orm.second_level_cache.logger_chain.class%');
$this->assertDICDefinitionClass($loggerStatisticsDef, '%doctrine.orm.second_level_cache.logger_statistics.class%');
$this->assertDICDefinitionClass($cacheDriverDef, ArrayAdapter::class);
$this->assertDICDefinitionClass($cacheDriverDef, CacheProvider::class);
$this->assertDICDefinitionMethodCallOnce($configDef, 'setSecondLevelCacheConfiguration');
$this->assertDICDefinitionMethodCallCount($slcFactoryDef, 'setRegion', [], 3);
$this->assertDICDefinitionMethodCallCount($loggerChainDef, 'setLogger', [], 3);
Expand Down Expand Up @@ -1281,7 +1384,6 @@ private function loadContainer(
): ContainerBuilder {
$container = $this->getContainer($bundles);
$container->registerExtension(new DoctrineExtension());
$container->addCompilerPass(new CacheCompatibilityPass());

$this->loadFromFile($container, $fixture);

Expand Down
5 changes: 5 additions & 0 deletions Tests/DependencyInjection/DoctrineExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,9 @@ public function testShardManager(): void
$this->assertEquals($managerClass, $bazManagerDef->getClass());
}

// Disabled to prevent changing the comment below to a single-line annotation
// phpcs:disable SlevomatCodingStandard.Commenting.RequireOneLineDocComment.MultiLineDocComment

/**
* @requires PHP 8
*/
Expand Down Expand Up @@ -1118,6 +1121,8 @@ public function testAsEntityListenerAttribute()
$this->assertSame([$expected], $definition->getTag('doctrine.orm.entity_listener'));
}

// phpcs:enable

/** @param list<string> $bundles */
private function getContainer(array $bundles = ['YamlBundle'], string $vendor = ''): ContainerBuilder
{
Expand Down
Loading

0 comments on commit afe36ab

Please sign in to comment.