diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php index de67e2f86..aa1e6a8e8 100644 --- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php +++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php @@ -25,6 +25,7 @@ use Doctrine\ORM\EntityNotFoundException; use Doctrine\Common\Util\ClassUtils; use Doctrine\Common\Proxy\Proxy; use Doctrine\Common\Proxy\ProxyGenerator; +use Doctrine\ORM\ORMInvalidArgumentException; /** * This factory is used to create proxy objects for entities at runtime. @@ -123,6 +124,31 @@ class ProxyFactory return $proxy; } + /** + * @param \Doctrine\Common\Proxy\Proxy $proxy + * + * @return \Doctrine\Common\Proxy\Proxy + * + * @throws \Doctrine\ORM\ORMInvalidArgumentException + */ + public function resetUninitializedProxy(Proxy $proxy) + { + if ($proxy->__isInitialized()) { + throw new ORMInvalidArgumentException('Provided proxy must not be initialized'); + } + + $className = $this->em->getClassMetadata(get_class($proxy))->getName(); + + if ( ! isset($this->definitions[$className])) { + $this->initProxyDefinitions($className); + } + + $proxy->__setInitializer($this->definitions[$className]['initializer']); + $proxy->__setCloner($this->definitions[$className]['cloner']); + + return $proxy; + } + /** * Generates proxy classes for all given classes. * @@ -179,8 +205,9 @@ class ProxyFactory */ private function initProxyDefinitions($className) { - $fqcn = ClassUtils::generateProxyClassName($className, $this->proxyNs); $classMetadata = $this->em->getClassMetadata($className); + $className = $classMetadata->getName(); + $fqcn = ClassUtils::generateProxyClassName($className, $this->proxyNs); if ( ! class_exists($fqcn, false)) { $generator = $this->getProxyGenerator(); diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 1fa7676bb..35f677fc5 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1769,7 +1769,8 @@ class UnitOfWork implements PropertyChangedListener $managedCopy = $entity; if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) { - if ($entity instanceof Proxy && ! $entity->__isInitialized__) { + if ($entity instanceof Proxy && ! $entity->__isInitialized()) { + $this->em->getProxyFactory()->resetUninitializedProxy($entity); $entity->__load(); } diff --git a/lib/vendor/doctrine-common b/lib/vendor/doctrine-common index c2b45fdb2..0aa165610 160000 --- a/lib/vendor/doctrine-common +++ b/lib/vendor/doctrine-common @@ -1 +1 @@ -Subproject commit c2b45fdb2757492e75abaab119164aaf311cd395 +Subproject commit 0aa165610e2fdd6617e0e2b91c35fedf92aea2f7 diff --git a/tests/Doctrine/Tests/Models/DDC1734/DDC1734Article.php b/tests/Doctrine/Tests/Models/DDC1734/DDC1734Article.php new file mode 100644 index 000000000..b018da840 --- /dev/null +++ b/tests/Doctrine/Tests/Models/DDC1734/DDC1734Article.php @@ -0,0 +1,37 @@ +name = $name; + } + + public function getId() + { + return $this->id; + } + + public function getName() + { + return $this->name; + } + +} diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1734Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1734Test.php new file mode 100644 index 000000000..d5748a5aa --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1734Test.php @@ -0,0 +1,78 @@ +useModelSet('ddc1734'); + parent::setUp(); + } + + /** + * This test is DDC-1734 minus the serialization, i.e. it works + * @group DDC-1734 + */ + public function testMergeWorksOnNonSerializedProxies() + { + $article = new DDC1734Article("Foo"); + $this->_em->persist($article); + $this->_em->flush(); + // Get a proxy of the entity + $this->_em->clear(); + $proxy = $this->getProxy($article); + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $proxy); + $this->assertFalse($proxy->__isInitialized__); + // Detach + $this->_em->detach($proxy); + $this->_em->clear(); + // Merge + $proxy = $this->_em->merge($proxy); + $this->assertEquals("Foo", $proxy->getName(), "The entity is broken"); + } + + /** + * This test reproduces DDC-1734 which is: + * - A non-initialized proxy is detached and serialized (the identifier of the proxy is *not* serialized) + * - the object is deserialized and merged (to turn into an entity) + * - the entity is broken because it has no identifier and no field defined + * @group DDC-1734 + */ + public function testMergeWorksOnSerializedProxies() + { + $article = new DDC1734Article("Foo"); + $this->_em->persist($article); + $this->_em->flush(); + // Get a proxy of the entity + $this->_em->clear(); + $proxy = $this->getProxy($article); + $this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $proxy); + $this->assertFalse($proxy->__isInitialized()); + // Detach and serialize + $this->_em->detach($proxy); + $serializedProxy = serialize($proxy); + $this->_em->clear(); + // Unserialize and merge + /** @var $unserializedProxy DDC1734Article */ + $unserializedProxy = unserialize($serializedProxy); + // Merge + $unserializedProxy = $this->_em->merge($unserializedProxy); + $this->assertEquals("Foo", $unserializedProxy->getName(), "The entity is broken"); + } + + private function getProxy($object) + { + $metadataFactory = $this->_em->getMetadataFactory(); + $identifier = $metadataFactory->getMetadataFor(get_class($object))->getIdentifierValues($object); + $proxyFactory = $this->_em->getProxyFactory(); + + return $proxyFactory->getProxy(get_class($object), $identifier); + } + +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/OrmFunctionalTestCase.php b/tests/Doctrine/Tests/OrmFunctionalTestCase.php index 715c32a5f..a16cba368 100644 --- a/tests/Doctrine/Tests/OrmFunctionalTestCase.php +++ b/tests/Doctrine/Tests/OrmFunctionalTestCase.php @@ -134,6 +134,9 @@ abstract class OrmFunctionalTestCase extends OrmTestCase 'Doctrine\Tests\Models\DDC117\DDC117Editor', 'Doctrine\Tests\Models\DDC117\DDC117Link', ), + 'ddc1734' => array( + 'Doctrine\Tests\Models\DDC1734\DDC1734Article', + ), 'stockexchange' => array( 'Doctrine\Tests\Models\StockExchange\Bond', 'Doctrine\Tests\Models\StockExchange\Stock',