Skip to content

Commit

Permalink
Completing rewrite: ProxyManager and Doctrine2 are now fully integrated
Browse files Browse the repository at this point in the history
  • Loading branch information
Ocramius committed Jan 9, 2015
1 parent b0faabd commit 9e11921
Show file tree
Hide file tree
Showing 14 changed files with 95 additions and 52 deletions.
4 changes: 2 additions & 2 deletions lib/Doctrine/ORM/Cache/DefaultQueryCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\Common\Proxy\Proxy;
use Doctrine\ORM\Cache;
use Doctrine\ORM\Query;
use ProxyManager\Proxy\GhostObjectInterface;

/**
* Default query cache implementation.
Expand Down Expand Up @@ -256,7 +256,7 @@ public function put(QueryCacheKey $key, ResultSetMapping $rsm, $result, array $h
foreach ($rsm->relationMap as $name) {
$assoc = $metadata->associationMappings[$name];

if (($assocValue = $metadata->getFieldValue($entity, $name)) === null || $assocValue instanceof Proxy) {
if (($assocValue = $metadata->getFieldValue($entity, $name)) === null || $assocValue instanceof GhostObjectInterface) {
continue;
}

Expand Down
1 change: 0 additions & 1 deletion lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Query;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Proxy\Proxy;
use ProxyManager\Proxy\GhostObjectInterface;

/**
Expand Down
82 changes: 64 additions & 18 deletions lib/Doctrine/ORM/Proxy/ProxyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@

use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\Common\Proxy\AbstractProxyFactory;
use Doctrine\Common\Proxy\Exception\InvalidArgumentException;
use Doctrine\Common\Proxy\Exception\OutOfBoundsException;
use Doctrine\Common\Proxy\ProxyDefinition;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Common\Proxy\Proxy as BaseProxy;
use Doctrine\Common\Proxy\ProxyGenerator;
use Doctrine\ORM\Persisters\EntityPersister;
use Doctrine\ORM\EntityManager;
Expand All @@ -33,6 +33,9 @@
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\Proxy\GhostObjectInterface;
use ProxyManager\ProxyGenerator\LazyLoadingGhostGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ReflectionClass;
use ReflectionProperty;

/**
* This factory is used to create proxy objects for entities at runtime.
Expand Down Expand Up @@ -92,30 +95,31 @@ public function getProxy($className, array $identifier)
$fqcn = $definition->proxyClassName;

if (! class_exists($fqcn, false)) {
$generatorStrategy = new EvaluatingGeneratorStrategy();
$proxyGenerator = new ClassGenerator();
$generatorStrategy = new EvaluatingGeneratorStrategy();
$proxyGenerator = new ClassGenerator();
$skippedProperties = array_filter(
Properties::fromReflectionClass(new ReflectionClass($className))->getInstanceProperties(),
function (ReflectionProperty $property) use ($definition) {
return ! in_array(
$property->getName(),
array_map(
function (ReflectionProperty $property) {
return $property->getName();
},
$definition->reflectionFields
)
)
|| in_array($property->getName(), $definition->identifierFields);
}
);

$proxyGenerator->setName($fqcn);

(new LazyLoadingGhostGenerator())->generate(
$this->em->getClassMetadata($className)->getReflectionClass(),
$proxyGenerator,
[
'skippedProperties' => array_map(
function (\ReflectionProperty $reflectionProperty) {
if ($reflectionProperty->isProtected()) {
return "\0*\0" . $reflectionProperty->getName();
}

if ($reflectionProperty->isPrivate()) {
return "\0" . $reflectionProperty->getDeclaringClass()->getName()
. "\0" . $reflectionProperty->getName();
}

return $reflectionProperty->getName();
},
$definition->reflectionFields
)
'skippedProperties' => array_map([$this, 'getInternalReflectionPropertyName'], $skippedProperties)
]
);

Expand All @@ -135,6 +139,29 @@ function (\ReflectionProperty $reflectionProperty) {
return $proxy;
}

/**
* Reset initialization/cloning logic for an un-initialized proxy
*
* @param GhostObjectInterface $proxy
*
* @return GhostObjectInterface
*
* @throws \Doctrine\Common\Proxy\Exception\InvalidArgumentException
*/
public function resetUninitializedProxy(GhostObjectInterface $proxy)
{
if ($proxy->isProxyInitialized()) {
throw InvalidArgumentException::unitializedProxyExpected($proxy);
}

$className = ClassUtils::getClass($proxy);
$definition = $this->createProxyDefinition($className);

$proxy->setProxyInitializer($definition->initializer);

return $proxy;
}

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -246,4 +273,23 @@ private function createCloner(ClassMetadata $classMetadata, EntityPersister $ent
}
};*/
}

