1
0
mirror of synced 2024-12-05 03:06:05 +03:00

Proxy generation as of doctrine/common#168 - DCOM-96

This commit is contained in:
Marco Pivetta 2012-10-20 01:27:53 +02:00
parent 35fda90473
commit 8272ffd23f
17 changed files with 474 additions and 769 deletions

3
.gitignore vendored
View File

@ -8,4 +8,5 @@ lib/Doctrine/Common
lib/Doctrine/DBAL
/.settings/
.buildpath
.project
.project
.idea

View File

@ -52,7 +52,7 @@ class AssignedGenerator extends AbstractIdGenerator
$identifier = array();
foreach ($idFields as $idField) {
$value = $class->reflFields[$idField]->getValue($entity);
$value = $class->getFieldValue($entity, $idField);
if ( ! isset($value)) {
throw ORMException::entityMissingAssignedIdForField($entity, $idField);

View File

@ -605,7 +605,7 @@ class ClassMetadataInfo implements ClassMetadata
/**
* The ReflectionProperty instances of the mapped class.
*
* @var array
* @var \ReflectionProperty[]
*/
public $reflFields = array();
@ -693,13 +693,14 @@ class ClassMetadataInfo implements ClassMetadata
return $id;
}
$value = $this->reflFields[$this->identifier[0]]->getValue($entity);
$id = $this->identifier[0];
$value = $this->reflFields[$id]->getValue($entity);
if ($value !== null) {
return array($this->identifier[0] => $value);
if (null === $value) {
return array();
}
return array();
return array($id => $value);
}
/**

View File

@ -853,9 +853,10 @@ final class PersistentCollection implements Collection, Selectable
$newObjects = $this->coll->matching($criteria)->toArray();
}
$targetClass = $this->em->getClassMetadata(get_class($this->owner));
$id = $targetClass->getSingleIdReflectionProperty()->getValue($this->owner);
$id = $this->em
->getClassMetadata(get_class($this->owner))
->getSingleIdReflectionProperty()
->getValue($this->owner);
$builder = Criteria::expr();
$ownerExpression = $builder->eq($this->backRefFieldName, $id);
$expression = $criteria->getWhereExpression();

View File

@ -19,82 +19,11 @@
namespace Doctrine\ORM\Proxy;
use Doctrine\ORM\Configuration;
use Closure;
use Doctrine\Common\Proxy\Autoloader as BaseAutoloader;
/**
* Special Autoloader for Proxy classes because them not being PSR-0 compatible.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @deprecated use \Doctrine\Common\Proxy\Autoloader instead
*/
class Autoloader
class Autoloader extends BaseAutoloader
{
/**
* Resolves proxy class name to a filename based on the following pattern.
*
* 1. Remove Proxy namespace from class name
* 2. Remove namespace seperators from remaining class name.
* 3. Return PHP filename from proxy-dir with the result from 2.
*
* @param string $proxyDir
* @param string $proxyNamespace
* @param string $className
*
* @return string
*
* @throws ProxyException
*/
static public function resolveFile($proxyDir, $proxyNamespace, $className)
{
if (0 !== strpos($className, $proxyNamespace)) {
throw ProxyException::notProxyClass($className, $proxyNamespace);
}
$className = str_replace('\\', '', substr($className, strlen($proxyNamespace) +1));
return $proxyDir . DIRECTORY_SEPARATOR . $className.'.php';
}
/**
* Registers and returns autoloader callback for the given proxy dir and
* namespace.
*
* @param string $proxyDir
* @param string $proxyNamespace
* @param \Closure $notFoundCallback Invoked when the proxy file is not found.
*
* @return \Closure
*/
static public function register($proxyDir, $proxyNamespace, Closure $notFoundCallback = null)
{
$proxyNamespace = ltrim($proxyNamespace, "\\");
$autoloader = function($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) {
if (0 === strpos($className, $proxyNamespace)) {
$file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
if ($notFoundCallback && ! file_exists($file)) {
$notFoundCallback($proxyDir, $proxyNamespace, $className);
}
require $file;
}
};
spl_autoload_register($autoloader, true, true);
return $autoloader;
}
/**
* Registers and returns autoloader callback from a Configuration instance
*
* @param Configuration $config
* @param \Closure $notFoundCallback
*
* @return \Closure
*/
static public function registerFromConfiguration(Configuration $configuration, Closure $notFoundCallback)
{
return self::register($configuration->getProxyDir(), $configuration->getProxyNamespace(), $notFoundCallback);
}
}

View File

@ -19,7 +19,7 @@
namespace Doctrine\ORM\Proxy;
use Doctrine\Common\Persistence\Proxy as BaseProxy;
use Doctrine\Common\Proxy\Proxy as BaseProxy;
/**
* Interface for proxy classes.

View File

@ -1,69 +0,0 @@
<?php
/*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Proxy;
/**
* ORM Proxy Exception.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class ProxyException extends \Doctrine\ORM\ORMException
{
/**
* @return ProxyException
*/
public static function proxyDirectoryRequired()
{
return new self("You must configure a proxy directory. See docs for details");
}
/**
* @return ProxyException
*/
public static function proxyDirectoryNotWritable()
{
return new self("Your proxy directory must be writable.");
}
/**
* @return ProxyException
*/
public static function proxyNamespaceRequired()
{
return new self("You must configure a proxy namespace. See docs for details");
}
/**
* @param $className
* @param $proxyNamespace
*
* @return ProxyException
*/
public static function notProxyClass($className, $proxyNamespace)
{
return new self(sprintf(
"The class %s is not part of the proxy namespace %s",
$className, $proxyNamespace
));
}
}

