Merge branch 'DDC-1604'
This commit is contained in:
commit
28403abe78
@ -1,7 +1,5 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
@ -21,10 +19,12 @@
|
||||
|
||||
namespace Doctrine\ORM\Proxy;
|
||||
|
||||
use Doctrine\Common\Persistence\Proxy as BaseProxy;
|
||||
|
||||
/**
|
||||
* Interface for proxy classes.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.0
|
||||
*/
|
||||
interface Proxy {}
|
||||
interface Proxy extends BaseProxy {}
|
||||
|
@ -21,7 +21,8 @@ namespace Doctrine\ORM\Proxy;
|
||||
|
||||
use Doctrine\ORM\EntityManager,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\Mapping\AssociationMapping;
|
||||
Doctrine\ORM\Mapping\AssociationMapping,
|
||||
Doctrine\Common\Util\ClassUtils;
|
||||
|
||||
/**
|
||||
* This factory is used to create proxy objects for entities at runtime.
|
||||
@ -41,6 +42,14 @@ class ProxyFactory
|
||||
/** The directory that contains all proxy classes. */
|
||||
private $_proxyDir;
|
||||
|
||||
/**
|
||||
* Used to match very simple id methods that don't need
|
||||
* to be proxied since the identifier is known.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PATTERN_MATCH_ID_METHOD = '((public\s)?(function\s{1,}%s\s?\(\)\s{1,})\s{0,}{\s{0,}return\s{0,}\$this->%s;\s{0,}})i';
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the <tt>ProxyFactory</tt> class that is
|
||||
* connected to the given <tt>EntityManager</tt>.
|
||||
@ -74,13 +83,12 @@ class ProxyFactory
|
||||
*/
|
||||
public function getProxy($className, $identifier)
|
||||
{
|
||||
$proxyClassName = str_replace('\\', '', $className) . 'Proxy';
|
||||
$fqn = $this->_proxyNamespace . '\\' . $proxyClassName;
|
||||
$fqn = ClassUtils::generateProxyClassName($className, $this->_proxyNamespace);
|
||||
|
||||
if (! class_exists($fqn, false)) {
|
||||
$fileName = $this->_proxyDir . DIRECTORY_SEPARATOR . $proxyClassName . '.php';
|
||||
$fileName = $this->getProxyFileName($className);
|
||||
if ($this->_autoGenerate) {
|
||||
$this->_generateProxyClass($this->_em->getClassMetadata($className), $proxyClassName, $fileName, self::$_proxyClassTemplate);
|
||||
$this->_generateProxyClass($this->_em->getClassMetadata($className), $fileName, self::$_proxyClassTemplate);
|
||||
}
|
||||
require $fileName;
|
||||
}
|
||||
@ -94,6 +102,17 @@ class ProxyFactory
|
||||
return new $fqn($entityPersister, $identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the Proxy file name
|
||||
*
|
||||
* @param string $className
|
||||
* @return string
|
||||
*/
|
||||
private function getProxyFileName($className)
|
||||
{
|
||||
return $this->_proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates proxy classes for all given classes.
|
||||
*
|
||||
@ -112,9 +131,8 @@ class ProxyFactory
|
||||
continue;
|
||||
}
|
||||
|
||||
$proxyClassName = str_replace('\\', '', $class->name) . 'Proxy';
|
||||
$proxyFileName = $proxyDir . $proxyClassName . '.php';
|
||||
$this->_generateProxyClass($class, $proxyClassName, $proxyFileName, self::$_proxyClassTemplate);
|
||||
$proxyFileName = $this->getProxyFileName($class->name);
|
||||
$this->_generateProxyClass($class, $proxyFileName, self::$_proxyClassTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,11 +140,10 @@ class ProxyFactory
|
||||
* Generates a proxy class file.
|
||||
*
|
||||
* @param $class
|
||||
* @param $originalClassName
|
||||
* @param $proxyClassName
|
||||
* @param $file The path of the file to write to.
|
||||
*/
|
||||
private function _generateProxyClass($class, $proxyClassName, $fileName, $file)
|
||||
private function _generateProxyClass($class, $fileName, $file)
|
||||
{
|
||||
$methods = $this->_generateMethods($class);
|
||||
$sleepImpl = $this->_generateSleep($class);
|
||||
@ -138,16 +155,19 @@ class ProxyFactory
|
||||
'<methods>', '<sleepImpl>', '<cloneImpl>'
|
||||
);
|
||||
|
||||
if(substr($class->name, 0, 1) == "\\") {
|
||||
$className = substr($class->name, 1);
|
||||
} else {
|
||||
$className = $class->name;
|
||||
}
|
||||
$className = ltrim($class->name, '\\');
|
||||
$proxyClassName = ClassUtils::generateProxyClassName($class->name, $this->_proxyNamespace);
|
||||
$parts = explode('\\', strrev($proxyClassName), 2);
|
||||
$proxyClassNamespace = strrev($parts[1]);
|
||||
$proxyClassName = strrev($parts[0]);
|
||||
|
||||
$replacements = array(
|
||||
$this->_proxyNamespace,
|
||||
$proxyClassName, $className,
|
||||
$methods, $sleepImpl, $cloneImpl
|
||||
$proxyClassNamespace,
|
||||
$proxyClassName,
|
||||
$className,
|
||||
$methods,
|
||||
$sleepImpl,
|
||||
$cloneImpl
|
||||
);
|
||||
|
||||
$file = str_replace($placeholders, $replacements, $file);
|
||||
@ -230,6 +250,14 @@ class ProxyFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the method is a short identifier getter.
|
||||
*
|
||||
* What does this mean? For proxy objects the identifier is already known,
|
||||
* however accessing the getter for this identifier usually triggers the
|
||||
* lazy loading, leading to a query that may not be necessary if only the
|
||||
* ID is interesting for the userland code (for example in views that
|
||||
* generate links to the entity, but do not display anything else).
|
||||
*
|
||||
* @param ReflectionMethod $method
|
||||
* @param ClassMetadata $class
|
||||
* @return bool
|
||||
@ -237,7 +265,7 @@ class ProxyFactory
|
||||
private function isShortIdentifierGetter($method, $class)
|
||||
{
|
||||
$identifier = lcfirst(substr($method->getName(), 3));
|
||||
return (
|
||||
$cheapCheck = (
|
||||
$method->getNumberOfParameters() == 0 &&
|
||||
substr($method->getName(), 0, 3) == "get" &&
|
||||
in_array($identifier, $class->identifier, true) &&
|
||||
@ -245,6 +273,18 @@ class ProxyFactory
|
||||
(($method->getEndLine() - $method->getStartLine()) <= 4)
|
||||
&& in_array($class->fieldMappings[$identifier]['type'], array('integer', 'bigint', 'smallint', 'string'))
|
||||
);
|
||||
|
||||
if ($cheapCheck) {
|
||||
$code = file($class->reflClass->getFileName());
|
||||
$code = trim(implode(" ", array_slice($code, $method->getStartLine() - 1, $method->getEndLine() - $method->getStartLine() + 1)));
|
||||
|
||||
$pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier);
|
||||
|
||||
if (preg_match($pattern, $code)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -318,6 +358,12 @@ class <proxyClassName> extends \<className> implements \Doctrine\ORM\Proxy\Proxy
|
||||
}
|
||||
}
|
||||
|
||||
/** @private */
|
||||
public function __isInitialized()
|
||||
{
|
||||
return $this->__isInitialized__;
|
||||
}
|
||||
|
||||
<methods>
|
||||
|
||||
public function __sleep()
|
||||
|
@ -195,4 +195,28 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertEquals('Doctrine Cookbook', $entity->getName());
|
||||
$this->assertTrue($entity->__isInitialized__, "Getting something other than the identifier initializes the proxy.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1604
|
||||
*/
|
||||
public function testCommonPersistenceProxy()
|
||||
{
|
||||
$id = $this->createProduct();
|
||||
|
||||
/* @var $entity 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->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());
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testReferenceProxyDelegatesLoadingToThePersister()
|
||||
{
|
||||
$identifier = array('id' => 42);
|
||||
$proxyClass = 'Proxies\DoctrineTestsModelsECommerceECommerceFeatureProxy';
|
||||
$proxyClass = 'Proxies\__CG__\Doctrine\Tests\Models\ECommerce\ECommerceFeature';
|
||||
$persister = $this->_getMockPersister();
|
||||
$this->_uowMock->setEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $persister);
|
||||
|
||||
@ -69,7 +69,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
public function testReferenceProxyExecutesLoadingOnlyOnce()
|
||||
{
|
||||
$identifier = array('id' => 42);
|
||||
$proxyClass = 'Proxies\DoctrineTestsModelsECommerceECommerceFeatureProxy';
|
||||
$proxyClass = 'Proxies\__CG__\Doctrine\Tests\Models\ECommerce\ECommerceFeature';
|
||||
$persister = $this->_getMockPersister();
|
||||
$this->_uowMock->setEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $persister);
|
||||
$proxy = $this->_proxyFactory->getProxy('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $identifier);
|
||||
@ -108,7 +108,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
|
||||
public function testCreatesAssociationProxyAsSubclassOfTheOriginalOne()
|
||||
{
|
||||
$proxyClass = 'Proxies\DoctrineTestsModelsECommerceECommerceFeatureProxy';
|
||||
$proxyClass = 'Proxies\__CG__\Doctrine\Tests\Models\ECommerce\ECommerceFeature';
|
||||
$this->assertTrue(is_subclass_of($proxyClass, 'Doctrine\Tests\Models\ECommerce\ECommerceFeature'));
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
require_once dirname(__FILE__)."/fixtures/NonNamespacedProxies.php";
|
||||
|
||||
$className = "\DoctrineOrmTestEntity";
|
||||
$proxyName = "DoctrineOrmTestEntityProxy";
|
||||
$proxyName = "DoctrineOrmTestEntity";
|
||||
$classMetadata = new \Doctrine\ORM\Mapping\ClassMetadata($className);
|
||||
$classMetadata->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
|
||||
$classMetadata->mapField(array('fieldName' => 'id', 'type' => 'integer'));
|
||||
@ -133,16 +133,16 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
|
||||
$this->_proxyFactory->generateProxyClasses(array($classMetadata));
|
||||
|
||||
$classCode = file_get_contents(dirname(__FILE__)."/generated/".$proxyName.".php");
|
||||
$classCode = file_get_contents(dirname(__FILE__)."/generated/__CG__".$proxyName.".php");
|
||||
|
||||
$this->assertNotContains("class DoctrineOrmTestEntityProxy extends \\\\DoctrineOrmTestEntity", $classCode);
|
||||
$this->assertContains("class DoctrineOrmTestEntityProxy extends \\DoctrineOrmTestEntity", $classCode);
|
||||
$this->assertNotContains("class DoctrineOrmTestEntity extends \\\\DoctrineOrmTestEntity", $classCode);
|
||||
$this->assertContains("class DoctrineOrmTestEntity extends \\DoctrineOrmTestEntity", $classCode);
|
||||
}
|
||||
|
||||
public function testClassWithSleepProxyGeneration()
|
||||
{
|
||||
$className = "\Doctrine\Tests\ORM\Proxy\SleepClass";
|
||||
$proxyName = "DoctrineTestsORMProxySleepClassProxy";
|
||||
$proxyName = "DoctrineTestsORMProxySleepClass";
|
||||
$classMetadata = new \Doctrine\ORM\Mapping\ClassMetadata($className);
|
||||
$classMetadata->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
|
||||
$classMetadata->mapField(array('fieldName' => 'id', 'type' => 'integer'));
|
||||
@ -150,7 +150,7 @@ class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
|
||||
|
||||
$this->_proxyFactory->generateProxyClasses(array($classMetadata));
|
||||
|
||||
$classCode = file_get_contents(dirname(__FILE__)."/generated/".$proxyName.".php");
|
||||
$classCode = file_get_contents(dirname(__FILE__)."/generated/__CG__".$proxyName.".php");
|
||||
|
||||
$this->assertEquals(1, substr_count($classCode, 'function __sleep'));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user