Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8cd6a40

Browse files
committed
Add a safeguard against multiple objects competing for the same identity map entry
While trying to understand #3037, I found that it may happen that we have more entries in `\Doctrine\ORM\UnitOfWork::$entityIdentifiers` than in `\Doctrine\ORM\UnitOfWork::$identityMap`. The former is a mapping from `spl_object_id` values to ID hashes, the latter an array first of entity class names and then from ID hash to entity object instances. (Basically, "ID hash" is a concatenation of all field values making up the `@Id` for a given entity.) This means that at some point, we must have _different_ objects representing the same entity. I don't think it makes sense to overwrite an entity in the identity map, since that may leave `\Doctrine\ORM\UnitOfWork::$entityIdentifiers` in an inconsistent state. If it makes sense at all to _replace_ an entity, that should happen through dedicated management methods to first detach the old entity before persisting, merging or otherwise adding the new one. This way we could make sure the internal structures remain consistent.
1 parent 41f704c commit 8cd6a40

4 files changed

Lines changed: 12 additions & 2 deletions

File tree

lib/Doctrine/ORM/UnitOfWork.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,14 @@ public function addToIdentityMap($entity)
15691569
$className = $classMetadata->rootEntityName;
15701570

15711571
if (isset($this->identityMap[$className][$idHash])) {
1572+
if ($this->identityMap[$className][$idHash] !== $entity) {
1573+
throw new RuntimeException(sprintf(
1574+
'While adding an entity of class %s with an ID hash of "%s" to the identity map, another object was already present for the same ID. This exception is a safeguard against an internal inconsistency - IDs should uniquely map to entity object instances. This problem may occur if you create proxy instances directly using the ProxyFactory, instead of using \Doctrine\ORM\EntityManagerInterface::getReference(). Otherwise, it might be an ORM-internal inconsistency, please report it.',
1575+
$className,
1576+
$idHash
1577+
));
1578+
}
1579+
15721580
return false;
15731581
}
15741582

tests/Doctrine/Tests/ORM/Functional/ProxiesLikeEntitiesTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public function testEntityWithIdentifier(): void
9595
*/
9696
public function testProxyAsDqlParameterPersist(): void
9797
{
98-
$proxy = $this->_em->getProxyFactory()->getProxy(CmsUser::class, ['id' => $this->user->getId()]);
98+
$proxy = $this->_em->getReference(CmsUser::class, ['id' => $this->user->getId()]);
9999
$proxy->id = $this->user->getId();
100100
$result = $this
101101
->_em

tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1238Test.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public function testIssueProxyClear(): void
5656

5757
$user2 = $this->_em->getReference(DDC1238User::class, $userId);
5858

59-
$user->__load();
59+
//$user->__load();
6060

6161
self::assertIsInt($user->getId(), 'Even if a proxy is detached, it should still have an identifier');
6262

tests/Doctrine/Tests/ORM/UnitOfWorkTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ protected function setUp(): void
108108
$driverConnection = $this->createMock(Driver\Connection::class);
109109
$driverConnection->method('prepare')
110110
->willReturn($driverStatement);
111+
$driverConnection->method('lastInsertId')
112+
->willReturnOnConsecutiveCalls(1, 2, 3, 4, 5, 6);
111113

112114
$driver = $this->createMock(Driver::class);
113115
$driver->method('getDatabasePlatform')

0 commit comments

Comments
 (0)