From 38bf6c665a05f4f7afa1d6e2a83149fa447b0f92 Mon Sep 17 00:00:00 2001
From: romanb <romanb@625475ce-881a-0410-a577-b389adb331d8>
Date: Tue, 5 Jan 2010 11:45:38 +0000
Subject: [PATCH] [2.0][DDC-237][DDC-216] Fixed. If you're using manual proxy
 generation through the CLI, please regenerate your proxies.

---
 lib/Doctrine/ORM/PersistentCollection.php     |  14 ++-
 lib/Doctrine/ORM/Proxy/Proxy.php              |   5 +-
 lib/Doctrine/ORM/Proxy/ProxyFactory.php       |   9 +-
 lib/Doctrine/ORM/UnitOfWork.php               |  14 ++-
 .../ORM/Functional/DefaultValuesTest.php      |   4 +-
 .../ORM/Functional/Ticket/DDC237Test.php      | 112 ++++++++++++++++++
 6 files changed, 137 insertions(+), 21 deletions(-)
 create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/DDC237Test.php

diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php
index 2c5f374e8..93e875c5c 100644
--- a/lib/Doctrine/ORM/PersistentCollection.php
+++ b/lib/Doctrine/ORM/PersistentCollection.php
@@ -571,6 +571,15 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect
         $this->_initialize();
         return $this->_coll->partition($p);
     }
+    
+    /**
+     * {@inheritdoc}
+     */
+    public function toArray()
+    {
+        $this->_initialize();
+        return $this->_coll->toArray();
+    }
 
     /**
      * {@inheritdoc}
@@ -637,11 +646,6 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect
         return $this->remove($offset);
     }
     
-    public function toArray()
-    {
-        return $this->_coll->toArray();
-    }
-    
     public function key()
     {
         return $this->_coll->key();
diff --git a/lib/Doctrine/ORM/Proxy/Proxy.php b/lib/Doctrine/ORM/Proxy/Proxy.php
index 904e7d4ef..853f9c1f0 100644
--- a/lib/Doctrine/ORM/Proxy/Proxy.php
+++ b/lib/Doctrine/ORM/Proxy/Proxy.php
@@ -27,7 +27,4 @@ namespace Doctrine\ORM\Proxy;
  * @author Roman Borschel <roman@code-factory.org>
  * @since 2.0
  */
