diff --git a/DataCollector/DoctrineDataCollector.php b/DataCollector/DoctrineDataCollector.php index b0996e7c7..632815e41 100644 --- a/DataCollector/DoctrineDataCollector.php +++ b/DataCollector/DoctrineDataCollector.php @@ -8,7 +8,7 @@ use Doctrine\ORM\Cache\Logging\StatisticsCacheLogger; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Tools\SchemaValidator; use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory; @@ -105,7 +105,7 @@ public function collect(Request $request, Response $response, ?Throwable $except assert($factory instanceof AbstractClassMetadataFactory); foreach ($factory->getLoadedMetadata() as $class) { - assert($class instanceof ClassMetadataInfo); + assert($class instanceof ClassMetadata); if (isset($entities[$name][$class->getName()])) { continue; } diff --git a/DependencyInjection/Compiler/IdGeneratorPass.php b/DependencyInjection/Compiler/IdGeneratorPass.php index e2d52c356..fe22d66fc 100644 --- a/DependencyInjection/Compiler/IdGeneratorPass.php +++ b/DependencyInjection/Compiler/IdGeneratorPass.php @@ -30,9 +30,7 @@ public function process(ContainerBuilder $container): void return; } - $generatorRefs = array_map(static function ($id) { - return new Reference($id); - }, $generatorIds); + $generatorRefs = array_map(static fn ($id) => new Reference($id), $generatorIds); $ref = ServiceLocatorTagPass::register($container, array_combine($generatorIds, $generatorRefs)); $container->setAlias('doctrine.id_generator_locator', new Alias((string) $ref, false)); diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index aba623dc8..4a5069b2b 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -2,11 +2,11 @@ namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection; -use Doctrine\Common\Proxy\AbstractProxyFactory; use Doctrine\DBAL\Schema\LegacySchemaManagerFactory; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Mapping\ClassMetadataFactory; +use Doctrine\ORM\Proxy\ProxyFactory; use InvalidArgumentException; use ReflectionClass; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; @@ -32,6 +32,7 @@ use function is_int; use function is_string; use function key; +use function method_exists; use function reset; use function sprintf; use function strlen; @@ -498,11 +499,11 @@ private function addOrmSection(ArrayNodeDefinition $node): void ->validate() ->ifString() ->then(static function ($v) { - return constant('Doctrine\Common\Proxy\AbstractProxyFactory::AUTOGENERATE_' . strtoupper($v)); + return constant('Doctrine\ORM\Proxy\ProxyFactory::AUTOGENERATE_' . strtoupper($v)); }) ->end() ->end() - ->booleanNode('enable_lazy_ghost_objects')->defaultFalse() + ->booleanNode('enable_lazy_ghost_objects')->defaultValue(! method_exists(ProxyFactory::class, 'resetUninitializedProxy')) ->end() ->scalarNode('proxy_dir')->defaultValue('%kernel.cache_dir%/doctrine/orm/Proxies')->end() ->scalarNode('proxy_namespace')->defaultValue('Proxies')->end() @@ -832,7 +833,7 @@ private function getAutoGenerateModes(): array { $constPrefix = 'AUTOGENERATE_'; $prefixLen = strlen($constPrefix); - $refClass = new ReflectionClass(AbstractProxyFactory::class); + $refClass = new ReflectionClass(ProxyFactory::class); $constsArray = $refClass->getConstants(); $namesArray = []; $valuesArray = []; diff --git a/DependencyInjection/DoctrineExtension.php b/DependencyInjection/DoctrineExtension.php index 5633d4516..2c6e86d60 100644 --- a/DependencyInjection/DoctrineExtension.php +++ b/DependencyInjection/DoctrineExtension.php @@ -19,12 +19,12 @@ use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface; use Doctrine\DBAL\Schema\LegacySchemaManagerFactory; use Doctrine\ORM\Configuration as OrmConfiguration; -use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Events; use Doctrine\ORM\Id\AbstractIdGenerator; use Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver; use Doctrine\ORM\Proxy\Autoloader; +use Doctrine\ORM\Proxy\ProxyFactory; use Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand; use Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand; use Doctrine\ORM\Tools\Export\ClassMetadataExporter; @@ -32,7 +32,6 @@ use Doctrine\Persistence\Reflection\RuntimeReflectionProperty; use InvalidArgumentException; use LogicException; -use ReflectionMethod; use Symfony\Bridge\Doctrine\ArgumentResolver\EntityValueResolver; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Bridge\Doctrine\DependencyInjection\AbstractDoctrineExtension; @@ -75,6 +74,9 @@ use function sprintf; use function str_replace; use function trait_exists; +use function trigger_deprecation; + +use const PHP_VERSION_ID; /** * DoctrineExtension is an extension for the Doctrine DBAL and ORM library. @@ -438,11 +440,6 @@ protected function ormLoad(array $config, ContainerBuilder $container) $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('orm.xml'); - if (! (new ReflectionMethod(EntityManager::class, '__construct'))->isPublic()) { - $container->getDefinition('doctrine.orm.entity_manager.abstract') - ->setFactory(['%doctrine.orm.entity_manager.class%', 'create']); - } - if (class_exists(AbstractType::class)) { $container->getDefinition('form.type.entity')->addTag('kernel.reset', ['method' => 'reset']); } @@ -545,13 +542,6 @@ protected function ormLoad(array $config, ContainerBuilder $container) $container->setParameter('doctrine.default_entity_manager', $config['default_entity_manager']); if ($config['enable_lazy_ghost_objects'] ?? false) { - if (! method_exists(OrmConfiguration::class, 'setLazyGhostObjectEnabled')) { - throw new LogicException( - 'Lazy ghost objects cannot be enabled because the "doctrine/orm" library' - . ' version 2.14 or higher is not installed. Please run "composer update doctrine/orm".', - ); - } - // available in Symfony 6.2 and higher /** @psalm-suppress UndefinedClass */ if (! trait_exists(LazyGhostTrait::class)) { @@ -567,6 +557,12 @@ protected function ormLoad(array $config, ContainerBuilder $container) . ' version 3.1 or higher is not installed. Please run "composer update doctrine/persistence".', ); } + } elseif (! method_exists(ProxyFactory::class, 'resetUninitializedProxy')) { + throw new LogicException( + 'Lazy ghost objects cannot be disabled for ORM 3.', + ); + } elseif (PHP_VERSION_ID >= 80100) { + trigger_deprecation('doctrine/doctrine-bundle', '2.11', 'Not setting "enable_lazy_ghost_objects" to true is deprecated.'); } $options = ['auto_generate_proxy_classes', 'enable_lazy_ghost_objects', 'proxy_dir', 'proxy_namespace']; diff --git a/Mapping/ContainerEntityListenerResolver.php b/Mapping/ContainerEntityListenerResolver.php index b2ff245c8..5bc96d745 100644 --- a/Mapping/ContainerEntityListenerResolver.php +++ b/Mapping/ContainerEntityListenerResolver.php @@ -32,7 +32,7 @@ public function __construct(ContainerInterface $container) /** * {@inheritDoc} */ - public function clear($className = null) + public function clear($className = null): void { if ($className === null) { $this->instances = []; @@ -48,7 +48,7 @@ public function clear($className = null) /** * {@inheritDoc} */ - public function register($object) + public function register($object): void { if (! is_object($object)) { throw new InvalidArgumentException(sprintf('An object was expected, but got "%s".', gettype($object))); @@ -70,7 +70,7 @@ public function registerService($className, $serviceId) /** * {@inheritDoc} */ - public function resolve($className) + public function resolve($className): object { $className = $this->normalizeClassName($className); @@ -85,8 +85,7 @@ public function resolve($className) return $this->instances[$className]; } - /** @return object */ - private function resolveService(string $serviceId) + private function resolveService(string $serviceId): object { if (! $this->container->has($serviceId)) { throw new RuntimeException(sprintf('There is no service named "%s"', $serviceId)); diff --git a/Mapping/MappingDriver.php b/Mapping/MappingDriver.php index 93c385732..88cfec04c 100644 --- a/Mapping/MappingDriver.php +++ b/Mapping/MappingDriver.php @@ -2,7 +2,7 @@ namespace Doctrine\Bundle\DoctrineBundle\Mapping; -use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\ClassMetadata as OrmClassMetadata; use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\Mapping\Driver\MappingDriver as MappingDriverInterface; use Psr\Container\ContainerInterface; @@ -42,8 +42,8 @@ public function loadMetadataForClass($className, ClassMetadata $metadata): void $this->driver->loadMetadataForClass($className, $metadata); if ( - ! $metadata instanceof ClassMetadataInfo - || $metadata->generatorType !== ClassMetadataInfo::GENERATOR_TYPE_CUSTOM + ! $metadata instanceof OrmClassMetadata + || $metadata->generatorType !== OrmClassMetadata::GENERATOR_TYPE_CUSTOM || ! isset($metadata->customGeneratorDefinition['class']) || ! $this->idGeneratorLocator->has($metadata->customGeneratorDefinition['class']) ) { @@ -52,7 +52,7 @@ public function loadMetadataForClass($className, ClassMetadata $metadata): void $idGenerator = $this->idGeneratorLocator->get($metadata->customGeneratorDefinition['class']); $metadata->setCustomGeneratorDefinition(['instance' => $idGenerator] + $metadata->customGeneratorDefinition); - $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_NONE); + $metadata->setIdGeneratorType(OrmClassMetadata::GENERATOR_TYPE_NONE); } /** diff --git a/Repository/ContainerRepositoryFactory.php b/Repository/ContainerRepositoryFactory.php index 030d224c4..848d9151d 100644 --- a/Repository/ContainerRepositoryFactory.php +++ b/Repository/ContainerRepositoryFactory.php @@ -4,6 +4,7 @@ use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\ServiceRepositoryCompilerPass; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Repository\RepositoryFactory; use Doctrine\Persistence\ObjectRepository; @@ -11,15 +12,19 @@ use RuntimeException; use function class_exists; +use function get_debug_type; use function is_a; use function spl_object_hash; use function sprintf; +use function trigger_deprecation; /** * Fetches repositories from the container or falls back to normal creation. */ final class ContainerRepositoryFactory implements RepositoryFactory { + use RepositoryFactoryCompatibility; + /** @var array */ private array $managedRepositories = []; @@ -32,11 +37,14 @@ public function __construct(ContainerInterface $container) } /** - * {@inheritDoc} + * @param class-string $entityName + * + * @return ObjectRepository + * @psalm-return ($strictTypeCheck is true ? EntityRepository : ObjectRepository) * * @template T of object */ - public function getRepository(EntityManagerInterface $entityManager, $entityName): ObjectRepository + private function doGetRepository(EntityManagerInterface $entityManager, string $entityName, bool $strictTypeCheck): ObjectRepository { $metadata = $entityManager->getClassMetadata($entityName); $repositoryServiceId = $metadata->customRepositoryClassName; @@ -47,8 +55,16 @@ public function getRepository(EntityManagerInterface $entityManager, $entityName if ($this->container->has($customRepositoryName)) { $repository = $this->container->get($customRepositoryName); + if (! $repository instanceof EntityRepository && $strictTypeCheck) { + throw new RuntimeException(sprintf('The service "%s" must extends EntityRepository (e.g. by extending ServiceEntityRepository), "%s" given.', $repositoryServiceId, get_debug_type($repository))); + } + if (! $repository instanceof ObjectRepository) { - throw new RuntimeException(sprintf('The service "%s" must implement ObjectRepository (or extend a base class, like ServiceEntityRepository).', $repositoryServiceId)); + throw new RuntimeException(sprintf('The service "%s" must implement ObjectRepository (or extend a base class, like ServiceEntityRepository), "%s" given.', $repositoryServiceId, get_debug_type($repository))); + } + + if (! $repository instanceof EntityRepository) { + trigger_deprecation('doctrine/doctrine-bundle', '2.11', 'The service "%s" of type "%s" should extend "%s", not doing so is deprecated.', $repositoryServiceId, get_debug_type($repository), EntityRepository::class); } /** @psalm-var ObjectRepository */ diff --git a/Repository/RepositoryFactoryCompatibility.php b/Repository/RepositoryFactoryCompatibility.php new file mode 100644 index 000000000..3e812ba7e --- /dev/null +++ b/Repository/RepositoryFactoryCompatibility.php @@ -0,0 +1,39 @@ +hasReturnType()) { + /** @internal */ + trait RepositoryFactoryCompatibility + { + /** + * Gets the repository for an entity class. + * + * @param class-string $entityName + * + * @return EntityRepository + * + * @template T of object + */ + public function getRepository(EntityManagerInterface $entityManager, string $entityName): EntityRepository + { + return $this->doGetRepository($entityManager, $entityName, true); + } + } +} else { + /** @internal */ + trait RepositoryFactoryCompatibility + { + /** {@inheritDoc} */ + public function getRepository(EntityManagerInterface $entityManager, $entityName): ObjectRepository + { + return $this->doGetRepository($entityManager, $entityName, false); + } + } +} diff --git a/Repository/ServiceEntityRepository.php b/Repository/ServiceEntityRepository.php index 0b580fb56..aa567518b 100644 --- a/Repository/ServiceEntityRepository.php +++ b/Repository/ServiceEntityRepository.php @@ -7,10 +7,11 @@ use LogicException; use Symfony\Component\VarExporter\LazyGhostTrait; +use function property_exists; use function sprintf; use function trait_exists; -if (trait_exists(LazyGhostTrait::class)) { +if (trait_exists(LazyGhostTrait::class) && property_exists(EntityRepository::class, '_entityName')) { /** * @template T of object * @template-extends LazyServiceEntityRepository diff --git a/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php b/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php index 4e9be9221..ad536a108 100644 --- a/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php +++ b/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php @@ -10,9 +10,11 @@ use Doctrine\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension; use Doctrine\Common\Cache\Psr6\DoctrineProvider; use Doctrine\DBAL\Configuration; +use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connections\PrimaryReadReplicaConnection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Schema\LegacySchemaManagerFactory; +use Doctrine\ORM\Configuration as OrmConfiguration; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver; @@ -20,7 +22,6 @@ use InvalidArgumentException; use PDO; use PHPUnit\Framework\TestCase; -use ReflectionMethod; use Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass\RegisterEventListenersAndSubscribersPass; use Symfony\Bundle\DoctrineBundle\Tests\DependencyInjection\TestHydrator; use Symfony\Component\Cache\Adapter\ArrayAdapter; @@ -313,18 +314,15 @@ public function testLoadSimpleSingleConnection(): void 'defaultTableOptions' => [], ], new Reference('doctrine.dbal.default_connection.configuration'), - new Reference('doctrine.dbal.default_connection.event_manager'), + method_exists(Connection::class, 'getEventManager') + ? new Reference('doctrine.dbal.default_connection.event_manager') + : null, [], ]); $definition = $container->getDefinition('doctrine.orm.default_entity_manager'); $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getClass()); - - if (! (new ReflectionMethod(EntityManager::class, '__construct'))->isPublic()) { - $this->assertSame(['%doctrine.orm.entity_manager.class%', 'create'], $definition->getFactory()); - } else { - $this->assertNull($definition->getFactory()); - } + $this->assertNull($definition->getFactory()); $this->assertDICConstructorArguments($definition, [ new Reference('doctrine.dbal.default_connection'), @@ -358,7 +356,9 @@ public function testLoadSimpleSingleConnectionWithoutDbName(): void 'defaultTableOptions' => [], ], new Reference('doctrine.dbal.default_connection.configuration'), - new Reference('doctrine.dbal.default_connection.event_manager'), + method_exists(Connection::class, 'getEventManager') + ? new Reference('doctrine.dbal.default_connection.event_manager') + : null, [], ]); @@ -395,7 +395,9 @@ public function testLoadSingleConnection(): void 'defaultTableOptions' => [], ], new Reference('doctrine.dbal.default_connection.configuration'), - new Reference('doctrine.dbal.default_connection.event_manager'), + method_exists(Connection::class, 'getEventManager') + ? new Reference('doctrine.dbal.default_connection.event_manager') + : null, [], ]); @@ -427,7 +429,9 @@ public function testLoadMultipleConnections(): void $this->assertEquals('localhost', $args[0]['host']); $this->assertEquals('sqlite_user', $args[0]['user']); $this->assertEquals('doctrine.dbal.conn1_connection.configuration', (string) $args[1]); - $this->assertEquals('doctrine.dbal.conn1_connection.event_manager', (string) $args[2]); + if (method_exists(Connection::class, 'getEventManager')) { + $this->assertEquals('doctrine.dbal.conn1_connection.event_manager', (string) $args[2]); + } $this->assertEquals('doctrine.orm.em2_entity_manager', (string) $container->getAlias('doctrine.orm.entity_manager')); @@ -447,7 +451,9 @@ public function testLoadMultipleConnections(): void $this->assertEquals('localhost', $args[0]['host']); $this->assertEquals('sqlite_user', $args[0]['user']); $this->assertEquals('doctrine.dbal.conn2_connection.configuration', (string) $args[1]); - $this->assertEquals('doctrine.dbal.conn2_connection.event_manager', (string) $args[2]); + if (method_exists(Connection::class, 'getEventManager')) { + $this->assertEquals('doctrine.dbal.conn2_connection.event_manager', (string) $args[2]); + } $definition = $container->getDefinition('doctrine.orm.em2_entity_manager'); $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getClass()); @@ -753,41 +759,43 @@ public static function cacheConfigProvider(): Generator 'cacheGetter' => 'getMetadataCache', ]; - yield 'query_cache_pool' => [ - 'expectedClass' => DoctrineProvider::class, - 'entityManagerName' => 'query_cache_pool', - 'cacheGetter' => 'getQueryCacheImpl', - ]; + if (method_exists(OrmConfiguration::class, 'getQueryCacheImpl')) { + 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_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 '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_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_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 'result_cache_service_doctrine' => [ + 'expectedClass' => DoctrineProvider::class, + 'entityManagerName' => 'result_cache_service_doctrine', + 'cacheGetter' => 'getResultCacheImpl', + ]; + } yield 'second_level_cache_pool' => [ 'expectedClass' => null, diff --git a/Tests/DependencyInjection/DoctrineExtensionTest.php b/Tests/DependencyInjection/DoctrineExtensionTest.php index 6120ab46e..ffa3b987b 100644 --- a/Tests/DependencyInjection/DoctrineExtensionTest.php +++ b/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -37,7 +37,6 @@ use LogicException; use PHPUnit\Framework\TestCase; use ReflectionClass; -use ReflectionMethod; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Bridge\Doctrine\Messenger\DoctrineClearEntityManagerWorkerSubscriber; use Symfony\Component\Cache\Adapter\ArrayAdapter; @@ -58,6 +57,7 @@ use function in_array; use function interface_exists; use function is_dir; +use function method_exists; use function sprintf; use function sys_get_temp_dir; @@ -483,17 +483,16 @@ public function testDependencyInjectionConfigurationDefaults(): void $this->assertEquals('localhost', $args[0]['host']); $this->assertEquals('root', $args[0]['user']); $this->assertEquals('doctrine.dbal.default_connection.configuration', (string) $args[1]); - $this->assertEquals('doctrine.dbal.default_connection.event_manager', (string) $args[2]); + if (method_exists(Connection::class, 'getEventManager')) { + $this->assertEquals('doctrine.dbal.default_connection.event_manager', (string) $args[2]); + } + $this->assertCount(0, $definition->getMethodCalls()); $definition = $container->getDefinition('doctrine.orm.default_entity_manager'); $this->assertEquals('%doctrine.orm.entity_manager.class%', $definition->getClass()); - if (! (new ReflectionMethod(EntityManager::class, '__construct'))->isPublic()) { - $this->assertSame(['%doctrine.orm.entity_manager.class%', 'create'], $definition->getFactory()); - } else { - $this->assertNull($definition->getFactory()); - } + $this->assertNull($definition->getFactory()); $this->assertEquals(['default' => 'doctrine.orm.default_entity_manager'], $container->getParameter('doctrine.entity_managers'), 'Set of the existing EntityManagers names is incorrect.'); $this->assertEquals('%doctrine.entity_managers%', $container->getDefinition('doctrine')->getArgument(2), 'Set of the existing EntityManagers names is incorrect.'); diff --git a/Tests/DependencyInjection/Fixtures/CustomEntityListenerServiceResolver.php b/Tests/DependencyInjection/Fixtures/CustomEntityListenerServiceResolver.php index 293c05a00..d4921c759 100644 --- a/Tests/DependencyInjection/Fixtures/CustomEntityListenerServiceResolver.php +++ b/Tests/DependencyInjection/Fixtures/CustomEntityListenerServiceResolver.php @@ -24,7 +24,7 @@ public function clear($className = null): void /** * {@inheritDoc} */ - public function resolve($className) + public function resolve($className): object { return $this->resolver->resolve($className); } diff --git a/Tests/DependencyInjection/Fixtures/CustomIdGenerator.php b/Tests/DependencyInjection/Fixtures/CustomIdGenerator.php index 1621ae572..a37bd470f 100644 --- a/Tests/DependencyInjection/Fixtures/CustomIdGenerator.php +++ b/Tests/DependencyInjection/Fixtures/CustomIdGenerator.php @@ -2,7 +2,7 @@ namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures; -use Doctrine\ORM\EntityManager; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Id\AbstractIdGenerator; class CustomIdGenerator extends AbstractIdGenerator @@ -10,7 +10,7 @@ class CustomIdGenerator extends AbstractIdGenerator /** * {@inheritDoc} */ - public function generate(EntityManager $em, $entity) + public function generate(EntityManagerInterface $em, $entity): int { return 42; } diff --git a/Tests/DependencyInjection/TestFilter.php b/Tests/DependencyInjection/TestFilter.php index 5dea06e0d..f8921e126 100644 --- a/Tests/DependencyInjection/TestFilter.php +++ b/Tests/DependencyInjection/TestFilter.php @@ -12,7 +12,8 @@ class TestFilter extends SQLFilter * * {@inheritDoc} */ - public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): void + public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string { + return ''; } } diff --git a/Tests/Mapping/DisconnectedMetadataFactoryTest.php b/Tests/Mapping/DisconnectedMetadataFactoryTest.php index daf574304..ce1473926 100644 --- a/Tests/Mapping/DisconnectedMetadataFactoryTest.php +++ b/Tests/Mapping/DisconnectedMetadataFactoryTest.php @@ -6,7 +6,7 @@ use Doctrine\Bundle\DoctrineBundle\Mapping\DisconnectedMetadataFactory; use Doctrine\Bundle\DoctrineBundle\Tests\TestCase; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\Persistence\ManagerRegistry; use RuntimeException; @@ -25,7 +25,7 @@ public static function setUpBeforeClass(): void public function testCannotFindNamespaceAndPathForMetadata(): void { - $class = new ClassMetadataInfo(self::class); + $class = new ClassMetadata(self::class); $collection = new ClassMetadataCollection([$class]); $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); @@ -41,7 +41,7 @@ public function testCannotFindNamespaceAndPathForMetadata(): void public function testFindNamespaceAndPathForMetadata(): void { /** @psalm-suppress UndefinedClass */ - $class = new ClassMetadataInfo('\Vendor\Package\Class'); + $class = new ClassMetadata('\Vendor\Package\Class'); $collection = new ClassMetadataCollection([$class]); $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); diff --git a/Tests/Repository/ContainerRepositoryFactoryTest.php b/Tests/Repository/ContainerRepositoryFactoryTest.php index c4c4fccbf..f4198721c 100644 --- a/Tests/Repository/ContainerRepositoryFactoryTest.php +++ b/Tests/Repository/ContainerRepositoryFactoryTest.php @@ -8,8 +8,9 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\Persistence\ObjectRepository; +use Doctrine\ORM\Repository\RepositoryFactory; use PHPUnit\Framework\TestCase; +use ReflectionMethod; use RuntimeException; use stdClass; use Symfony\Component\DependencyInjection\Container; @@ -30,7 +31,7 @@ public static function setUpBeforeClass(): void public function testGetRepositoryReturnsService(): void { $em = $this->createEntityManager(['Foo\CoolEntity' => 'my_repo']); - $repo = new StubRepository(); + $repo = new StubRepository($em, new ClassMetadata('Foo\CoolEntity')); $container = $this->createContainer(['my_repo' => $repo]); $factory = new ContainerRepositoryFactory($container); @@ -73,15 +74,18 @@ public function testServiceRepositoriesMustExtendObjectRepository(): void $factory = new ContainerRepositoryFactory($container); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage(<<<'EXCEPTION' -The service "my_repo" must implement ObjectRepository (or extend a base class, like ServiceEntityRepository). -EXCEPTION); + if ((new ReflectionMethod(RepositoryFactory::class, 'getRepository'))->hasReturnType()) { + $this->expectExceptionMessage('The service "my_repo" must extends EntityRepository (e.g. by extending ServiceEntityRepository), "stdClass" given.'); + } else { + $this->expectExceptionMessage('The service "my_repo" must implement ObjectRepository (or extend a base class, like ServiceEntityRepository), "stdClass" given.'); + } + $factory->getRepository($em, 'Foo\CoolEntity'); } public function testServiceRepositoriesCanNotExtendsEntityRepository(): void { - $repo = $this->getMockBuilder(ObjectRepository::class)->getMock(); + $repo = $this->createStub(EntityRepository::class); $container = $this->createContainer(['my_repo' => $repo]); @@ -161,54 +165,10 @@ private function createEntityManager(array $entityRepositoryClasses): EntityMana } } -/** - * Repository implementing non-deprecated interface, as current interface implemented in ORM\EntityRepository - * uses deprecated one and Composer autoload triggers deprecations that can't be silenced by @group legacy - */ -class NonDeprecatedRepository implements ObjectRepository -{ - /** - * {@inheritDoc} - */ - public function find($id) - { - return null; - } - - /** - * {@inheritDoc} - */ - public function findAll(): array - { - return []; - } - - /** - * {@inheritDoc} - */ - public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array - { - return []; - } - - /** - * {@inheritDoc} - */ - public function findOneBy(array $criteria) - { - return null; - } - - public function getClassName(): string - { - return stdClass::class; - } -} - -class StubRepository extends NonDeprecatedRepository +class StubRepository extends EntityRepository { } -class StubServiceRepository extends NonDeprecatedRepository implements ServiceEntityRepositoryInterface +class StubServiceRepository extends EntityRepository implements ServiceEntityRepositoryInterface { } diff --git a/composer.json b/composer.json index e7128d397..89a39daad 100644 --- a/composer.json +++ b/composer.json @@ -41,6 +41,7 @@ "symfony/deprecation-contracts": "^2.1 || ^3", "symfony/doctrine-bridge": "^5.4.19 || ^6.0.7 || ^7.0", "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0", + "symfony/polyfill-php80": "^1.15", "symfony/service-contracts": "^1.1.1 || ^2.0 || ^3" }, "require-dev": { @@ -68,7 +69,7 @@ }, "conflict": { "doctrine/annotations": ">=3.0", - "doctrine/orm": "<2.11 || >=3.0", + "doctrine/orm": "<2.14 || >=4.0", "twig/twig": "<1.34 || >=2.0 <2.4" }, "suggest": { diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 294705c59..a705c5ada 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -24,6 +24,7 @@ Tests/* + Repository/RepositoryFactoryCompatibility.php Repository/ServiceEntityRepository.php