Commit
…Proxy * Adjust ProxyFactory to generate proxies according to new naming schema. * Change proxy naming and file-name generation to be a bit more consistent than previous approach. [DDC-1598] Additional regexp to check for simple ID methods to make it even more safe.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,7 +21,8 @@ | |
|
||
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 @@ public function __construct(EntityManager $em, $proxyDir, $proxyNs, $autoGenerat | |
*/ | ||
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 @@ public function getProxy($className, $identifier) | |
return new $fqn($entityPersister, $identifier); | ||
} | ||
|
||
/** | ||
* Generate the Proxy file name | ||
* | ||
* @param string $className | ||
* @return string | ||
*/ | ||
private function getProxyFileName($className) | ||
{ | ||
This comment has been minimized.
Sorry, something went wrong. |
||
return $this->_proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php'; | ||
} | ||
|
||
/** | ||
* Generates proxy classes for all given classes. | ||
* | ||
|
@@ -112,21 +131,19 @@ public function generateProxyClasses(array $classes, $toDir = null) | |
continue; | ||
} | ||
|
||
$proxyClassName = str_replace('\\', '', $class->name) . 'Proxy'; | ||
$proxyFileName = $proxyDir . $proxyClassName . '.php'; | ||
This comment has been minimized.
Sorry, something went wrong.
gedrox
Contributor
|
||
$this->_generateProxyClass($class, $proxyClassName, $proxyFileName, self::$_proxyClassTemplate); | ||
$proxyFileName = $this->getProxyFileName($class->name); | ||
$this->_generateProxyClass($class, $proxyFileName, self::$_proxyClassTemplate); | ||
} | ||
} | ||
|
||
/** | ||
* 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 @@ private function _generateProxyClass($class, $proxyClassName, $fileName, $file) | |
'<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,21 +250,41 @@ private function _generateMethods(ClassMetadata $class) | |
} | ||
|
||
/** | ||
* 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 | ||
*/ | ||
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) && | ||
$class->hasField($identifier) && | ||
(($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 @@ public function __load() | |
} | ||
} | ||
/** @private */ | ||
public function __isInitialized() | ||
{ | ||
return $this->__isInitialized__; | ||
} | ||
<methods> | ||
public function __sleep() | ||
|
6 comments
on commit a029b28
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@beberlei Please fix also DoctrineBundle as it's now unusable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stloyd oh, i was pretty sure that the implementation was backwards compatible. I will see what has to be done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stloyd what does "unusable" constitute for you? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cannot load proxy files because DoctrineBundle looks for old named proxy files. Here is an error:
The proxy file "/var/www/vhosts/myhost/app/cache/dev/doctrine/orm/Proxies/__CG__\Buu\FooBundle\Entity\Category.php" does not exist. If you still have objects serialized in the session, you need to clear the session manually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, this is missing. I suppose the proxy file is named __CG__BuuFooBundleEntityCategory.php
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just simple fix commited: doctrine/DoctrineBundle#9
shouldn't this use the MARKER constant ?