/**
* @param ReflectionProperty $reflectionProperty
*
* @return string
*/
private function getInternalReflectionPropertyName(ReflectionProperty $reflectionProperty)
{
if ($reflectionProperty->isProtected()) {
return "\0*\0" . $reflectionProperty->getName();
}

if ($reflectionProperty->isPrivate()) {
return "\0" . $reflectionProperty->getDeclaringClass()->getName()
. "\0" . $reflectionProperty->getName();
}

return $reflectionProperty->getName();
}
}
4 changes: 2 additions & 2 deletions lib/Doctrine/ORM/Tools/DebugUnitOfWorkListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@

namespace Doctrine\ORM\Tools;

use Doctrine\Common\Persistence\Proxy;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\EntityManager;
use ProxyManager\Proxy\GhostObjectInterface;

/**
* Use this logger to dump the identity map during the onFlush event. This is useful for debugging
Expand Down Expand Up @@ -102,7 +102,7 @@ public function dumpIdentityMap(EntityManager $em)
if ($value === null) {
fwrite($fh, " NULL\n");
} else {
if ($value instanceof Proxy && !$value->__isInitialized()) {
if ($value instanceof GhostObjectInterface && !$value->isProxyInitialized()) {
fwrite($fh, "[PROXY] ");
}

Expand Down
15 changes: 7 additions & 8 deletions lib/Doctrine/ORM/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
use Doctrine\Common\PropertyChangedListener;
use Doctrine\Common\Persistence\ObjectManagerAware;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Proxy\Proxy;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
Expand Down Expand Up @@ -1801,9 +1800,9 @@ private function doMerge($entity, array &$visited, $prevManagedCopy = null, $ass
$managedCopy = $entity;

if ($this->getEntityState($entity, self::STATE_DETACHED) !== self::STATE_MANAGED) {
if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
if ($entity instanceof GhostObjectInterface && ! $entity->isProxyInitialized()) {
$this->em->getProxyFactory()->resetUninitializedProxy($entity);
$entity->__load();
$entity->initializeProxy();
}

// Try to look the entity up in the identity map.
Expand Down Expand Up @@ -2546,7 +2545,7 @@ public function createEntity($className, array $data, &$hints = array())
isset($hints[Query::HINT_REFRESH])
&& isset($hints[Query::HINT_REFRESH_ENTITY])
&& ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
&& $unmanagedProxy instanceof Proxy
&& $unmanagedProxy instanceof GhostObjectInterface
&& $this->isIdentifierEquals($unmanagedProxy, $entity)
) {
// DDC-1238 - we have a managed instance, but it isn't the provided one.
Expand Down Expand Up @@ -2747,7 +2746,7 @@ public function createEntity($className, array $data, &$hints = array())

if (
$newValue instanceof NotifyPropertyChanged &&
( ! $newValue instanceof Proxy || $newValue->__isInitialized())
( ! $newValue instanceof GhostObjectInterface || $newValue->isProxyInitialized())
) {
$newValue->addPropertyChangedListener($this);
}
Expand Down Expand Up @@ -3120,7 +3119,7 @@ public function registerManaged($entity, array $id, array $data)

$this->addToIdentityMap($entity);

if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof Proxy || $entity->__isInitialized())) {
if ($entity instanceof NotifyPropertyChanged && ( ! $entity instanceof GhostObjectInterface || $entity->isProxyInitialized())) {
$entity->addPropertyChangedListener($this);
}
}
Expand Down Expand Up @@ -3228,8 +3227,8 @@ public function getScheduledCollectionUpdates()
*/
public function initializeObject($obj)
{
if ($obj instanceof Proxy) {
$obj->__load();
if ($obj instanceof GhostObjectInterface) {
$obj->initializeProxy();

return;
}
Expand Down
11 changes: 6 additions & 5 deletions tests/Doctrine/Tests/ORM/Functional/ReferenceProxyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
use Doctrine\Tests\Models\ECommerce\ECommerceShipping;
use Doctrine\Tests\Models\Company\CompanyAuction;
use ProxyManager\Proxy\GhostObjectInterface;

/**
* Tests the generation of a proxy object for lazy loading.
Expand Down Expand Up @@ -230,20 +231,20 @@ public function testCommonPersistenceProxy()
{
$id = $this->createProduct();

/* @var $entity Doctrine\Tests\Models\ECommerce\ECommerceProduct */
/* @var $entity \ProxyManager\Proxy\GhostObjectInterface|\Doctrine\Tests\Models\ECommerce\ECommerceProduct */
$entity = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct' , $id);
$className = \Doctrine\Common\Util\ClassUtils::getClass($entity);

$this->assertInstanceOf('Doctrine\Common\Persistence\Proxy', $entity);
$this->assertFalse($entity->__isInitialized());
$this->assertInstanceOf(GhostObjectInterface::class, $entity);
$this->assertFalse($entity->isProxyInitialized());
$this->assertEquals('Doctrine\Tests\Models\ECommerce\ECommerceProduct', $className);

$restName = str_replace($this->_em->getConfiguration()->getProxyNamespace(), "", get_class($entity));
$restName = substr(get_class($entity), strlen($this->_em->getConfiguration()->getProxyNamespace()) +1);
$proxyFileName = $this->_em->getConfiguration()->getProxyDir() . DIRECTORY_SEPARATOR . str_replace("\\", "", $restName) . ".php";
$this->assertTrue(file_exists($proxyFileName), "Proxy file name cannot be found generically.");

$entity->__load();
$this->assertTrue($entity->__isInitialized());
$entity->initializeProxy();
$this->assertTrue($entity->isProxyInitialized());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ public function testResolveAssociationCacheEntry()
$this->assertNotNull($state2->getCountry());
$this->assertEquals($queryCount, $this->getCurrentQueryCount());
$this->assertInstanceOf('Doctrine\Tests\Models\Cache\State', $state2);
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $state2->getCountry());
$this->assertInstanceOf(GhostObjectInterface::class, $state2->getCountry());
$this->assertEquals($countryName, $state2->getCountry()->getName());
$this->assertEquals($stateId, $state2->getId());
}
Expand Down Expand Up @@ -962,7 +962,7 @@ public function testResolveToManyAssociationCacheEntry()

