Skip to content

Commit

Permalink
Merge pull request #690 from Ahummeling/add-psr6-compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
IonBazan authored Oct 16, 2021
2 parents 6ac19e0 + 4c68233 commit 658aa70
Show file tree
Hide file tree
Showing 11 changed files with 389 additions and 8 deletions.
100 changes: 100 additions & 0 deletions DependencyInjection/Compiler/CacheCompatibilityPass.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

declare(strict_types=1);

namespace Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler;

use Doctrine\Bundle\MongoDBBundle\DependencyInjection\DoctrineMongoDBExtension;
use Doctrine\Common\Cache\CacheProvider;
use Doctrine\Common\Cache\Psr6\CacheAdapter;
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
use Psr\Cache\CacheItemPoolInterface;
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 is_a;
use function trigger_deprecation;

/** @internal */
final class CacheCompatibilityPass implements CompilerPassInterface
{
private const CACHE_SETTER_METHODS_PSR6_SUPPORT = ['setMetadataCache' => true];

public function process(ContainerBuilder $container): void
{
foreach (array_keys($container->findTaggedServiceIds(DoctrineMongoDBExtension::CONFIGURATION_TAG)) as $id) {
/** @var array{0: string, 1: mixed[]} $methodCall */
foreach ($container->getDefinition($id)->getMethodCalls() as $methodCall) {
$methodName = $methodCall[0];

if (! isset(self::CACHE_SETTER_METHODS_PSR6_SUPPORT[$methodName])) {
continue;
}

$methodArgs = $methodCall[1];
$definitionId = (string) $methodArgs[0];
$aliasId = null;
if ($container->hasAlias($definitionId)) {
$aliasId = $definitionId;
$definitionId = (string) $container->getAlias($aliasId);
}

$shouldBePsr6 = self::CACHE_SETTER_METHODS_PSR6_SUPPORT[$methodName];

$this->wrapIfNecessary($container, $aliasId, $definitionId, $shouldBePsr6);
}
}
}

private function createCompatibilityLayerDefinition(ContainerBuilder $container, string $definitionId, bool $shouldBePsr6): ?Definition
{
$definition = $container->getDefinition($definitionId);

while (! $definition->getClass() && $definition instanceof ChildDefinition) {
$definition = $container->findDefinition($definition->getParent());
}

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/mongodb-odm-bundle',
'4.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;
}

$aliasId = $aliasId ?? $definitionId;

$compatibilityLayerId = $definitionId . '.compatibility_layer';
$container->setDefinition($compatibilityLayerId, $compatibilityLayer);

$container->setAlias($aliasId, $compatibilityLayerId);
}
}
98 changes: 97 additions & 1 deletion DependencyInjection/DoctrineMongoDBExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@
use Doctrine\Bundle\MongoDBBundle\EventSubscriber\EventSubscriberInterface;
use Doctrine\Bundle\MongoDBBundle\Fixture\ODMFixtureInterface;
use Doctrine\Bundle\MongoDBBundle\Repository\ServiceDocumentRepositoryInterface;
use Doctrine\Common\Cache\MemcacheCache;
use Doctrine\Common\Cache\RedisCache;
use Doctrine\Common\DataFixtures\Loader as DataFixturesLoader;
use Doctrine\Common\EventSubscriber;
use Doctrine\ODM\MongoDB\DocumentManager;
use InvalidArgumentException;
use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension;
use Symfony\Bridge\Doctrine\Messenger\DoctrineClearEntityManagerWorkerSubscriber;
use Symfony\Component\Cache\Adapter\ApcuAdapter;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\MemcachedAdapter;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Config\Definition\BaseNode;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Alias;
Expand All @@ -39,6 +46,9 @@
*/
class DoctrineMongoDBExtension extends AbstractDoctrineExtension
{
/** @internal */
public const CONFIGURATION_TAG = 'doctrine.odm.configuration';

/**
* Responds to the doctrine_mongodb configuration parameter.
*/
Expand Down Expand Up @@ -198,6 +208,7 @@ protected function loadDocumentManager(array $documentManager, $defaultDM, $defa
$defaultDatabase = $documentManager['database'] ?? $defaultDB;

$odmConfigDef = new Definition('%doctrine_mongodb.odm.configuration.class%');
$odmConfigDef->addTag(self::CONFIGURATION_TAG);
$container->setDefinition(
$configurationId,
$odmConfigDef
Expand All @@ -207,7 +218,7 @@ protected function loadDocumentManager(array $documentManager, $defaultDM, $defa
$this->loadObjectManagerCacheDriver($documentManager, $container, 'metadata_cache');

$methods = [
'setMetadataCacheImpl' => new Reference(sprintf('doctrine_mongodb.odm.%s_metadata_cache', $documentManager['name'])),
'setMetadataCache' => new Reference(sprintf('doctrine_mongodb.odm.%s_metadata_cache', $documentManager['name'])),
'setMetadataDriverImpl' => new Reference(sprintf('doctrine_mongodb.odm.%s_metadata_driver', $documentManager['name'])),
'setProxyDir' => '%doctrine_mongodb.odm.proxy_dir%',
'setProxyNamespace' => '%doctrine_mongodb.odm.proxy_namespace%',
Expand Down Expand Up @@ -508,6 +519,91 @@ public function getXsdValidationBasePath()
return __DIR__ . '/../Resources/config/schema';
}

/**
* Loads a cache driver.
*
* @param string $cacheName The cache driver name
* @param string $objectManagerName The object manager name
* @param array $cacheDriver The cache driver mapping
*
* @return string
*
* @throws InvalidArgumentException
*
* @psalm-suppress UndefinedClass this won't be necessary when removing metadata cache configuration
*/
protected function loadCacheDriver($cacheName, $objectManagerName, array $cacheDriver, ContainerBuilder $container)
{
if (isset($cacheDriver['namespace'])) {
return parent::loadCacheDriver($cacheName, $objectManagerName, $cacheDriver, $container);
}

$cacheDriverServiceId = $this->getObjectManagerElementName($objectManagerName . '_' . $cacheName);

switch ($cacheDriver['type']) {
case 'service':
$container->setAlias($cacheDriverServiceId, new Alias($cacheDriver['id'], false));

return $cacheDriverServiceId;

case 'memcached':
if (! empty($cacheDriver['class']) && $cacheDriver['class'] !== MemcacheCache::class) {
return parent::loadCacheDriver($cacheName, $objectManagerName, $cacheDriver, $container);
}

$memcachedInstanceClass = ! empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%' . $this->getObjectManagerElementName('cache.memcached_instance.class') . '%';
$memcachedHost = ! empty($cacheDriver['host']) ? $cacheDriver['host'] : '%' . $this->getObjectManagerElementName('cache.memcached_host') . '%';
$memcachedPort = ! empty($cacheDriver['port']) ? $cacheDriver['port'] : '%' . $this->getObjectManagerElementName('cache.memcached_port') . '%';
$memcachedInstance = new Definition($memcachedInstanceClass);
$memcachedInstance->addMethodCall('addServer', [
$memcachedHost,
$memcachedPort,
]);
$container->setDefinition($this->getObjectManagerElementName(sprintf('%s_memcached_instance', $objectManagerName)), $memcachedInstance);

$cacheDef = new Definition(MemcachedAdapter::class, [new Reference($this->getObjectManagerElementName(sprintf('%s_memcached_instance', $objectManagerName)))]);

break;

case 'redis':
if (! empty($cacheDriver['class']) && $cacheDriver['class'] !== RedisCache::class) {
return parent::loadCacheDriver($cacheName, $objectManagerName, $cacheDriver, $container);
}

$redisInstanceClass = ! empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%' . $this->getObjectManagerElementName('cache.redis_instance.class') . '%';
$redisHost = ! empty($cacheDriver['host']) ? $cacheDriver['host'] : '%' . $this->getObjectManagerElementName('cache.redis_host') . '%';
$redisPort = ! empty($cacheDriver['port']) ? $cacheDriver['port'] : '%' . $this->getObjectManagerElementName('cache.redis_port') . '%';
$redisInstance = new Definition($redisInstanceClass);
$redisInstance->addMethodCall('connect', [
$redisHost,
$redisPort,
]);
$container->setDefinition($this->getObjectManagerElementName(sprintf('%s_redis_instance', $objectManagerName)), $redisInstance);

$cacheDef = new Definition(RedisAdapter::class, [new Reference($this->getObjectManagerElementName(sprintf('%s_redis_instance', $objectManagerName)))]);

break;

case 'apcu':
$cacheDef = new Definition(ApcuAdapter::class);

break;

case 'array':
$cacheDef = new Definition(ArrayAdapter::class);

break;

default:
return parent::loadCacheDriver($cacheName, $objectManagerName, $cacheDriver, $container);
}

$cacheDef->setPublic(false);
$container->setDefinition($cacheDriverServiceId, $cacheDef);

return $cacheDriverServiceId;
}

private function buildDeprecationArgs(string $version, string $message): array
{
// @todo Remove when support for Symfony 5.1 and older is dropped
Expand Down
2 changes: 2 additions & 0 deletions DoctrineMongoDBBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Doctrine\Bundle\MongoDBBundle;

use Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\CacheCompatibilityPass;
use Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\CreateHydratorDirectoryPass;
use Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\CreateProxyDirectoryPass;
use Doctrine\Bundle\MongoDBBundle\DependencyInjection\Compiler\FixturesCompilerPass;
Expand Down Expand Up @@ -33,6 +34,7 @@ class DoctrineMongoDBBundle extends Bundle

public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new CacheCompatibilityPass());
$container->addCompilerPass(new RegisterEventListenersAndSubscribersPass('doctrine_mongodb.odm.connections', 'doctrine_mongodb.odm.%s_connection.event_manager', 'doctrine_mongodb.odm'), PassConfig::TYPE_BEFORE_OPTIMIZATION);
$container->addCompilerPass(new CreateProxyDirectoryPass(), PassConfig::TYPE_BEFORE_REMOVING);
$container->addCompilerPass(new CreateHydratorDirectoryPass(), PassConfig::TYPE_BEFORE_REMOVING);
Expand Down
17 changes: 17 additions & 0 deletions Repository/ContainerRepositoryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
use Doctrine\ODM\MongoDB\Repository\DocumentRepository;
use Doctrine\ODM\MongoDB\Repository\GridFSRepository;
use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
use Doctrine\Persistence\ObjectRepository;
use Psr\Container\ContainerInterface;
Expand Down Expand Up @@ -37,6 +38,13 @@ public function __construct(ContainerInterface $container)
$this->container = $container;
}

/**
* @psalm-param class-string<T> $documentName
*
* @psalm-return ObjectRepository<T>
*
* @template T of object
*/
public function getRepository(DocumentManager $documentManager, string $documentName): ObjectRepository
{
$metadata = $documentManager->getClassMetadata($documentName);
Expand All @@ -45,6 +53,7 @@ public function getRepository(DocumentManager $documentManager, string $document
if ($customRepositoryName !== null) {
// fetch from the container
if ($this->container && $this->container->has($customRepositoryName)) {
/** @var ObjectRepository<T> $repository */
$repository = $this->container->get($customRepositoryName);

if (! $repository instanceof DocumentRepository) {
Expand All @@ -69,6 +78,13 @@ public function getRepository(DocumentManager $documentManager, string $document
return $this->getOrCreateRepository($documentManager, $metadata);
}

/**
* @psalm-param ClassMetadata<T> $metadata
*
* @psalm-return ObjectRepository<T>
*
* @template T of object
*/
private function getOrCreateRepository(DocumentManager $documentManager, ClassMetadata $metadata): ObjectRepository
{
$repositoryHash = $metadata->getName() . spl_object_hash($documentManager);
Expand All @@ -79,6 +95,7 @@ private function getOrCreateRepository(DocumentManager $documentManager, ClassMe
if ($metadata->customRepositoryClassName) {
$repositoryClassName = $metadata->customRepositoryClassName;
} elseif ($metadata->isFile) {
/** @psalm-var class-string<GridFSRepository<T>> $repositoryClassName */
$repositoryClassName = $documentManager->getConfiguration()->getDefaultGridFSRepositoryClassName();
} else {
$repositoryClassName = $documentManager->getConfiguration()->getDefaultDocumentRepositoryClassName();
Expand Down
4 changes: 4 additions & 0 deletions Repository/ServiceDocumentRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@
* parent::__construct($registry, YourDocument::class);
* }
* }
*
* @template T of object
* @template-extends DocumentRepository<T>
*/
class ServiceDocumentRepository extends DocumentRepository implements ServiceDocumentRepositoryInterface
{
/** @use ServiceRepositoryTrait<T> */
use ServiceRepositoryTrait;
}
4 changes: 4 additions & 0 deletions Repository/ServiceRepositoryTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@
use function assert;
use function sprintf;

/**
* @template T of object
*/
trait ServiceRepositoryTrait
{
/**
* @param string $documentClass The class name of the entity this repository manages
* @psalm-param class-string<T> $documentClass
*/
public function __construct(ManagerRegistry $registry, $documentClass)
{
Expand Down
6 changes: 6 additions & 0 deletions Tests/DependencyInjection/AbstractMongoDBExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ abstract class AbstractMongoDBExtensionTest extends TestCase
{
abstract protected function loadFromFile(ContainerBuilder $container, string $file): void;

/**
* @psalm-suppress UndefinedClass this won't be necessary when removing metadata cache configuration
*/
public function testDependencyInjectionConfigurationDefaults(): void
{
$container = $this->getContainer();
Expand Down Expand Up @@ -336,6 +339,9 @@ public function testDocumentManagerMetadataCacheDriverConfiguration(): void
$this->assertEquals('%doctrine_mongodb.odm.cache.apc.class%', $definition->getClass());
}

/**
* @psalm-suppress UndefinedClass this won't be necessary when removing metadata cache configuration
*/
public function testDocumentManagerMemcachedMetadataCacheDriverConfiguration(): void
{
$container = $this->getContainer();
Expand Down
Loading

0 comments on commit 658aa70

Please sign in to comment.