Skip to content

Commit

Permalink
Avoid returning null from EntityManager::getReference() and `getPar…
Browse files Browse the repository at this point in the history
…tialReference()`
  • Loading branch information
simPod committed Aug 1, 2023
1 parent 2dc19e6 commit 5599b3f
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 9 deletions.
4 changes: 4 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,10 @@ following classes and methods:

Use `toIterable()` instead.

## BC BREAK: `Doctrine\ORM\EntityManagerInterface#getReference()` and `getPartialReference()` does not return `null` anymore

Method `Doctrine\ORM\EntityManagerInterface#getReference()` and `getPartialReference()` throws `InstanceOfTheWrongTypeEncountered` in 3.0 when different entity type is found in inheritance hierachy.

# Upgrade to 2.16

## Changing the way how reflection-based mapping drivers report fields, deprecated the "old" mode
Expand Down
13 changes: 11 additions & 2 deletions lib/Doctrine/ORM/EntityManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\LockMode;
use Doctrine\ORM\Exception\EntityManagerClosed;
use Doctrine\ORM\Exception\InstanceOfTheWrongTypeEncountered;
use Doctrine\ORM\Exception\InvalidHydrationMode;
use Doctrine\ORM\Exception\MissingIdentifierField;
use Doctrine\ORM\Exception\MissingMappingDriverImplementation;
Expand Down Expand Up @@ -386,7 +387,11 @@ public function getReference(string $entityName, mixed $id): object|null

// Check identity map first, if its already in there just return it.
if ($entity !== false) {
return $entity instanceof $class->name ? $entity : null;
if (! $entity instanceof $class->name) {
throw InstanceOfTheWrongTypeEncountered::forInstance($entity);
}

return $entity;
}

if ($class->subClasses) {
Expand All @@ -408,7 +413,11 @@ public function getPartialReference(string $entityName, mixed $identifier): obje

// Check identity map first, if its already in there just return it.
if ($entity !== false) {
return $entity instanceof $class->name ? $entity : null;
if (! $entity instanceof $class->name) {
throw InstanceOfTheWrongTypeEncountered::forInstance($entity);
}

return $entity;

Check warning on line 420 in lib/Doctrine/ORM/EntityManager.php

View check run for this annotation

Codecov / codecov/patch

lib/Doctrine/ORM/EntityManager.php#L420

Added line #L420 was not covered by tests
}

if (! is_array($identifier)) {
Expand Down
4 changes: 2 additions & 2 deletions lib/Doctrine/ORM/EntityManagerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public function refresh(object $object, LockMode|int|null $lockMode = null): voi
* @param mixed $id The entity identifier.
* @psalm-param class-string<T> $entityName
*
* @psalm-return T|null
* @psalm-return T
*
* @throws ORMException
*
Expand All @@ -182,7 +182,7 @@ public function getReference(string $entityName, mixed $id): object|null;
* @param mixed $identifier The entity identifier.
* @psalm-param class-string<T> $entityName
*
* @psalm-return T|null
* @psalm-return T
*
* @template T of object
*/
Expand Down
19 changes: 19 additions & 0 deletions lib/Doctrine/ORM/Exception/InstanceOfTheWrongTypeEncountered.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Doctrine\ORM\Exception;

use LogicException;

use function get_debug_type;

final class InstanceOfTheWrongTypeEncountered extends LogicException implements ORMException
{
/** @param object $instance */
public static function forInstance($instance): self
{
return new self('Instance of the wrong type (' . get_debug_type($instance) . ') was retrieved in inheritance hierachy.' .
'This happens because identity map aggregates instances by rootEntityName ');
}
}
4 changes: 2 additions & 2 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@
<code>$entity</code>
<code>$entity</code>
<code>$entity</code>
<code><![CDATA[$entity instanceof $class->name ? $entity : null]]></code>
<code><![CDATA[$entity instanceof $class->name ? $entity : null]]></code>
<code><![CDATA[$entity]]></code>
<code><![CDATA[$entity]]></code>
<code><![CDATA[$persister->load($sortedId, null, null, [], $lockMode)]]></code>
<code><![CDATA[$persister->loadById($sortedId)]]></code>
<code><![CDATA[$this->metadataFactory]]></code>
Expand Down
15 changes: 12 additions & 3 deletions tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1041Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\ORM\Exception\InstanceOfTheWrongTypeEncountered;
use Doctrine\Tests\Models\Company\CompanyFixContract;
use Doctrine\Tests\Models\Company\CompanyFlexContract;
use Doctrine\Tests\OrmFunctionalTestCase;
Expand All @@ -19,7 +20,7 @@ protected function setUp(): void
parent::setUp();
}

public function testGrabWrongSubtypeReturnsNull(): void
public function testGrabWrongSubtypeReturnsNullOrThrows(): void
{
$fix = new CompanyFixContract();
$fix->setFixPrice(2000);
Expand All @@ -30,7 +31,15 @@ public function testGrabWrongSubtypeReturnsNull(): void
$id = $fix->getId();

self::assertNull($this->_em->find(CompanyFlexContract::class, $id));
self::assertNull($this->_em->getReference(CompanyFlexContract::class, $id));
self::assertNull($this->_em->getPartialReference(CompanyFlexContract::class, $id));

try {
$this->_em->getReference(CompanyFlexContract::class, $id);
} catch (InstanceOfTheWrongTypeEncountered) {
}

try {
$this->_em->getPartialReference(CompanyFlexContract::class, $id);
} catch (InstanceOfTheWrongTypeEncountered) {
}
}
}

0 comments on commit 5599b3f

Please sign in to comment.