Skip to content

Commit

Permalink
Implement compatibility with Persistence 4
Browse files Browse the repository at this point in the history
  • Loading branch information
greg0ire committed Jun 27, 2024
1 parent efe62e3 commit 253acf5
Show file tree
Hide file tree
Showing 16 changed files with 158 additions and 29 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"doctrine/inflector": "^1.4 || ^2.0",
"doctrine/instantiator": "^1.3 || ^2",
"doctrine/lexer": "^3",
"doctrine/persistence": "^3.3.1",
"doctrine/persistence": "4.0.x-dev",
"psr/cache": "^1 || ^2 || ^3",
"symfony/console": "^5.4 || ^6.0 || ^7.0",
"symfony/var-exporter": "^6.3.9 || ^7.0"
Expand Down
2 changes: 2 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
</rule>

<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
<exclude-pattern>src/Mapping/Driver/LoadMappingFileImplementation.php</exclude-pattern>
<exclude-pattern>src/Mapping/GetReflectionClassImplementation.php</exclude-pattern>
<exclude-pattern>tests/*</exclude-pattern>
</rule>

Expand Down
10 changes: 10 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,21 @@ 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
path: src/Mapping/ClassMetadataFactory.php

-
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\Driver\\\\XmlDriver\\:\\:doLoadMappingFile\\(\\) is unused\\.$#"
count: 1
path: src/Mapping/Driver/XmlDriver.php

-
message: "#^Method Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\:\\:fromMappingArray\\(\\) should return static\\(Doctrine\\\\ORM\\\\Mapping\\\\ToOneOwningSideMapping\\) but returns Doctrine\\\\ORM\\\\Mapping\\\\ManyToOneAssociationMapping\\|Doctrine\\\\ORM\\\\Mapping\\\\OneToOneOwningSideMapping\\.$#"
count: 1
Expand Down
3 changes: 3 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ includes:
- phpstan-params.neon

parameters:
excludePaths:
- src/Mapping/Driver/LoadMappingFileImplementation.php
- src/Mapping/GetReflectionClassImplementation.php
ignoreErrors:
# Symfony cache supports passing a key prefix to the clear method.
- '/^Method Psr\\Cache\\CacheItemPoolInterface\:\:clear\(\) invoked with 1 parameter, 0 required\.$/'
Expand Down
23 changes: 17 additions & 6 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -300,15 +300,9 @@
<code><![CDATA[$mapping]]></code>
<code><![CDATA[$overrideMapping]]></code>
</InvalidArgument>
<InvalidNullableReturnType>
<code><![CDATA[ReflectionClass|null]]></code>
</InvalidNullableReturnType>
<InvalidPropertyAssignmentValue>
<code><![CDATA[$definition]]></code>
</InvalidPropertyAssignmentValue>
<NullableReturnStatement>
<code><![CDATA[$this->reflClass]]></code>
</NullableReturnStatement>
<ParamNameMismatch>
<code><![CDATA[$entity]]></code>
</ParamNameMismatch>
Expand Down Expand Up @@ -342,6 +336,7 @@
<RedundantCondition>
<code><![CDATA[$mapping !== false]]></code>
<code><![CDATA[$mapping !== false]]></code>
<code><![CDATA[$this->reflClass]]></code>
</RedundantCondition>
<RedundantFunctionCall>
<code><![CDATA[array_values]]></code>
Expand Down Expand Up @@ -475,6 +470,14 @@
<code><![CDATA[getIndexes]]></code>
</PossiblyNullReference>
</file>
<file src="src/Mapping/Driver/LoadMappingFileImplementation.php">
<LessSpecificImplementedReturnType>
<code><![CDATA[array]]></code>
</LessSpecificImplementedReturnType>
<MissingParamType>
<code><![CDATA[$file]]></code>
</MissingParamType>
</file>
<file src="src/Mapping/Driver/SimplifiedXmlDriver.php">
<MissingParamType>
<code><![CDATA[$fileExtension]]></code>
Expand Down Expand Up @@ -527,6 +530,14 @@
<code><![CDATA[$metadata]]></code>
</MoreSpecificImplementedParamType>
</file>
<file src="src/Mapping/GetReflectionClassImplementation.php">
<ImplementedReturnTypeMismatch>
<code><![CDATA[ReflectionClass]]></code>
</ImplementedReturnTypeMismatch>
<InvalidNullableReturnType>
<code><![CDATA[ReflectionClass]]></code>
</InvalidNullableReturnType>
</file>
<file src="src/Mapping/ManyToManyInverseSideMapping.php">
<PropertyNotSetInConstructor>
<code><![CDATA[ManyToManyInverseSideMapping]]></code>
Expand Down
4 changes: 2 additions & 2 deletions src/EntityManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -560,9 +560,9 @@ public function initializeObject(object $obj): void
/**
* {@inheritDoc}
*/
public function isUninitializedObject($obj): bool
public function isUninitializedObject($value): bool