$this->assertEquals($queryCount, $this->getCurrentQueryCount());
$this->assertInstanceOf('Doctrine\Tests\Models\Cache\State', $state2);
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $state2->getCountry());
$this->assertInstanceOf(GhostObjectInterface::class, $state2->getCountry());
$this->assertInstanceOf('Doctrine\Tests\Models\Cache\City', $state2->getCities()->get(0));
$this->assertInstanceOf('Doctrine\Tests\Models\Cache\State', $state2->getCities()->get(0)->getState());
$this->assertSame($state2, $state2->getCities()->get(0)->getState());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ public function testRepositoryCacheFindAllToOneAssociation()
$this->assertInstanceOf(State::CLASSNAME, $entities[1]);
$this->assertInstanceOf(Country::CLASSNAME, $entities[0]->getCountry());
$this->assertInstanceOf(Country::CLASSNAME, $entities[1]->getCountry());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[0]->getCountry());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[1]->getCountry());
$this->assertInstanceOf(GhostObjectInterface::class, $entities[0]->getCountry());
$this->assertInstanceOf(GhostObjectInterface::class, $entities[1]->getCountry());

// invalidate cache
$this->_em->persist(new State('foo', $this->_em->find(Country::CLASSNAME, $this->countries[0]->getId())));
Expand All @@ -231,8 +231,8 @@ public function testRepositoryCacheFindAllToOneAssociation()
$this->assertInstanceOf(State::CLASSNAME, $entities[1]);
$this->assertInstanceOf(Country::CLASSNAME, $entities[0]->getCountry());
$this->assertInstanceOf(Country::CLASSNAME, $entities[1]->getCountry());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[0]->getCountry());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[1]->getCountry());
$this->assertInstanceOf(GhostObjectInterface::class, $entities[0]->getCountry());
$this->assertInstanceOf(GhostObjectInterface::class, $entities[1]->getCountry());

