Skip to content

Commit

Permalink
DDC-2332: Ensure managed entities are always tracked by UOW
Browse files Browse the repository at this point in the history
  • Loading branch information
bigfoot90 committed Sep 9, 2021
1 parent 7fcab3d commit 47c1b24
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
17 changes: 16 additions & 1 deletion lib/Doctrine/ORM/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,15 @@ class UnitOfWork implements PropertyChangedListener
/** @var ReflectionPropertiesGetter */
private $reflectionPropertiesGetter;

/**
* Associate entities with OIDs to ensure the GC won't recycle a managed entity
*
* DDC-2332 / #3037
*
* @var array
*/
private $oidMap = array();

/**
* Initializes a new UnitOfWork instance, bound to the given EntityManager.
*/
Expand Down Expand Up @@ -1543,7 +1552,8 @@ public function isEntityScheduled($entity)
public function addToIdentityMap($entity)
{
$classMetadata = $this->em->getClassMetadata(get_class($entity));
$identifier = $this->entityIdentifiers[spl_object_hash($entity)];
$oid = spl_object_hash($entity);
$identifier = $this->entityIdentifiers[$oid];

if (empty($identifier) || in_array(null, $identifier, true)) {
throw ORMInvalidArgumentException::entityWithoutIdentity($classMetadata->name, $entity);
Expand All @@ -1552,6 +1562,8 @@ public function addToIdentityMap($entity)
$idHash = implode(' ', $identifier);
$className = $classMetadata->rootEntityName;

$this->oidMap[$oid] = $entity;

if (isset($this->identityMap[$className][$idHash])) {
return false;
}
Expand Down Expand Up @@ -1666,6 +1678,7 @@ public function removeFromIdentityMap($entity)
}

$className = $classMetadata->rootEntityName;
unset($this->oidMap[$oid]);

if (isset($this->identityMap[$className][$idHash])) {
unset($this->identityMap[$className][$idHash]);
Expand Down Expand Up @@ -2538,6 +2551,7 @@ public function clear($entityName = null)
{
if ($entityName === null) {
$this->identityMap =
$this->oidMap =
$this->entityIdentifiers =
$this->originalEntityData =
$this->entityChangeSets =
Expand Down Expand Up @@ -2713,6 +2727,7 @@ public function createEntity($className, array $data, &$hints = [])
$this->entityIdentifiers[$oid] = $id;
$this->entityStates[$oid] = self::STATE_MANAGED;
$this->originalEntityData[$oid] = $data;
$this->oidMap[$oid] = $entity;

$this->identityMap[$class->rootEntityName][$idHash] = $entity;

Expand Down
46 changes: 46 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/IdentityMapTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Doctrine\ORM\Query;
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\OrmFunctionalTestCase;
Expand Down Expand Up @@ -257,4 +258,49 @@ public function testCollectionValuedAssociationIdentityMapBehaviorWithRefresh():
// Now the collection should be refreshed with correct count
$this->assertEquals(4, count($user2->getPhonenumbers()));
}

/**
* @group HashCollision
*/
public function testHashCollision(): void
{
$user = new CmsUser();
$user->username = 'Test';
$user->name = 'Test';
$this->_em->persist($user);
$this->_em->flush();

$articles = [];
for ($i = 0; $i < 100; $i++) {
$article = new CmsArticle();
$article->topic = "Test";
$article->text = "Test";
$article->setAuthor($this->_em->merge($user));
$this->_em->persist($article);
$this->_em->flush();
$this->_em->clear();
$articles [] = $article;
}

$user = $this->_em->merge($user);

foreach ($articles as $article) {
$article = $this->_em->merge($article);
$article->setAuthor($user);
}

unset($article);
gc_collect_cycles();
$keep = [];

for ($x = 0; $x < 1000; $x++) {
$keep[] = $article = new CmsArticle();
$article->topic = "Test";
$article->text = "Test";
$article->setAuthor($this->_em->merge($user));
$this->_em->persist($article);
$this->_em->flush();
$this->assertNotNull($article->id, "Article wasn't persisted on iteration $x");
}
}
}

0 comments on commit 47c1b24

Please sign in to comment.