Skip to content

Commit

Permalink
Deprecate custom ObjectRepository implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
derrabus committed Feb 24, 2022
1 parent a52d988 commit 2ef0669
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 37 deletions.
8 changes: 8 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Upgrade to 2.12

## Deprecate custom repository classes that don't extend `EntityRepository`

Although undocumented, it is currently possible to configure a custom repository
class that implements `ObjectRepository` but does not extend the
`EntityRepository` base class.

This is now deprecated. Please extend `EntityRepository` instead.

## Deprecate helper methods from `AbstractCollectionPersister`

The following protected methods of
Expand Down
19 changes: 14 additions & 5 deletions lib/Doctrine/ORM/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@
use Doctrine\Persistence\ObjectRepository;
use LogicException;
use Psr\Cache\CacheItemPoolInterface;
use ReflectionClass;

use function class_exists;
use function is_a;
use function method_exists;
use function sprintf;
use function strtolower;
Expand Down Expand Up @@ -832,27 +832,36 @@ public function getFilterClassName($name)
* Sets default repository class.
*
* @param string $className
* @psalm-param class-string<EntityRepository> $className
*
* @return void
*
* @throws InvalidEntityRepository If $classname is not an ObjectRepository.
*/
public function setDefaultRepositoryClassName($className)
{
$reflectionClass = new ReflectionClass($className);

if (! $reflectionClass->implementsInterface(ObjectRepository::class)) {
if (! class_exists($className) || ! is_a($className, ObjectRepository::class, true)) {
throw InvalidEntityRepository::fromClassName($className);
}

if (! is_a($className, EntityRepository::class, true)) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/9533',
'Configuring %s as default repository class is deprecated because it does not extend %s.',
$className,
EntityRepository::class
);
}

$this->_attributes['defaultRepositoryClassName'] = $className;
}

/**
* Get default repository class.
*
* @return string
* @psalm-return class-string
* @psalm-return class-string<EntityRepository>
*/
public function getDefaultRepositoryClassName()
{
Expand Down
4 changes: 2 additions & 2 deletions lib/Doctrine/ORM/Exception/InvalidEntityRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

namespace Doctrine\ORM\Exception;

use Doctrine\Persistence\ObjectRepository;
use Doctrine\ORM\EntityRepository;

final class InvalidEntityRepository extends ORMException implements ConfigurationException
{
public static function fromClassName(string $className): self
{
return new self(
"Invalid repository class '" . $className . "'. It must be a " . ObjectRepository::class . '.'
"Invalid repository class '" . $className . "'. It must be a " . EntityRepository::class . '.'
);
}
}
16 changes: 15 additions & 1 deletion lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

namespace Doctrine\ORM\Repository;

use Doctrine\Deprecations\Deprecation;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Persistence\ObjectRepository;

use function spl_object_id;
Expand All @@ -18,6 +20,7 @@ final class DefaultRepositoryFactory implements RepositoryFactory
* The list of EntityRepository instances.
*
* @var ObjectRepository[]
* @psalm-var array<string, ObjectRepository>
*/
private $repositoryList = [];

Expand Down Expand Up @@ -49,6 +52,17 @@ private function createRepository(
$repositoryClassName = $metadata->customRepositoryClassName
?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();

return new $repositoryClassName($entityManager, $metadata);
$repository = new $repositoryClassName($entityManager, $metadata);
if (! $repository instanceof EntityRepository) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/9533',
'Configuring %s as repository class is deprecated because it does not extend %s.',
$repositoryClassName,
EntityRepository::class
);
}