// load from cache
$queryCount = $this->getCurrentQueryCount();
Expand All @@ -245,7 +245,7 @@ public function testRepositoryCacheFindAllToOneAssociation()
$this->assertInstanceOf(State::CLASSNAME, $entities[1]);
$this->assertInstanceOf(Country::CLASSNAME, $entities[0]->getCountry());
$this->assertInstanceOf(Country::CLASSNAME, $entities[1]->getCountry());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[0]->getCountry());
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $entities[1]->getCountry());
$this->assertInstanceOf(GhostObjectInterface::class, $entities[0]->getCountry());
$this->assertInstanceOf(GhostObjectInterface::class, $entities[1]->getCountry());
}
}
2 changes: 1 addition & 1 deletion tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1392Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function testFailingCase()
$file = $picture->getFile();

// With this activated there will be no problem
//$file->__load();
//$file->isProxyInitialized();

$picture->setFile(null);

Expand Down
2 changes: 1 addition & 1 deletion tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1690Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function testChangeTracking()
'Verifying that $child is a proxy before using proxy API'
);
$this->assertCount(0, $child->listeners);
$child->__load();
$child->initializeProxy();
$this->assertCount(1, $child->listeners);
unset($parent, $child);

Expand Down
2 changes: 1 addition & 1 deletion tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2306Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public function testIssue()

$this->assertNotNull($userId);

$user->__load();
$user->isProxyInitialized();

$this->assertEquals(
$userId,
Expand Down
6 changes: 3 additions & 3 deletions tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2494Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ public function testIssue()
$queryCount = $this->getCurrentQueryCount();

$this->assertInstanceOf(GhostObjectInterface::class, $item->getCurrency());
$this->assertFalse($item->getCurrency()->__isInitialized());
$this->assertFalse($item->getCurrency()->isProxyInitialized());

$this->assertArrayHasKey('convertToPHPValue', DDC2494TinyIntType::$calls);
$this->assertCount(1, DDC2494TinyIntType::$calls['convertToPHPValue']);

$this->assertInternalType('integer', $item->getCurrency()->getId());
$this->assertCount(1, DDC2494TinyIntType::$calls['convertToPHPValue']);
$this->assertFalse($item->getCurrency()->__isInitialized());
$this->assertFalse($item->getCurrency()->isProxyInitialized());

$this->assertEquals($queryCount, $this->getCurrentQueryCount());

$this->assertInternalType('integer', $item->getCurrency()->getTemp());
$this->assertCount(3, DDC2494TinyIntType::$calls['convertToPHPValue']);
$this->assertTrue($item->getCurrency()->__isInitialized());
$this->assertTrue($item->getCurrency()->isProxyInitialized());

$this->assertEquals($queryCount + 1, $this->getCurrentQueryCount());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
namespace Doctrine\Tests\ORM\Performance;

use Doctrine\Tests\OrmPerformanceTestCase;
use Doctrine\Common\Proxy\Proxy;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\UnitOfWork;
use Doctrine\ORM\Proxy\ProxyFactory;
Expand Down
1 change: 0 additions & 1 deletion tests/Doctrine/Tests/ORM/Proxy/ProxyFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Doctrine\ORM\EntityNotFoundException;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\Common\Proxy\ProxyGenerator;
use Doctrine\Tests\Mocks\ConnectionMock;
use Doctrine\Tests\Mocks\EntityManagerMock;
use Doctrine\Tests\Mocks\UnitOfWorkMock;
Expand Down

0 comments on commit 9e11921

Please sign in to comment.