New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[DDC-717] Add eval() and FILE_NOT_EXISTS strategies for proxy generation #291
Changes from all commits
5e7f5db
a9fcac9
2d65b49
565572e
a7d96c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,43 @@ | |
*/ | ||
abstract class AbstractProxyFactory | ||
{ | ||
/** | ||
* Never autogenerate a proxy and rely that it was generated by some | ||
* process before deployment. | ||
* | ||
* @var integer | ||
*/ | ||
const AUTOGENERATE_NEVER = 0; | ||
|
||
/** | ||
* Always generates a new proxy in every request. | ||
* | ||
* This is only sane during development. | ||
* | ||
* @var integer | ||
*/ | ||
const AUTOGENERATE_ALWAYS = 1; | ||
|
||
/** | ||
* Autogenerate the proxy class when the proxy file does not exist. | ||
* | ||
* This strategy causes a file exists call whenever any proxy is used the | ||
* first time in a request. | ||
* | ||
* @var integer | ||
*/ | ||
const AUTOGENERATE_FILE_NOT_EXISTS = 2; | ||
|
||
/** | ||
* Generate the proxy classes using eval(). | ||
* | ||
* This strategy is only sane for development, and even then it gives me | ||
* the creeps a little. | ||
* | ||
* @var integer | ||
*/ | ||
const AUTOGENERATE_EVAL = 3; | ||
|
||
/** | ||
* @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory | ||
*/ | ||
|
@@ -54,13 +91,13 @@ abstract class AbstractProxyFactory | |
/** | ||
* @param \Doctrine\Common\Proxy\ProxyGenerator $proxyGenerator | ||
* @param \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory | ||
* @param bool $autoGenerate | ||
* @param bool|int $autoGenerate | ||
*/ | ||
public function __construct(ProxyGenerator $proxyGenerator, ClassMetadataFactory $metadataFactory, $autoGenerate) | ||
{ | ||
$this->proxyGenerator = $proxyGenerator; | ||
$this->metadataFactory = $metadataFactory; | ||
$this->autoGenerate = $autoGenerate; | ||
$this->autoGenerate = (int)$autoGenerate; | ||
} | ||
|
||
/** | ||
|
@@ -158,11 +195,27 @@ private function getProxyDefinition($className) | |
if ( ! class_exists($proxyClassName, false)) { | ||
$fileName = $this->proxyGenerator->getProxyFileName($className); | ||
|
||
if ($this->autoGenerate) { | ||
$this->proxyGenerator->generateProxyClass($classMetadata); | ||
switch ($this->autoGenerate) { | ||
case self::AUTOGENERATE_NEVER: | ||
require $fileName; | ||
break; | ||
|
||
case self::AUTOGENERATE_FILE_NOT_EXISTS: | ||
if ( ! file_exists($fileName)) { | ||
$this->proxyGenerator->generateProxyClass($classMetadata, $fileName); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Newline after this line |
||
require $fileName; | ||
break; | ||
|
||
case self::AUTOGENERATE_ALWAYS: | ||
$this->proxyGenerator->generateProxyClass($classMetadata, $fileName); | ||
require $fileName; | ||
break; | ||
|
||
case self::AUTOGENERATE_EVAL: | ||
$this->proxyGenerator->generateProxyClass($classMetadata, false); | ||
break; | ||
} | ||
|
||
require $fileName; | ||
} | ||
|
||
return $this->definitions[$className]; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -254,11 +254,11 @@ public function setProxyClassTemplate($proxyClassTemplate) | |
* Generates a proxy class file. | ||
* | ||
* @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class Metadata for the original class. | ||
* @param string $fileName Filename (full path) for the generated class. | ||
* @param string|bool $fileName Filename (full path) for the generated class. If none is given, eval() is used. | ||
* | ||
* @throws UnexpectedValueException | ||
*/ | ||
public function generateProxyClass(ClassMetadata $class, $fileName = null) | ||
public function generateProxyClass(ClassMetadata $class, $fileName = false) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changing this to |
||
{ | ||
preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches); | ||
|
||
|
@@ -277,8 +277,18 @@ public function generateProxyClass(ClassMetadata $class, $fileName = null) | |
} | ||
} | ||
|
||
$proxyCode = strtr($this->proxyClassTemplate, $placeholders); | ||
$fileName = $fileName ?: $this->getProxyFileName($class->getName()); | ||
$proxyCode = strtr($this->proxyClassTemplate, $placeholders); | ||
|
||
if ( ! $fileName) { | ||
$proxyClassName = $this->generateNamespace($class) . '\\' . $this->generateProxyShortClassName($class); | ||
|
||
if ( ! class_exists($proxyClassName)) { | ||
eval(substr($proxyCode, 5)); | ||
} | ||
|
||
return; | ||
} | ||
|
||
$parentDirectory = dirname($fileName); | ||
|
||
if ( ! is_dir($parentDirectory) && (false === @mkdir($parentDirectory, 0775, true))) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,8 +61,7 @@ protected function setUp() | |
return; | ||
} | ||
|
||
$this->proxyGenerator->generateProxyClass($this->metadata); | ||
require_once $this->proxyGenerator->getProxyFileName($this->metadata->getName()); | ||
$this->generateAndRequire($this->proxyGenerator, $this->metadata); | ||
} | ||
|
||
public function testReferenceProxyRespectsMethodsParametersTypeHinting() | ||
|
@@ -107,38 +106,37 @@ public function testNonNamespacedProxyGeneration() | |
public function testClassWithSleepProxyGeneration() | ||
{ | ||
if (!class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\SleepClass', false)) { | ||
$metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); | ||
$reflClass = new ReflectionClass('Doctrine\Tests\Common\Proxy\SleepClass'); | ||
$metadata->expects($this->any())->method('getReflectionClass')->will($this->returnValue($reflClass)); | ||
$metadata->expects($this->any())->method('getIdentifierFieldNames')->will($this->returnValue(array('id'))); | ||
$metadata->expects($this->any())->method('getName')->will($this->returnValue($reflClass->getName())); | ||
$className = 'Doctrine\Tests\Common\Proxy\SleepClass'; | ||
$metadata = $this->createClassMetadata($className, array('id')); | ||
$proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); | ||
$proxyGenerator->generateProxyClass($metadata); | ||
require_once $proxyGenerator->getProxyFileName($metadata->getName()); | ||
|
||
$this->generateAndRequire($proxyGenerator, $metadata); | ||
} | ||
|
||
$classCode = file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxySleepClass.php'); | ||
$this->assertEquals(1, substr_count($classCode, 'function __sleep')); | ||
$this->assertEquals(1, substr_count($classCode, 'parent::__sleep()')); | ||
} | ||
|
||
private function generateAndRequire($proxyGenerator, $metadata) | ||
{ | ||
$proxyGenerator->generateProxyClass($metadata, $proxyGenerator->getProxyFileName($metadata->getName())); | ||
|
||
require_once $proxyGenerator->getProxyFileName($metadata->getName()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing line break |
||
} | ||
|
||
public function testClassWithCallableTypeHintOnProxiedMethod() | ||
{ | ||
if (PHP_VERSION_ID < 50400) { | ||
$this->markTestSkipped('`callable` is only supported in PHP >=5.4.0'); | ||
} | ||
|
||
if (!class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\CallableTypeHintClass', false)) { | ||
$metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); | ||
$reflClass = new ReflectionClass('Doctrine\Tests\Common\Proxy\CallableTypeHintClass'); | ||
$proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); | ||
|
||
$metadata->expects($this->any())->method('getReflectionClass')->will($this->returnValue($reflClass)); | ||
$metadata->expects($this->any())->method('getIdentifierFieldNames')->will($this->returnValue(array('id'))); | ||
$metadata->expects($this->any())->method('getName')->will($this->returnValue($reflClass->getName())); | ||
$className = 'Doctrine\Tests\Common\Proxy\CallableTypeHintClass'; | ||
$metadata = $this->createClassMetadata($className, array('id')); | ||
|
||
$proxyGenerator->generateProxyClass($metadata); | ||
require_once $proxyGenerator->getProxyFileName($metadata->getName()); | ||
$proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); | ||
$this->generateAndRequire($proxyGenerator, $metadata); | ||
} | ||
|
||
$classCode = file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyCallableTypeHintClass.php'); | ||
|
@@ -149,11 +147,7 @@ public function testClassWithCallableTypeHintOnProxiedMethod() | |
public function testClassWithInvalidTypeHintOnProxiedMethod() | ||
{ | ||
$className = 'Doctrine\Tests\Common\Proxy\InvalidTypeHintClass'; | ||
$metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); | ||
$reflClass = new ReflectionClass($className); | ||
$metadata->expects($this->any())->method('getReflectionClass')->will($this->returnValue($reflClass)); | ||
$metadata->expects($this->any())->method('getIdentifierFieldNames')->will($this->returnValue(array())); | ||
$metadata->expects($this->any())->method('getName')->will($this->returnValue($className)); | ||
$metadata = $this->createClassMetadata($className, array('id')); | ||
$proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); | ||
|
||
$this->setExpectedException( | ||
|
@@ -182,4 +176,34 @@ public function testInvalidPlaceholderThrowsException() | |
$generator = new ProxyGenerator(__DIR__ . '/generated', 'SomeNamespace'); | ||
$generator->setPlaceholder('<somePlaceholder>', array()); | ||
} | ||
|
||
public function testUseEvalIfNoFilenameIsGiven() | ||
{ | ||
$proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true); | ||
|
||
$className = __NAMESPACE__ . '\\EvalBase'; | ||
|
||
$metadata = $this->createClassMetadata($className, array('id')); | ||
|
||
$proxyGenerator->generateProxyClass($metadata); | ||
|
||
$reflClass = new ReflectionClass('Doctrine\Tests\Common\ProxyProxy\__CG__\Doctrine\Tests\Common\Proxy\EvalBase'); | ||
|
||
$this->assertContains("eval()'d code", $reflClass->getFileName()); | ||
} | ||
|
||
private function createClassMetadata($className, array $ids) | ||
{ | ||
$metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata'); | ||
$reflClass = new ReflectionClass($className); | ||
$metadata->expects($this->any())->method('getReflectionClass')->will($this->returnValue($reflClass)); | ||
$metadata->expects($this->any())->method('getIdentifierFieldNames')->will($this->returnValue($ids)); | ||
$metadata->expects($this->any())->method('getName')->will($this->returnValue($className)); | ||
|
||
return $metadata; | ||
} | ||
} | ||
|
||
class EvalBase | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you move this to another file? |
||
{ | ||
} |
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.
Space