return $repository;
}
}
2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/Repository/RepositoryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ interface RepositoryFactory
* @param EntityManagerInterface $entityManager The EntityManager instance.
* @param string $entityName The name of the entity.
*
* @return ObjectRepository
* @return ObjectRepository This type will change to {@see EntityRepository} in 3.0.
*/
public function getRepository(EntityManagerInterface $entityManager, $entityName);
}
13 changes: 6 additions & 7 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,7 @@
</NoInterfaceProperties>
</file>
<file src="lib/Doctrine/ORM/Configuration.php">
<ArgumentTypeCoercion occurrences="2">
<code>$className</code>
<ArgumentTypeCoercion occurrences="1">
<code>$className</code>
</ArgumentTypeCoercion>
<DeprecatedClass occurrences="2">
Expand Down Expand Up @@ -2726,12 +2725,12 @@
</RedundantConditionGivenDocblockType>
</file>
<file src="lib/Doctrine/ORM/Repository/DefaultRepositoryFactory.php">
<LessSpecificReturnStatement occurrences="1">
<TypeDoesNotContainType occurrences="1">
<code>$repository instanceof EntityRepository</code>
</TypeDoesNotContainType>
<UnsafeInstantiation occurrences="1">
<code>new $repositoryClassName($entityManager, $metadata)</code>
</LessSpecificReturnStatement>
<MoreSpecificReturnType occurrences="1">
<code>ObjectRepository</code>
</MoreSpecificReturnType>
</UnsafeInstantiation>
</file>
<file src="lib/Doctrine/ORM/Tools/Console/Command/ClearCache/CollectionRegionCommand.php">
<MissingReturnType occurrences="1">
Expand Down
4 changes: 2 additions & 2 deletions tests/Doctrine/Performance/Hydration/SimpleHydrationBench.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
namespace Doctrine\Performance\Hydration;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\Performance\EntityManagerFactory;
use Doctrine\Persistence\ObjectRepository;
use Doctrine\Tests\Models\CMS;

/**
Expand All @@ -17,7 +17,7 @@ final class SimpleHydrationBench
/** @var EntityManagerInterface */
private $entityManager;

/** @var ObjectRepository */
/** @var EntityRepository */
private $repository;

public function init(): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

namespace Doctrine\Performance\Hydration;

use Doctrine\ORM\EntityRepository;
use Doctrine\Performance\EntityManagerFactory;
use Doctrine\Persistence\ObjectRepository;
use Doctrine\Tests\Models\Company;
use PhpBench\Benchmark\Metadata\Annotations\BeforeMethods;

Expand All @@ -14,16 +14,16 @@
*/
final class SingleTableInheritanceHydrationPerformanceBench
{
/** @var ObjectRepository */
/** @var EntityRepository */
private $contractsRepository;

/** @var ObjectRepository */
/** @var EntityRepository */
private $fixContractsRepository;

/** @var ObjectRepository */
/** @var EntityRepository */
private $flexContractRepository;

/** @var ObjectRepository */
/** @var EntityRepository */
private $ultraContractRepository;

public function init(): void
Expand Down
48 changes: 47 additions & 1 deletion tests/Doctrine/Tests/ORM/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Doctrine\ORM\Cache\Exception\QueryCacheUsesNonPersistentCache;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Exception\InvalidEntityRepository;
use Doctrine\ORM\Exception\ORMException;
use Doctrine\ORM\Exception\ProxyClassesAlwaysRegenerating;
use Doctrine\ORM\Mapping as AnnotationNamespace;
Expand All @@ -25,10 +26,12 @@
use Doctrine\ORM\Mapping\QuoteStrategy;
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\Persistence\ObjectRepository;
use Doctrine\Tests\DoctrineTestCase;
use Doctrine\Tests\Models\DDC753\DDC753CustomRepository;
use Psr\Cache\CacheItemPoolInterface;
use ReflectionClass;
use stdClass;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

use function class_exists;
Expand Down Expand Up @@ -388,10 +391,19 @@ public function setDefaultRepositoryClassName(): void
self::assertSame(EntityRepository::class, $this->configuration->getDefaultRepositoryClassName());
$this->configuration->setDefaultRepositoryClassName(DDC753CustomRepository::class);
self::assertSame(DDC753CustomRepository::class, $this->configuration->getDefaultRepositoryClassName());
$this->expectException(ORMException::class);
$this->expectException(InvalidEntityRepository::class);
$this->expectExceptionMessage('Invalid repository class \'Doctrine\Tests\ORM\ConfigurationTest\'. It must be a Doctrine\ORM\EntityRepository.');
$this->configuration->setDefaultRepositoryClassName(self::class);
}

public function testSetDeprecatedDefaultRepositoryClassName(): void
{
$this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/9533');

$this->configuration->setDefaultRepositoryClassName(DeprecatedRepository::class);
self::assertSame(DeprecatedRepository::class, $this->configuration->getDefaultRepositoryClassName());
}

public function testSetGetNamingStrategy(): void
{
self::assertInstanceOf(NamingStrategy::class, $this->configuration->getNamingStrategy());
Expand Down Expand Up @@ -445,3 +457,37 @@ public function namespacedAnnotationMethod(): void
{
}
}

class DeprecatedRepository implements ObjectRepository
{
/**
* {@inheritdoc}
*/
public function find($id)
{
return null;
}

public function findAll(): array
{
return [];
}

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;
}
}
Loading

0 comments on commit 2ef0669

Please sign in to comment.