-interface Proxy
-{
-    function __isInitialized__();
-}
\ No newline at end of file
+interface Proxy {}
\ No newline at end of file
diff --git a/lib/Doctrine/ORM/Proxy/ProxyFactory.php b/lib/Doctrine/ORM/Proxy/ProxyFactory.php
index 9a2812f27..b9e53d611 100644
--- a/lib/Doctrine/ORM/Proxy/ProxyFactory.php
+++ b/lib/Doctrine/ORM/Proxy/ProxyFactory.php
@@ -253,26 +253,25 @@ namespace <namespace> {
     class <proxyClassName> extends \<className> implements \Doctrine\ORM\Proxy\Proxy {
         private $_entityPersister;
         private $_identifier;
-        private $_loaded = false;
+        public $__isInitialized__ = false;
         public function __construct($entityPersister, $identifier) {
             $this->_entityPersister = $entityPersister;
             $this->_identifier = $identifier;
             <constructorInvocation>
         }
         private function _load() {
-            if ( ! $this->_loaded) {
-                $this->_loaded = true;
+            if (!$this->__isInitialized__) {
+                $this->__isInitialized__ = true;
                 $this->_entityPersister->load($this->_identifier, $this);
                 unset($this->_entityPersister);
                 unset($this->_identifier);
             }
         }
-        public function __isInitialized__() { return $this->_loaded; }
 
         <methods>
 
         public function __sleep() {
-            if (!$this->_loaded) {
+            if (!$this->__isInitialized__) {
                 throw new \RuntimeException("Not fully loaded proxy can not be serialized.");
             }
             <sleepImpl>
diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php
index c970cec31..eb1cbc6c7 100644
--- a/lib/Doctrine/ORM/UnitOfWork.php
+++ b/lib/Doctrine/ORM/UnitOfWork.php
@@ -38,8 +38,7 @@ use Doctrine\Common\Collections\ArrayCollection,
  * @since       2.0
  * @version     $Revision$
  * @author      Roman Borschel <roman@code-factory.org>
- * @internal    This class contains performance-critical code. Work with care and
- *              regularly run the ORM performance tests.
+ * @internal    This class contains performance-critical code.
  */
 class UnitOfWork implements PropertyChangedListener
 {
@@ -401,7 +400,7 @@ class UnitOfWork implements PropertyChangedListener
 
             foreach ($entitiesToProcess as $entity) {
                 // Ignore uninitialized proxy objects
-                if (/* $entity is readOnly || */ $entity instanceof Proxy && ! $entity->__isInitialized__()) {
+                if (/* $entity is readOnly || */ $entity instanceof Proxy && ! $entity->__isInitialized__) {
                     continue;
                 }
                 // Only MANAGED entities that are NOT SCHEDULED FOR INSERTION are processed here.
@@ -560,7 +559,7 @@ class UnitOfWork implements PropertyChangedListener
         // Look through the entities, and in any of their associations, for transient
         // enities, recursively. ("Persistence by reachability")
         if ($assoc->isOneToOne()) {
-            if ($value instanceof Proxy && ! $value->__isInitialized__()) {
+            if ($value instanceof Proxy && ! $value->__isInitialized__) {
                 return; // Ignore uninitialized proxy objects
             }
             $value = array($value);
@@ -1732,7 +1731,12 @@ class UnitOfWork implements PropertyChangedListener
         if (isset($this->_identityMap[$class->rootEntityName][$idHash])) {
             $entity = $this->_identityMap[$class->rootEntityName][$idHash];
             $oid = spl_object_hash($entity);
-            $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
+            if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
+                $entity->__isInitialized__ = true;
+                $overrideLocalValues = true;
+            } else {
+                $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
+            }
         } else {
             //$entity = clone $class->prototype;
             $entity = new $className;
diff --git a/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php b/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php
index 0878e1aea..71edf4dc2 100644
--- a/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php
+++ b/tests/Doctrine/Tests/ORM/Functional/DefaultValuesTest.php
@@ -34,7 +34,7 @@ class DefaultValuesTest extends \Doctrine\Tests\OrmFunctionalTestCase
         $user2 = $this->_em->getReference(get_class($user), $userId);
         
         $this->_em->flush();
-        $this->assertFalse($user2->__isInitialized__());
+        $this->assertFalse($user2->__isInitialized__);
         
         $a = new DefaultValueAddress;
         $a->country = 'de';
@@ -46,7 +46,7 @@ class DefaultValuesTest extends \Doctrine\Tests\OrmFunctionalTestCase
         $this->_em->persist($a);
         $this->_em->flush();
         
-        $this->assertFalse($user2->__isInitialized__());
+        $this->assertFalse($user2->__isInitialized__);
         $this->_em->clear();
         
         $a2 = $this->_em->find(get_class($a), $a->id);
diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC237Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC237Test.php
new file mode 100644
index 000000000..877b3b861
--- /dev/null
+++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC237Test.php
@@ -0,0 +1,112 @@
+<?php
+
+namespace Doctrine\Tests\ORM\Functional\Ticket;
+
+require_once __DIR__ . '/../../../TestInit.php';
+
+class DDC237Test extends \Doctrine\Tests\OrmFunctionalTestCase
+{
+    protected function setUp()
+    {
+        parent::setUp();
+        $this->_schemaTool->createSchema(array(
+            $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC237EntityX'),
+            $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC237EntityY'),
+            $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC237EntityZ')
+        ));
+    }
+    
+    public function testUninitializedProxyIsInitializedOnFetchJoin()
+    {
+        $x = new DDC237EntityX;
+        $y = new DDC237EntityY;
+        $z = new DDC237EntityZ;
+        
+        $x->data = 'X';
+        $y->data = 'Y';
+        $z->data = 'Z';
+        
+        $x->y = $y;
+        $z->y = $y;
+        
+        $this->_em->persist($x);
+        $this->_em->persist($y);
+        $this->_em->persist($z);
+        
+        $this->_em->flush();        
+        $this->_em->clear();
+        
+        $x2 = $this->_em->find(get_class($x), $x->id); // proxy injected for Y
+        $this->assertTrue($x2->y instanceof \Doctrine\ORM\Proxy\Proxy);
+        $this->assertFalse($x2->y->__isInitialized__);
+        
+        // proxy for Y is in identity map
+        
+        $z2 = $this->_em->createQuery('select z,y from ' . get_class($z) . ' z join z.y y where z.id = ?1')
+                ->setParameter(1, $z->id)
+                ->getSingleResult();
+        $this->assertTrue($z2->y instanceof \Doctrine\ORM\Proxy\Proxy);
+        $this->assertTrue($z2->y->__isInitialized__);
+        $this->assertEquals('Y', $z2->y->data);
+        $this->assertEquals($y->id, $z2->y->id);
+        
+        // since the Y is the same, the instance from the identity map is
+        // used, even if it is a proxy.
+
+        $this->assertNotSame($x, $x2);
+        $this->assertNotSame($z, $z2);
+        $this->assertSame($z2->y, $x2->y);
+        $this->assertTrue($z2->y instanceof \Doctrine\ORM\Proxy\Proxy);
+        
+    }
+}
+
+
+/**
+ * @Entity @Table(name="ddc237_x")
+ */
+class DDC237EntityX
+{ 
+    /**
+     * @Id @Column(type="integer") @GeneratedValue(strategy="AUTO")
+     */
+    public $id;
+    /**
+     * @Column(type="string")
+     */
+    public $data;
+    /**
+     * @OneToOne(targetEntity="DDC237EntityY")
+     * @JoinColumn(name="y_id", referencedColumnName="id")
+     */
+    public $y;
+}
+
+
+/** @Entity @Table(name="ddc237_y") */
+class DDC237EntityY
+{
+    /**
+     * @Id @Column(type="integer") @GeneratedValue(strategy="AUTO")
+     */
+    public $id;
+    /**
+     * @Column(type="string")
+     */
+    public $data;
+}
+
+/** @Entity @Table(name="ddc237_z") */
+class DDC237EntityZ
+{
+    /** @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") */
+    public $id;
+    /** @Column(type="string") */
+    public $data;
+    
+    /**
+     * @OneToOne(targetEntity="DDC237EntityY")
+     * @JoinColumn(name="y_id", referencedColumnName="id")
+     */
+    public $y;
+}
\ No newline at end of file