View File

@ -20,53 +20,62 @@
namespace Doctrine\ORM\Proxy;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\EntityNotFoundException;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Common\Proxy\Proxy;
use Doctrine\Common\Proxy\ProxyGenerator;
/**
* This factory is used to create proxy objects for entities at runtime.
*
* @author Roman Borschel <roman@code-factory.org>
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
* @author Marco Pivetta <ocramius@gmail.com>
* @since 2.0
*/
class ProxyFactory
{
/**
* The EntityManager this factory is bound to.
*
* @var \Doctrine\ORM\EntityManager
* @var EntityManager The EntityManager this factory is bound to.
*/
private $_em;
private $em;
/**
* Whether to automatically (re)generate proxy classes.
*
* @var bool
* @var \Doctrine\ORM\UnitOfWork The UnitOfWork this factory uses to retrieve persisters
*/
private $_autoGenerate;
private $uow;
/**
* @var ProxyGenerator the proxy generator responsible for creating the proxy classes/files.
*/
private $proxyGenerator;
/**
* @var bool Whether to automatically (re)generate proxy classes.
*/
private $autoGenerate;
/**
* The namespace that contains all proxy classes.
*
* @var string
*/
private $_proxyNamespace;
private $proxyNs;
/**
* The directory that contains all proxy classes.
*
* @var string
*/
private $_proxyDir;
private $proxyDir;
/**
* Used to match very simple id methods that don't need
* to be proxied since the identifier is known.
*
* @var string
* @var array definitions (indexed by requested class name) for the proxy classes.
* Each element is an array containing following items:
* "fqcn" - FQCN of the proxy class
* "initializer" - Closure to be used as proxy __initializer__
* "cloner" - Closure to be used as proxy __cloner__
* "identifierFields" - list of field names for the identifiers
* "reflectionFields" - ReflectionProperties for the fields
*/
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';
private $definitions = array();
/**
* Initializes a new instance of the <tt>ProxyFactory</tt> class that is
@ -76,365 +85,195 @@ class ProxyFactory
* @param string $proxyDir The directory to use for the proxy classes. It must exist.
* @param string $proxyNs The namespace to use for the proxy classes.
* @param boolean $autoGenerate Whether to automatically generate proxy classes.
*
* @throws ProxyException
*/
public function __construct(EntityManager $em, $proxyDir, $proxyNs, $autoGenerate = false)
{
if ( ! $proxyDir) {
throw ProxyException::proxyDirectoryRequired();
}
if ( ! $proxyNs) {
throw ProxyException::proxyNamespaceRequired();
}
$this->_em = $em;
$this->_proxyDir = $proxyDir;
$this->_autoGenerate = $autoGenerate;
$this->_proxyNamespace = $proxyNs;
$this->em = $em;
$this->uow = $em->getUnitOfWork();
$this->proxyDir = $proxyDir;
$this->proxyNs = $proxyNs;
$this->autoGenerate = $autoGenerate;
}
/**
* Gets a reference proxy instance for the entity of the given type and identified by
* the given identifier.
*
* @param string $className
* @param mixed $identifier
*
* @param string $className
* @param mixed $identifier
* @return object
*/
public function getProxy($className, $identifier)
{
$fqn = ClassUtils::generateProxyClassName($className, $this->_proxyNamespace);
if (! class_exists($fqn, false)) {
$fileName = $this->getProxyFileName($className);
if ($this->_autoGenerate) {
$this->_generateProxyClass($this->_em->getClassMetadata($className), $fileName, self::$_proxyClassTemplate);
}
require $fileName;
if ( ! isset($this->definitions[$className])) {
$this->initProxyDefinitions($className);
}
$entityPersister = $this->_em->getUnitOfWork()->getEntityPersister($className);
$definition = $this->definitions[$className];
$fqcn = $definition['fqcn'];
$identifierFields = $definition['identifierFields'];
/* @var $reflectionFields \ReflectionProperty[] */
$reflectionFields = $definition['reflectionFields'];
$proxy = new $fqcn($definition['initializer'], $definition['cloner']);
return new $fqn($entityPersister, $identifier);
}
foreach ($identifierFields as $idField) {
$reflectionFields[$idField]->setValue($proxy, $identifier[$idField]);
}
/**
* Generates the Proxy file name.
*
* @param string $className
* @param string|null $baseDir Optional base directory for proxy file name generation.
* If not specified, the directory configured on the Configuration of the
* EntityManager will be used by this factory.
* @return string
*/
private function getProxyFileName($className, $baseDir = null)
{
$proxyDir = $baseDir ?: $this->_proxyDir;
return $proxyDir . DIRECTORY_SEPARATOR . '__CG__' . str_replace('\\', '', $className) . '.php';
return $proxy;
}
/**
* Generates proxy classes for all given classes.
*
* @param array $classes The classes (ClassMetadata instances) for which to generate proxies.
* @param string|null $toDir The target directory of the proxy classes. If not specified, the
* directory configured on the Configuration of the EntityManager used
* by this factory is used.
*
* @param \Doctrine\Common\Persistence\Mapping\ClassMetadata[] $classes The classes (ClassMetadata instances)
* for which to generate proxies.
* @param string $proxyDir The target directory of the proxy classes. If not specified, the
* directory configured on the Configuration of the EntityManager used
* by this factory is used.
* @return int Number of generated proxies.
*/
public function generateProxyClasses(array $classes, $toDir = null)
public function generateProxyClasses(array $classes, $proxyDir = null)
{
$proxyDir = $toDir ?: $this->_proxyDir;
$proxyDir = rtrim($proxyDir, DIRECTORY_SEPARATOR);
$num = 0;
$generated = 0;
foreach ($classes as $class) {
/* @var $class ClassMetadata */
if ($class->isMappedSuperclass || $class->reflClass->isAbstract()) {
/* @var $class \Doctrine\ORM\Mapping\ClassMetadataInfo */
if ($class->isMappedSuperclass || $class->getReflectionClass()->isAbstract()) {
continue;
}
$proxyFileName = $this->getProxyFileName($class->name, $proxyDir);
$generator = $this->getProxyGenerator();
$this->_generateProxyClass($class, $proxyFileName, self::$_proxyClassTemplate);
$num++;
$proxyFileName = $generator->getProxyFileName($class->getName(), $proxyDir);
$generator->generateProxyClass($class, $proxyFileName);
$generated += 1;
}
return $num;
return $generated;
}
/**
* Generates a proxy class file.
*
* @param ClassMetadata $class Metadata for the original class.
* @param string $fileName Filename (full path) for the generated class.
* @param string $file The proxy class template data.
*
* @return void
*
* @throws ProxyException
* @param ProxyGenerator $proxyGenerator
*/
private function _generateProxyClass(ClassMetadata $class, $fileName, $file)
public function setProxyGenerator(ProxyGenerator $proxyGenerator)
{
$methods = $this->_generateMethods($class);
$sleepImpl = $this->_generateSleep($class);
$cloneImpl = $class->reflClass->hasMethod('__clone') ? 'parent::__clone();' : ''; // hasMethod() checks case-insensitive
$placeholders = array(
'<namespace>',
'<proxyClassName>', '<className>',
'<methods>', '<sleepImpl>', '<cloneImpl>'
);
$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(
$proxyClassNamespace,
$proxyClassName,
$className,
$methods,
$sleepImpl,
$cloneImpl
);
$file = str_replace($placeholders, $replacements, $file);
$parentDirectory = dirname($fileName);
if ( ! is_dir($parentDirectory)) {
if (false === @mkdir($parentDirectory, 0775, true)) {
throw ProxyException::proxyDirectoryNotWritable();
}
} else if ( ! is_writable($parentDirectory)) {
throw ProxyException::proxyDirectoryNotWritable();
}
$tmpFileName = $fileName . '.' . uniqid("", true);
file_put_contents($tmpFileName, $file);
rename($tmpFileName, $fileName);
$this->proxyGenerator = $proxyGenerator;
}
/**
* Generates the methods of a proxy class.
*
* @param ClassMetadata $class
*
* @return string The code of the generated methods.
* @return ProxyGenerator
*/
private function _generateMethods(ClassMetadata $class)
public function getProxyGenerator()
{
$methods = '';
if (null === $this->proxyGenerator) {
$this->proxyGenerator = new ProxyGenerator($this->proxyDir, $this->proxyNs);
$this->proxyGenerator->setPlaceholder('<baseProxyInterface>', 'Doctrine\ORM\Proxy\Proxy');
}
$methodNames = array();
foreach ($class->reflClass->getMethods() as $method) {
/* @var $method \ReflectionMethod */
if ($method->isConstructor() || in_array(strtolower($method->getName()), array("__sleep", "__clone")) || isset($methodNames[$method->getName()])) {
continue;
return $this->proxyGenerator;
}
/**
* @param string $className
*/
private function initProxyDefinitions($className)
{
$fqcn = ClassUtils::generateProxyClassName($className, $this->proxyNs);
$classMetadata = $this->em->getClassMetadata($className);
if ( ! class_exists($fqcn, false)) {
$generator = $this->getProxyGenerator();
$fileName = $generator->getProxyFileName($className);
if ($this->autoGenerate) {
$generator->generateProxyClass($classMetadata);
}
$methodNames[$method->getName()] = true;
if ($method->isPublic() && ! $method->isFinal() && ! $method->isStatic()) {
$methods .= "\n" . ' public function ';
if ($method->returnsReference()) {
$methods .= '&';
require $fileName;
}
$entityPersister = $this->uow->getEntityPersister($className);
if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) {
$initializer = function (Proxy $proxy) use ($entityPersister, $classMetadata) {
$proxy->__setInitializer(null);
$proxy->__setCloner(null);
if ($proxy->__isInitialized()) {
return;
}
$methods .= $method->getName() . '(';
$firstParam = true;
$parameterString = $argumentString = '';
foreach ($method->getParameters() as $param) {
if ($firstParam) {
$firstParam = false;
} else {
$parameterString .= ', ';
$argumentString .= ', ';
}
$properties = $proxy->__getLazyProperties();
// We need to pick the type hint class too
if (($paramClass = $param->getClass()) !== null) {
$parameterString .= '\\' . $paramClass->getName() . ' ';
} else if ($param->isArray()) {
$parameterString .= 'array ';
}
if ($param->isPassedByReference()) {
$parameterString .= '&';
}
$parameterString .= '$' . $param->getName();
$argumentString .= '$' . $param->getName();
if ($param->isDefaultValueAvailable()) {
$parameterString .= ' = ' . var_export($param->getDefaultValue(), true);
foreach ($properties as $propertyName => $property) {
if (!isset($proxy->$propertyName)) {
$proxy->$propertyName = $properties[$propertyName];
}
}
$methods .= $parameterString . ')';
$methods .= "\n" . ' {' . "\n";
if ($this->isShortIdentifierGetter($method, $class)) {
$identifier = lcfirst(substr($method->getName(), 3));
$proxy->__setInitialized(true);
$proxy->__wakeup();
$cast = in_array($class->fieldMappings[$identifier]['type'], array('integer', 'smallint')) ? '(int) ' : '';
$methods .= ' if ($this->__isInitialized__ === false) {' . "\n";
$methods .= ' return ' . $cast . '$this->_identifier["' . $identifier . '"];' . "\n";
$methods .= ' }' . "\n";
if (null === $entityPersister->load($classMetadata->getIdentifierValues($proxy), $proxy)) {
throw new EntityNotFoundException();
}
$methods .= ' $this->__load();' . "\n";
$methods .= ' return parent::' . $method->getName() . '(' . $argumentString . ');';
$methods .= "\n" . ' }' . "\n";
}
}
return $methods;
}
/**
* Checks 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, ClassMetadata $class)
{
$identifier = lcfirst(substr($method->getName(), 3));
$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($method->getDeclaringClass()->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;
}
/**
* Generates the code for the __sleep method for a proxy class.
*
* @param ClassMetadata $class
*
* @return string
*/
private function _generateSleep(ClassMetadata $class)
{
$sleepImpl = '';
if ($class->reflClass->hasMethod('__sleep')) {
$sleepImpl .= "return array_merge(array('__isInitialized__'), parent::__sleep());";
};
} else {
$sleepImpl .= "return array('__isInitialized__', ";
$first = true;
$initializer = function (Proxy $proxy) use ($entityPersister, $classMetadata) {
$proxy->__setInitializer(null);
$proxy->__setCloner(null);
foreach ($class->getReflectionProperties() as $name => $prop) {
if ($first) {
$first = false;
} else {
$sleepImpl .= ', ';
if ($proxy->__isInitialized()) {
return;
}
$sleepImpl .= "'" . $name . "'";
}
$properties = $proxy->__getLazyProperties();
$sleepImpl .= ');';
foreach ($properties as $propertyName => $property) {
if (!isset($proxy->$propertyName)) {
$proxy->$propertyName = $properties[$propertyName];
}
}
$proxy->__setInitialized(true);
if (null === $entityPersister->load($classMetadata->getIdentifierValues($proxy), $proxy)) {
throw new EntityNotFoundException();
}
};
}
return $sleepImpl;
}
/** Proxy class code template */
private static $_proxyClassTemplate =
'<?php
namespace <namespace>;
/**
* THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE.
*/
class <proxyClassName> extends \<className> implements \Doctrine\ORM\Proxy\Proxy
{
private $_entityPersister;
private $_identifier;
public $__isInitialized__ = false;
public function __construct($entityPersister, $identifier)
{
$this->_entityPersister = $entityPersister;
$this->_identifier = $identifier;
}
/** @private */
public function __load()
{
if (!$this->__isInitialized__ && $this->_entityPersister) {
$this->__isInitialized__ = true;
if (method_exists($this, "__wakeup")) {
// call this after __isInitialized__to avoid infinite recursion
// but before loading to emulate what ClassMetadata::newInstance()
// provides.
$this->__wakeup();
$cloner = function (Proxy $proxy) use ($entityPersister, $classMetadata) {
if ($proxy->__isInitialized()) {
return;
}
if ($this->_entityPersister->load($this->_identifier, $this) === null) {
throw new \Doctrine\ORM\EntityNotFoundException();
$proxy->__setInitialized(true);
$proxy->__setInitializer(null);
$class = $entityPersister->getClassMetadata();
$original = $entityPersister->load($classMetadata->getIdentifierValues($proxy));
if (null === $original) {
throw new EntityNotFoundException();
}
unset($this->_entityPersister, $this->_identifier);
}
}
/** @private */
public function __isInitialized()
{
return $this->__isInitialized__;
}
foreach ($class->getReflectionClass()->getProperties() as $reflectionProperty) {
$propertyName = $reflectionProperty->getName();
<methods>
public function __sleep()
{
<sleepImpl>
}
public function __clone()
{
if (!$this->__isInitialized__ && $this->_entityPersister) {
$this->__isInitialized__ = true;
$class = $this->_entityPersister->getClassMetadata();
$original = $this->_entityPersister->load($this->_identifier);
if ($original === null) {
throw new \Doctrine\ORM\EntityNotFoundException();
if ($class->hasField($propertyName) || $class->hasAssociation($propertyName)) {
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($proxy, $reflectionProperty->getValue($original));
}
}
foreach ($class->reflFields as $field => $reflProperty) {
$reflProperty->setValue($this, $reflProperty->getValue($original));
}
unset($this->_entityPersister, $this->_identifier);
}
<cloneImpl>
};
$this->definitions[$className] = array(
'fqcn' => $fqcn,
'initializer' => $initializer,
'cloner' => $cloner,
'identifierFields' => $classMetadata->getIdentifierFieldNames(),
'reflectionFields' => $classMetadata->getReflectionProperties(),
);
}
}';
}

View File

@ -96,7 +96,7 @@ class DebugUnitOfWorkListener
foreach ($cm->associationMappings as $field => $assoc) {
fwrite($fh, " " . $field . " ");
$value = $cm->reflFields[$field]->getValue($entity);
$value = $cm->getFieldValue($entity, $field);
if ($assoc['type'] & ClassMetadata::TO_ONE) {
if ($value === null) {

View File

@ -666,7 +666,9 @@ class UnitOfWork implements PropertyChangedListener
// Look for changes in associations of the entity
foreach ($class->associationMappings as $field => $assoc) {
if (($val = $class->reflFields[$field]->getValue($entity)) !== null) {
$val = $class->reflFields[$field]->getValue($entity);
if (null !== $val) {
$this->computeAssociationChanges($assoc, $val);
if (!isset($this->entityChangeSets[$oid]) &&
$assoc['isOwningSide'] &&
@ -1815,8 +1817,9 @@ class UnitOfWork implements PropertyChangedListener
}
if ($class->isVersioned) {
$managedCopyVersion = $class->reflFields[$class->versionField]->getValue($managedCopy);
$entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
$reflField = $class->reflFields[$class->versionField];
$managedCopyVersion = $reflField->getValue($managedCopy);
$entityVersion = $reflField->getValue($entity);
// Throw exception if versions dont match.
if ($managedCopyVersion != $entityVersion) {
@ -1828,14 +1831,17 @@ class UnitOfWork implements PropertyChangedListener
foreach ($class->reflClass->getProperties() as $prop) {
$name = $prop->name;
$prop->setAccessible(true);
if ( ! isset($class->associationMappings[$name])) {
if ( ! $class->isIdentifier($name)) {
$prop->setValue($managedCopy, $prop->getValue($entity));
}
} else {
$assoc2 = $class->associationMappings[$name];
if ($assoc2['type'] & ClassMetadata::TO_ONE) {
$other = $prop->getValue($entity);
if ($other === null) {
$prop->setValue($managedCopy, null);
} else if ($other instanceof Proxy && !$other->__isInitialized__) {
@ -1857,6 +1863,7 @@ class UnitOfWork implements PropertyChangedListener
}
} else {
$mergeCol = $prop->getValue($entity);
if ($mergeCol instanceof PersistentCollection && !$mergeCol->isInitialized()) {
// do not merge fields marked lazy that have not been fetched.
// keep the lazy persistent collection of the managed copy.
@ -1864,6 +1871,7 @@ class UnitOfWork implements PropertyChangedListener
}
$managedCol = $prop->getValue($managedCopy);
if (!$managedCol) {
$managedCol = new PersistentCollection($this->em,
$this->em->getClassMetadata($assoc2['targetEntity']),
@ -2468,8 +2476,26 @@ class UnitOfWork implements PropertyChangedListener
$entity = $this->identityMap[$class->rootEntityName][$idHash];
$oid = spl_object_hash($entity);
if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
$entity->__isInitialized__ = true;
if (
isset($hints[Query::HINT_REFRESH])
&& isset($hints[Query::HINT_REFRESH_ENTITY])
&& ($unmanagedProxy = $hints[Query::HINT_REFRESH_ENTITY]) !== $entity
&& $unmanagedProxy instanceof Proxy
) {
// DDC-1238 - we have a managed instance, but it isn't the provided one.
// Therefore we clear its identifier. Also, we must re-fetch metadata since the
// refreshed object may be anything
$class = $this->em->getClassMetadata(get_class($unmanagedProxy));
foreach ($class->identifier as $fieldName) {
$class->reflFields[$fieldName]->setValue($unmanagedProxy, null);
}
return $unmanagedProxy;
}
if ($entity instanceof Proxy && ! $entity->__isInitialized()) {
$entity->__setInitialized(true);
$overrideLocalValues = true;
if ($entity instanceof NotifyPropertyChanged) {

@ -1 +1 @@
Subproject commit d514e3920656921ba1148f16a4089222c58bc83a
Subproject commit c2b45fdb2757492e75abaab119164aaf311cd395

View File

@ -2,9 +2,8 @@
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Util\ClassUtils,
Doctrine\Tests\Models\CMS\CmsUser,
Doctrine\Tests\Proxies\__CG__\Doctrine\Tests\Models\CMS\CmsUser as Proxy;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\Tests\Models\CMS\CmsUser;
/**
* Test that Doctrine ORM correctly works with proxy instances exactly like with ordinary Entities
@ -12,7 +11,7 @@ use Doctrine\Common\Util\ClassUtils,
* The test considers two possible cases:
* a) __initialized__ = true and no identifier set in proxy
* b) __initialized__ = false and identifier set in proxy and in property
* @todo All other cases would cause lazy loading issues
* @todo All other cases would cause lazy loading
*/
class ProxiesLikeEntitiesTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
@ -27,6 +26,11 @@ class ProxiesLikeEntitiesTest extends \Doctrine\Tests\OrmFunctionalTestCase
try {
$this->_schemaTool->createSchema(array(
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser'),
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'),
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsArticle'),
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsAddress'),
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsEmail'),
$this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsGroup'),
));
} catch (\Exception $e) {
}
@ -44,8 +48,7 @@ class ProxiesLikeEntitiesTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testPersistUpdate()
{
// Considering case (a)
$persister = $this->_em->getUnitOfWork()->getEntityPersister('Doctrine\Tests\Models\CMS\CmsUser');
$proxy = new Proxy($persister, array());
$proxy = $this->_em->getProxyFactory()->getProxy('Doctrine\Tests\Models\CMS\CmsUser', array('id' => null));
$proxy->__isInitialized__ = true;
$proxy->username = 'ocra';
$proxy->name = 'Marco';
@ -65,15 +68,15 @@ class ProxiesLikeEntitiesTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testEntityWithIdentifier()
{
// Considering case (b)
$persister = $this->_em->getUnitOfWork()->getEntityPersister('Doctrine\Tests\Models\CMS\CmsUser');
$uninitializedProxy = new Proxy($persister, array('id' => $this->user->getId()));
$uninitializedProxy->id = $this->user->getId();
$uninitializedProxy->username = 'ocra';
$uninitializedProxy->name = 'Marco Pivetta';
$userId = $this->user->getId();
/* @var $uninitializedProxy \Doctrine\Tests\Proxies\__CG__\Doctrine\Tests\Models\CMS\CmsUser */
$uninitializedProxy = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsUser', $userId);
$this->assertInstanceOf('Doctrine\Tests\Proxies\__CG__\Doctrine\Tests\Models\CMS\CmsUser', $uninitializedProxy);
$this->_em->persist($uninitializedProxy);
$this->_em->flush();
$this->assertEquals($this->user->getId(), $uninitializedProxy->getId());
$this->_em->flush($uninitializedProxy);
$this->assertFalse($uninitializedProxy->__isInitialized(), 'Proxy didn\'t get initialized during flush operations');
$this->assertEquals($userId, $uninitializedProxy->getId());
$this->_em->remove($uninitializedProxy);
$this->_em->flush();
}
@ -83,8 +86,7 @@ class ProxiesLikeEntitiesTest extends \Doctrine\Tests\OrmFunctionalTestCase
*/
public function testProxyAsDqlParameterPersist()
{
$persister = $this->_em->getUnitOfWork()->getEntityPersister('Doctrine\Tests\Models\CMS\CmsUser');
$proxy = new Proxy($persister, array('id' => $this->user->getId()));
$proxy = $this->_em->getProxyFactory()->getProxy('Doctrine\Tests\Models\CMS\CmsUser', array('id' => $this->user->getId()));
$proxy->id = $this->user->getId();
$result = $this
->_em

View File

@ -0,0 +1,165 @@
<?php
/*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
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;
use Doctrine\ORM\Persisters\BasicEntityPersister;
/**
* Performance test used to measure performance of proxy instantiation
*
* @author Marco Pivetta <ocramius@gmail.com>
* @group performance
*/
class ProxyPerformanceTest extends OrmPerformanceTestCase
{
/**
* @return array
*/
public function entitiesProvider()
{
return array(
array('Doctrine\Tests\Models\CMS\CmsEmployee'),
array('Doctrine\Tests\Models\CMS\CmsUser'),
);
}
/**
* @dataProvider entitiesProvider
*/
public function testProxyInstantiationPerformance($entityName)
{
$proxyFactory = $this->_getEntityManager()->getProxyFactory();
$this->setMaxRunningTime(5);
$start = microtime(true);
for ($i = 0; $i < 100000; $i += 1) {
$user = $proxyFactory->getProxy($entityName, array('id' => $i));
}
echo __FUNCTION__ . " - " . (microtime(true) - $start) . " seconds with " . $entityName . PHP_EOL;
}
/**
* @dataProvider entitiesProvider
*/
public function testProxyForcedInitializationPerformance($entityName)
{
$em = new MockEntityManager($this->_getEntityManager());
$proxyFactory = $em->getProxyFactory();
/* @var $user \Doctrine\Common\Proxy\Proxy */
$user = $proxyFactory->getProxy($entityName, array('id' => 1));
$initializer = $user->__getInitializer();
$this->setMaxRunningTime(5);
$start = microtime(true);
for ($i = 0; $i < 100000; $i += 1) {
$user->__setInitialized(false);
$user->__setInitializer($initializer);
$user->__load();
$user->__load();
}
echo __FUNCTION__ . " - " . (microtime(true) - $start) . " seconds with " . $entityName . PHP_EOL;
}
}
/**
* Mock entity manager to fake `getPersister()`
*/
class MockEntityManager extends EntityManager
{
/** @var EntityManager */
private $em;
/** @param EntityManager $em */
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/** {@inheritDoc} */
public function getProxyFactory()
{
$config = $this->em->getConfiguration();
return new ProxyFactory(
$this,
$config->getProxyDir(),
$config->getProxyNamespace(),
$config->getAutoGenerateProxyClasses()
);
}
/** {@inheritDoc} */
public function getClassMetadata($className)
{
return $this->em->getClassMetadata($className);
}
/** {@inheritDoc} */
public function getUnitOfWork()
{
return new MockUnitOfWork();
}
}
/**
* Mock UnitOfWork manager to fake `getPersister()`
*/
class MockUnitOfWork extends UnitOfWork
{
/** @var PersisterMock */
private $entityPersister;
/** */
public function __construct()
{
$this->entityPersister = new PersisterMock();
}
/** {@inheritDoc} */
public function getEntityPersister($entityName)
{
return $this->entityPersister;
}
}
/**
* Mock persister (we don't want PHPUnit comparator API to play a role in here)
*/
class PersisterMock extends BasicEntityPersister
{
/** */
public function __construct()
{
}
/** {@inheritDoc} */
public function load(array $criteria, $entity = null, $assoc = null, array $hints = array(), $lockMode = 0, $limit = null, array $orderBy = null)
{
return $entity;
}
}

View File

@ -1,62 +0,0 @@
<?php
/*
* 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
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Tests\ORM\Proxy;
use Doctrine\Tests\OrmTestCase;
use Doctrine\ORM\Proxy\Autoloader;
/**
* @group DDC-1698
*/
class AutoloaderTest extends OrmTestCase
{
static public function dataResolveFile()
{
return array(
array('/tmp', 'MyProxy', 'MyProxy\__CG__\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__RealClass.php'),
array('/tmp', 'MyProxy\Subdir', 'MyProxy\Subdir\__CG__\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__RealClass.php'),
array('/tmp', 'MyProxy', 'MyProxy\__CG__\Other\RealClass', '/tmp' . DIRECTORY_SEPARATOR . '__CG__OtherRealClass.php'),
);
}
/**
* @dataProvider dataResolveFile
*/
public function testResolveFile($proxyDir, $proxyNamespace, $className, $expectedProxyFile)
{
$actualProxyFile = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className);
$this->assertEquals($expectedProxyFile, $actualProxyFile);
}
public function testAutoload()
{
if (file_exists(sys_get_temp_dir() ."/AutoloaderTestClass.php")) {
unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php");
}
$autoloader = Autoloader::register(sys_get_temp_dir(), 'ProxyAutoloaderTest', function($proxyDir, $proxyNamespace, $className) {
file_put_contents(sys_get_temp_dir() . "/AutoloaderTestClass.php", "<?php namespace ProxyAutoloaderTest; class AutoloaderTestClass {} ");
});
$this->assertTrue(class_exists('ProxyAutoloaderTest\AutoloaderTestClass', true));
unlink(sys_get_temp_dir() ."/AutoloaderTestClass.php");
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace Doctrine\Tests\ORM\Proxy;
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;
use Doctrine\Tests\Mocks\DriverMock;
require_once __DIR__ . '/../../TestInit.php';
/**
* Test the proxy generator. Its work is generating on-the-fly subclasses of a given model, which implement the Proxy pattern.
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
*/
class ProxtFactoryTest extends \Doctrine\Tests\OrmTestCase
{
/**
* @var ConnectionMock
*/
private $connectionMock;
/**
* @var UnitOfWorkMock
*/
private $uowMock;
/**
* @var EntityManagerMock
*/
private $emMock;
/**
* @var \Doctrine\ORM\Proxy\ProxyFactory
*/
private $proxyFactory;
/**
* {@inheritDoc}
*/
protected function setUp()
{
parent::setUp();
$this->connectionMock = new ConnectionMock(array(), new DriverMock());
$this->emMock = EntityManagerMock::create($this->connectionMock);
$this->uowMock = new UnitOfWorkMock($this->emMock);
$this->emMock->setUnitOfWork($this->uowMock);
$this->proxyFactory = new ProxyFactory($this->emMock, sys_get_temp_dir(), 'Proxies', true);
}
public function testReferenceProxyDelegatesLoadingToThePersister()
{
$identifier = array('id' => 42);
$proxyClass = 'Proxies\__CG__\Doctrine\Tests\Models\ECommerce\ECommerceFeature';
$persister = $this->getMock('Doctrine\ORM\Persisters\BasicEntityPersister', array('load'), array(), '', false);
$this->uowMock->setEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $persister);
$proxy = $this->proxyFactory->getProxy('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $identifier);
$persister
->expects($this->atLeastOnce())
->method('load')
->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass))
->will($this->returnValue(new \stdClass()));
$proxy->getDescription();
}
/**
* @group DDC-1771
*/
public function testSkipAbstractClassesOnGeneration()
{
$cm = new \Doctrine\ORM\Mapping\ClassMetadata(__NAMESPACE__ . '\\AbstractClass');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->assertNotNull($cm->reflClass);
$num = $this->proxyFactory->generateProxyClasses(array($cm));
$this->assertEquals(0, $num, "No proxies generated.");
}
}
abstract class AbstractClass
{
}

View File

@ -1,204 +0,0 @@
<?php
namespace Doctrine\Tests\ORM\Proxy;
use Doctrine\ORM\Proxy\ProxyFactory;
use Doctrine\Tests\Mocks\ConnectionMock;
use Doctrine\Tests\Mocks\EntityManagerMock;
use Doctrine\Tests\Mocks\UnitOfWorkMock;
use Doctrine\Tests\Models\ECommerce\ECommerceCart;
use Doctrine\Tests\Models\ECommerce\ECommerceCustomer;
use Doctrine\Tests\Models\ECommerce\ECommerceFeature;
use Doctrine\Tests\Models\ECommerce\ECommerceProduct;
use Doctrine\Tests\Models\ECommerce\ECommerceShipping;
require_once __DIR__ . '/../../TestInit.php';
/**
* Test the proxy generator. Its work is generating on-the-fly subclasses of a given model, which implement the Proxy pattern.
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
*/
class ProxyClassGeneratorTest extends \Doctrine\Tests\OrmTestCase
{
private $_connectionMock;
private $_uowMock;
private $_emMock;
/**
* @var \Doctrine\ORM\Proxy\ProxyFactory
*/
private $_proxyFactory;
protected function setUp()
{
parent::setUp();
$this->_connectionMock = new ConnectionMock(array(), new \Doctrine\Tests\Mocks\DriverMock());
$this->_emMock = EntityManagerMock::create($this->_connectionMock);
$this->_uowMock = new UnitOfWorkMock($this->_emMock);
$this->_emMock->setUnitOfWork($this->_uowMock);
// SUT
$this->_proxyFactory = new ProxyFactory($this->_emMock, __DIR__ . '/generated', 'Proxies', true);
}
protected function tearDown()
{
foreach (new \DirectoryIterator(__DIR__ . '/generated') as $file) {
if (strstr($file->getFilename(), '.php')) {
unlink($file->getPathname());
}
}
}
public function testReferenceProxyDelegatesLoadingToThePersister()
{
$identifier = array('id' => 42);
$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);
$persister->expects($this->atLeastOnce())
->method('load')
->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass))
->will($this->returnValue(new \stdClass())); // fake return of entity instance
$proxy->getDescription();
}
public function testReferenceProxyExecutesLoadingOnlyOnce()
{
$identifier = array('id' => 42);
$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);
$persister->expects($this->atLeastOnce())
->method('load')
->with($this->equalTo($identifier), $this->isInstanceOf($proxyClass))
->will($this->returnValue(new \stdClass())); // fake return of entity instance
$proxy->getDescription();
$proxy->getProduct();
}
public function testReferenceProxyRespectsMethodsParametersTypeHinting()
{
$proxyClass = 'Proxies\DoctrineTestsModelsECommerceECommerceFeatureProxy';
$persister = $this->_getMockPersister();
$this->_uowMock->setEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceFeature', $persister);
$proxy = $this->_proxyFactory->getProxy('Doctrine\Tests\Models\ECommerce\ECommerceFeature', null);
$method = new \ReflectionMethod(get_class($proxy), 'setProduct');
$params = $method->getParameters();
$this->assertEquals(1, count($params));
$this->assertEquals('Doctrine\Tests\Models\ECommerce\ECommerceProduct', $params[0]->getClass()->getName());
}
/**
* Test that the proxy behaves in regard to methods like &foo() correctly
*/
public function testProxyRespectsMethodsWhichReturnValuesByReference() {
$proxy = $this->_proxyFactory->getProxy('Doctrine\Tests\Models\Forum\ForumEntry', null);
$method = new \ReflectionMethod(get_class($proxy), 'getTopicByReference');
$this->assertTrue($method->returnsReference());
}
public function testCreatesAssociationProxyAsSubclassOfTheOriginalOne()
{
$proxyClass = 'Proxies\__CG__\Doctrine\Tests\Models\ECommerce\ECommerceFeature';
$this->assertTrue(is_subclass_of($proxyClass, 'Doctrine\Tests\Models\ECommerce\ECommerceFeature'));
}
public function testAllowsConcurrentCreationOfBothProxyTypes()
{
$referenceProxyClass = 'Proxies\DoctrineTestsModelsECommerceECommerceFeatureProxy';
$associationProxyClass = 'Proxies\DoctrineTestsModelsECommerceECommerceFeatureAProxy';
$this->assertNotEquals($referenceProxyClass, $associationProxyClass);
}
public function testNonNamespacedProxyGeneration()
{
require_once dirname(__FILE__)."/fixtures/NonNamespacedProxies.php";
$className = "\DoctrineOrmTestEntity";
$proxyName = "DoctrineOrmTestEntity";
$classMetadata = new \Doctrine\ORM\Mapping\ClassMetadata($className);
$classMetadata->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$classMetadata->mapField(array('fieldName' => 'id', 'type' => 'integer'));
$classMetadata->setIdentifier(array('id'));
$this->_proxyFactory->generateProxyClasses(array($classMetadata));
$classCode = file_get_contents(dirname(__FILE__)."/generated/__CG__".$proxyName.".php");
$this->assertNotContains("class DoctrineOrmTestEntity extends \\\\DoctrineOrmTestEntity", $classCode);
$this->assertContains("class DoctrineOrmTestEntity extends \\DoctrineOrmTestEntity", $classCode);
}
public function testClassWithSleepProxyGeneration()
{
$className = "\Doctrine\Tests\ORM\Proxy\SleepClass";
$proxyName = "DoctrineTestsORMProxySleepClass";
$classMetadata = new \Doctrine\ORM\Mapping\ClassMetadata($className);
$classMetadata->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$classMetadata->mapField(array('fieldName' => 'id', 'type' => 'integer'));
$classMetadata->setIdentifier(array('id'));
$this->_proxyFactory->generateProxyClasses(array($classMetadata));
$classCode = file_get_contents(dirname(__FILE__)."/generated/__CG__".$proxyName.".php");
$this->assertEquals(1, substr_count($classCode, 'function __sleep'));
}
/**
* @group DDC-1771
*/
public function testSkipAbstractClassesOnGeneration()
{
$cm = new \Doctrine\ORM\Mapping\ClassMetadata(__NAMESPACE__ . '\\AbstractClass');
$cm->initializeReflection(new \Doctrine\Common\Persistence\Mapping\RuntimeReflectionService);
$this->assertNotNull($cm->reflClass);
$num = $this->_proxyFactory->generateProxyClasses(array($cm));
$this->assertEquals(0, $num, "No proxies generated.");
}
public function testNoConfigDir_ThrowsException()
{
$this->setExpectedException('Doctrine\ORM\Proxy\ProxyException');
new ProxyFactory($this->_getTestEntityManager(), null, null);
}
public function testNoNamespace_ThrowsException()
{
$this->setExpectedException('Doctrine\ORM\Proxy\ProxyException');
new ProxyFactory($this->_getTestEntityManager(), __DIR__ . '/generated', null);
}
protected function _getMockPersister()
{
$persister = $this->getMock('Doctrine\ORM\Persisters\BasicEntityPersister', array('load'), array(), '', false);
return $persister;
}
}
class SleepClass
{
public $id;
public function __sleep()
{
return array('id');
}
}
abstract class AbstractClass
{
}

View File

@ -1,13 +0,0 @@
<?php
/**
* @entity
*/
class DoctrineOrmTestEntity
{
/**
* @column(type="integer")
* @id
*/
public $id;
}