From 81c0d599c956071e974f9ba4134a8cab9d07565c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Thu, 27 Jun 2024 23:22:59 +0200 Subject: [PATCH] Implement compatibility with Persistence 4 --- composer.json | 2 +- phpcs.xml.dist | 2 ++ phpstan-baseline.neon | 5 +++ phpstan-dbal3.neon | 5 +++ phpstan.neon | 5 +++ psalm-baseline.xml | 23 ++++++++---- src/EntityManager.php | 4 +-- src/Mapping/ClassMetadata.php | 12 ++----- .../Driver/LoadMappingFileImplementation.php | 35 +++++++++++++++++++ src/Mapping/Driver/XmlDriver.php | 8 ++--- .../GetReflectionClassImplementation.php | 33 +++++++++++++++++ .../Mock/NonProxyLoadingEntityManager.php | 8 +++++ tests/Tests/Mocks/MetadataDriverMock.php | 6 ++-- .../ORM/Functional/Ticket/DDC3103Test.php | 14 ++++++-- tests/Tests/ORM/Mapping/ClassMetadataTest.php | 5 +++ 15 files changed, 139 insertions(+), 28 deletions(-) create mode 100644 src/Mapping/Driver/LoadMappingFileImplementation.php create mode 100644 src/Mapping/GetReflectionClassImplementation.php diff --git a/composer.json b/composer.json index ec2ff1fd5a7..7269a3c08a1 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "doctrine/inflector": "^1.4 || ^2.0", "doctrine/instantiator": "^1.3 || ^2", "doctrine/lexer": "^3", - "doctrine/persistence": "^3.3.1", + "doctrine/persistence": "^3.3.1 || ^4", "psr/cache": "^1 || ^2 || ^3", "symfony/console": "^5.4 || ^6.0 || ^7.0", "symfony/var-exporter": "^6.3.9 || ^7.0" diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 4fde4cbf5e5..b14bbc6c1d8 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -48,6 +48,8 @@ + src/Mapping/Driver/LoadMappingFileImplementation.php + src/Mapping/GetReflectionClassImplementation.php tests/* diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7c8ec81ae84..1b6362a6d8e 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -120,6 +120,11 @@ parameters: count: 1 path: src/EntityRepository.php + - + message: "#^If condition is always true\\.$#" + count: 1 + path: src/Mapping/ClassMetadata.php + - message: "#^If condition is always true\\.$#" count: 1 diff --git a/phpstan-dbal3.neon b/phpstan-dbal3.neon index 724fe2003f7..889d94c1df3 100644 --- a/phpstan-dbal3.neon +++ b/phpstan-dbal3.neon @@ -34,3 +34,8 @@ parameters: - message: '~deprecated class Doctrine\\DBAL\\Tools\\Console\\Command\\ReservedWordsCommand\:~' path: src/Tools/Console/ConsoleRunner.php + + # Compatibility with Persistence 3 + - + message: '#Expression on left side of \?\? is not nullable.#' + path: src/Mapping/Driver/AttributeDriver.php diff --git a/phpstan.neon b/phpstan.neon index d90ec9fe41f..f98eb8d00b8 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -45,3 +45,8 @@ parameters: message: '#Negated boolean expression is always false\.#' paths: - src/Mapping/Driver/AttributeDriver.php + + # Compatibility with Persistence 3 + - + message: '#Expression on left side of \?\? is not nullable.#' + path: src/Mapping/Driver/AttributeDriver.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index c7897f4d614..cef61546b74 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -298,15 +298,9 @@ - - - - - reflClass]]> - @@ -340,6 +334,7 @@ + reflClass]]> @@ -474,6 +469,14 @@ + + + + + + + + @@ -526,6 +529,14 @@ + + + + + + + + diff --git a/src/EntityManager.php b/src/EntityManager.php index eb5a123d0b6..5324d9cac7b 100644 --- a/src/EntityManager.php +++ b/src/EntityManager.php @@ -565,9 +565,9 @@ public function initializeObject(object $obj): void /** * {@inheritDoc} */ - public function isUninitializedObject($obj): bool + public function isUninitializedObject($value): bool { - return $this->unitOfWork->isUninitializedObject($obj); + return $this->unitOfWork->isUninitializedObject($value); } public function getFilters(): FilterCollection diff --git a/src/Mapping/ClassMetadata.php b/src/Mapping/ClassMetadata.php index 7c9020805a5..b91bd9b5ac1 100644 --- a/src/Mapping/ClassMetadata.php +++ b/src/Mapping/ClassMetadata.php @@ -73,6 +73,8 @@ */ class ClassMetadata implements PersistenceClassMetadata, Stringable { + use GetReflectionClassImplementation; + /* The inheritance mapping types */ /** * NONE means the class does not participate in an inheritance hierarchy @@ -932,16 +934,6 @@ public function validateLifecycleCallbacks(ReflectionService $reflService): void } } - /** - * {@inheritDoc} - * - * Can return null when using static reflection, in violation of the LSP - */ - public function getReflectionClass(): ReflectionClass|null - { - return $this->reflClass; - } - /** @psalm-param array{usage?: mixed, region?: mixed} $cache */ public function enableCache(array $cache): void { diff --git a/src/Mapping/Driver/LoadMappingFileImplementation.php b/src/Mapping/Driver/LoadMappingFileImplementation.php new file mode 100644 index 00000000000..df351889ba2 --- /dev/null +++ b/src/Mapping/Driver/LoadMappingFileImplementation.php @@ -0,0 +1,35 @@ +doLoadMappingFile($file); + } + } +} else { + /** @internal */ + trait LoadMappingFileImplementation + { + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + return $this->doLoadMappingFile($file); + } + } +} diff --git a/src/Mapping/Driver/XmlDriver.php b/src/Mapping/Driver/XmlDriver.php index 30e85900e2e..e11b6b61d6f 100644 --- a/src/Mapping/Driver/XmlDriver.php +++ b/src/Mapping/Driver/XmlDriver.php @@ -43,6 +43,8 @@ */ class XmlDriver extends FileDriver { + use LoadMappingFileImplementation; + public const DEFAULT_FILE_EXTENSION = '.dcm.xml'; /** @@ -878,10 +880,8 @@ private function getCascadeMappings(SimpleXMLElement $cascadeElement): array return $cascades; } - /** - * {@inheritDoc} - */ - protected function loadMappingFile($file) + /** @return array */ + private function doLoadMappingFile(string $file): array { $this->validateMapping($file); $result = []; diff --git a/src/Mapping/GetReflectionClassImplementation.php b/src/Mapping/GetReflectionClassImplementation.php new file mode 100644 index 00000000000..780015c3680 --- /dev/null +++ b/src/Mapping/GetReflectionClassImplementation.php @@ -0,0 +1,33 @@ +reflClass; + } + } +} else { + trait GetReflectionClassImplementation + { + /** + * {@inheritDoc} + * + * Can return null when using static reflection, in violation of the LSP + */ + public function getReflectionClass(): ReflectionClass|null + { + return $this->reflClass; + } + } +} diff --git a/tests/Performance/Mock/NonProxyLoadingEntityManager.php b/tests/Performance/Mock/NonProxyLoadingEntityManager.php index 20f233e0089..bff330ab9be 100644 --- a/tests/Performance/Mock/NonProxyLoadingEntityManager.php +++ b/tests/Performance/Mock/NonProxyLoadingEntityManager.php @@ -212,4 +212,12 @@ public function contains(object $object): bool { return $this->realEntityManager->contains($object); } + + /** + * {@inheritDoc} + */ + public function isUninitializedObject($value): bool + { + return $this->realEntityManager->isUninitializedObject($value); + } } diff --git a/tests/Tests/Mocks/MetadataDriverMock.php b/tests/Tests/Mocks/MetadataDriverMock.php index b3be873c596..a2472cf8c46 100644 --- a/tests/Tests/Mocks/MetadataDriverMock.php +++ b/tests/Tests/Mocks/MetadataDriverMock.php @@ -15,14 +15,14 @@ class MetadataDriverMock implements MappingDriver /** * {@inheritDoc} */ - public function loadMetadataForClass($className, ClassMetadata $metadata) + public function loadMetadataForClass($className, ClassMetadata $metadata): void { } /** * {@inheritDoc} */ - public function isTransient($className) + public function isTransient($className): bool { return false; } @@ -30,7 +30,7 @@ public function isTransient($className) /** * {@inheritDoc} */ - public function getAllClassNames() + public function getAllClassNames(): array { return []; } diff --git a/tests/Tests/ORM/Functional/Ticket/DDC3103Test.php b/tests/Tests/ORM/Functional/Ticket/DDC3103Test.php index ec505c07741..5ead8d58698 100644 --- a/tests/Tests/ORM/Functional/Ticket/DDC3103Test.php +++ b/tests/Tests/ORM/Functional/Ticket/DDC3103Test.php @@ -7,10 +7,12 @@ use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Embeddable; +use Doctrine\Persistence\Mapping\StaticReflectionService; use Doctrine\Tests\OrmFunctionalTestCase; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\Group; +use function class_exists; use function serialize; use function unserialize; @@ -18,6 +20,15 @@ #[Group('DDC-3103')] class DDC3103Test extends OrmFunctionalTestCase { + protected function setUp(): void + { + if (! class_exists(StaticReflectionService::class)) { + self::markTestSkipped('This test is not supported by the current installed doctrine/persistence version'); + } + + parent::setUp(); + } + public function testIssue(): void { $classMetadata = new ClassMetadata(DDC3103ArticleId::class); @@ -39,7 +50,6 @@ public function testIssue(): void #[Embeddable] class DDC3103ArticleId { - /** @var string */ #[Column(name: 'name', type: 'string', length: 255)] - protected $nameValue; + protected string $nameValue; } diff --git a/tests/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Tests/ORM/Mapping/ClassMetadataTest.php index 38b2bde7ca9..60b1bc1822f 100644 --- a/tests/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Tests/ORM/Mapping/ClassMetadataTest.php @@ -56,6 +56,7 @@ use stdClass; use function assert; +use function class_exists; use function count; use function serialize; use function str_contains; @@ -979,6 +980,10 @@ public function testCanInstantiateInternalPhpClassSubclassFromUnserializedMetada public function testWakeupReflectionWithEmbeddableAndStaticReflectionService(): void { + if (! class_exists(StaticReflectionService::class)) { + self::markTestSkipped('This test is not supported by the current installed doctrine/persistence version'); + } + $classMetadata = new ClassMetadata(TestEntity1::class); $classMetadata->mapEmbedded(