Check warning on line 563 in src/EntityManager.php

View check run for this annotation

Codecov / codecov/patch

src/EntityManager.php#L563

Added line #L563 was not covered by tests
{
return $this->unitOfWork->isUninitializedObject($obj);
return $this->unitOfWork->isUninitializedObject($value);

Check warning on line 565 in src/EntityManager.php

View check run for this annotation

Codecov / codecov/patch

src/EntityManager.php#L565

Added line #L565 was not covered by tests
}

public function getFilters(): FilterCollection
Expand Down
16 changes: 15 additions & 1 deletion src/Exception/NotSupported.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@

use function sprintf;

/** @deprecated */
final class NotSupported extends LogicException implements ORMException
{
/** @deprecated */
public static function create(): self
{
return new self('This behaviour is (currently) not supported by Doctrine 2');
}

/** @deprecated */
public static function createForDbal3(string $context): self
{
return new self(sprintf(
Expand All @@ -29,6 +30,7 @@ public static function createForDbal3(string $context): self
));
}

/** @deprecated */
public static function createForPersistence3(string $context): self
{
return new self(sprintf(
Expand All @@ -41,4 +43,16 @@ public static function createForPersistence3(string $context): self
$context,
));
}

public static function createForPersistence4(string $context): self

Check warning on line 47 in src/Exception/NotSupported.php

View check run for this annotation

Codecov / codecov/patch

src/Exception/NotSupported.php#L47

Added line #L47 was not covered by tests
{
return new self(sprintf(
<<<'EXCEPTION'

Check warning on line 50 in src/Exception/NotSupported.php

View check run for this annotation

Codecov / codecov/patch

src/Exception/NotSupported.php#L49-L50

Added lines #L49 - L50 were not covered by tests
Context: %s
Problem: Feature was deprecated in doctrine/persistence 3.x and is not supported by installed doctrine/persistence:4.x
Solution: See the doctrine/deprecations logs for new alternative approaches.
EXCEPTION,
$context,
));

Check warning on line 56 in src/Exception/NotSupported.php

View check run for this annotation

Codecov / codecov/patch

src/Exception/NotSupported.php#L54-L56

Added lines #L54 - L56 were not covered by tests
}
}
12 changes: 2 additions & 10 deletions src/Mapping/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
*/
class ClassMetadata implements PersistenceClassMetadata, Stringable
{
use GetReflectionClassImplementation;

/* The inheritance mapping types */
/**
* NONE means the class does not participate in an inheritance hierarchy
Expand Down Expand Up @@ -929,16 +931,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
{
Expand Down
7 changes: 7 additions & 0 deletions src/Mapping/Driver/DatabaseDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@
use Doctrine\DBAL\Types\Types;
use Doctrine\Inflector\Inflector;
use Doctrine\Inflector\InflectorFactory;
use Doctrine\ORM\Exception\NotSupported;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata;
use Doctrine\Persistence\Mapping\Driver\MappingDriver;
use Doctrine\Persistence\Mapping\StaticReflectionService;
use InvalidArgumentException;
use TypeError;

use function array_diff;
use function array_keys;
use function array_merge;
use function assert;
use function class_exists;
use function count;
use function current;
use function get_debug_type;
Expand Down Expand Up @@ -79,6 +82,10 @@ class DatabaseDriver implements MappingDriver

public function __construct(private readonly AbstractSchemaManager $sm)
{
if (! class_exists(StaticReflectionService::class)) {
throw NotSupported::createForPersistence4('StaticReflectionService');

Check warning on line 86 in src/Mapping/Driver/DatabaseDriver.php

View check run for this annotation

Codecov / codecov/patch

src/Mapping/Driver/DatabaseDriver.php#L85-L86

Added lines #L85 - L86 were not covered by tests
}

$this->inflector = InflectorFactory::create()->build();
}

Expand Down
35 changes: 35 additions & 0 deletions src/Mapping/Driver/LoadMappingFileImplementation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Doctrine\ORM\Mapping\Driver;

use Doctrine\Persistence\Mapping\StaticReflectionService;

use function class_exists;

if (class_exists(StaticReflectionService::class)) {
/** @internal */
trait LoadMappingFileImplementation
{
/**
* {@inheritDoc}
*/
protected function loadMappingFile($file)

Check failure on line 18 in src/Mapping/Driver/LoadMappingFileImplementation.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (3.8.2, phpstan-dbal3.neon)

Return type mixed of method Doctrine\ORM\Mapping\Driver\XmlDriver::loadMappingFile() is not covariant with return type array of method Doctrine\Persistence\Mapping\Driver\FileDriver::loadMappingFile().
{
return $this->doLoadMappingFile($file);

Check warning on line 20 in src/Mapping/Driver/LoadMappingFileImplementation.php

View check run for this annotation

Codecov / codecov/patch

src/Mapping/Driver/LoadMappingFileImplementation.php#L20

Added line #L20 was not covered by tests
}
}
} else {
/** @internal */
trait LoadMappingFileImplementation
{
/**
* {@inheritDoc}
*/
protected function loadMappingFile($file): array
{
return $this->doLoadMappingFile($file);
}
}
}
8 changes: 4 additions & 4 deletions src/Mapping/Driver/XmlDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
*/
class XmlDriver extends FileDriver
{
use LoadMappingFileImplementation;

public const DEFAULT_FILE_EXTENSION = '.dcm.xml';

/**
Expand Down Expand Up @@ -876,10 +878,8 @@ private function getCascadeMappings(SimpleXMLElement $cascadeElement): array
return $cascades;
}

/**
* {@inheritDoc}
*/
protected function loadMappingFile($file)
/** @return array<class-string, SimpleXMLElement> */
private function doLoadMappingFile(string $file): array
{
$this->validateMapping($file);
$result = [];
Expand Down
33 changes: 33 additions & 0 deletions src/Mapping/GetReflectionClassImplementation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Doctrine\ORM\Mapping;

use Doctrine\Persistence\Mapping\StaticReflectionService;
use ReflectionClass;

use function class_exists;

if (class_exists(StaticReflectionService::class)) {

Check warning on line 12 in src/Mapping/GetReflectionClassImplementation.php

View check run for this annotation

Codecov / codecov/patch

src/Mapping/GetReflectionClassImplementation.php#L12

Added line #L12 was not covered by tests
trait GetReflectionClassImplementation
{
/**
* {@inheritDoc}
*
* Can return null when using static reflection, in violation of the LSP
*/
public function getReflectionClass(): ReflectionClass|null

Check failure on line 20 in src/Mapping/GetReflectionClassImplementation.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (3.8.2, phpstan-dbal3.neon)

Return type ReflectionClass|null of method Doctrine\ORM\Mapping\ClassMetadata::getReflectionClass() is not covariant with return type ReflectionClass of method Doctrine\Persistence\Mapping\ClassMetadata::getReflectionClass().
{
return $this->reflClass;

Check warning on line 22 in src/Mapping/GetReflectionClassImplementation.php

View check run for this annotation

Codecov / codecov/patch

src/Mapping/GetReflectionClassImplementation.php#L22

Added line #L22 was not covered by tests
}
}
} else {
trait GetReflectionClassImplementation
{
public function getReflectionClass(): ReflectionClass
{
return $this->reflClass;
}
}
}
6 changes: 3 additions & 3 deletions tests/Tests/Mocks/MetadataDriverMock.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,22 @@ 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;
}

/**
* {@inheritDoc}
*/
public function getAllClassNames()
public function getAllClassNames(): array
{
return [];
}
Expand Down
9 changes: 9 additions & 0 deletions tests/Tests/ORM/Functional/DatabaseDriverTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\Driver\DatabaseDriver;
use Doctrine\Persistence\Mapping\StaticReflectionService;
use Doctrine\Tests\OrmFunctionalTestCase;

use function array_keys;
use function array_map;
use function class_exists;
use function count;
use function implode;
use function in_array;
Expand All @@ -20,6 +22,13 @@
*/
abstract class DatabaseDriverTestCase 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');
}
}

/** @psalm-return array<string, ClassMetadata> */
protected function convertToClassMetadata(array $entityTables, array $manyTables = []): array
{
Expand Down
12 changes: 10 additions & 2 deletions tests/Tests/ORM/Functional/Ticket/DDC3103Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,26 @@
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;

#[CoversClass(ClassMetadata::class)]
#[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');
}
}

public function testIssue(): void
{
$classMetadata = new ClassMetadata(DDC3103ArticleId::class);
Expand All @@ -39,7 +48,6 @@ public function testIssue(): void
#[Embeddable]
class DDC3103ArticleId
{
/** @var string */
#[Column(name: 'name', type: 'string', length: 255)]
protected $nameValue;
protected string $nameValue;
}
Loading

0 comments on commit 253acf5

Please sign in to comment.