. */ namespace Doctrine\ORM\Proxy; use Doctrine\Common\Persistence\Mapping\ClassMetadata; use Doctrine\Common\Proxy\AbstractProxyFactory; 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\ORMInvalidArgumentException; use Doctrine\ORM\Persisters\BasicEntityPersister; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityNotFoundException; /** * This factory is used to create proxy objects for entities at runtime. * * @author Roman Borschel * @author Giorgio Sironi * @author Marco Pivetta * @since 2.0 */ class ProxyFactory extends AbstractProxyFactory { /** * @var \Doctrine\ORM\EntityManager The EntityManager this factory is bound to. */ private $em; /** * @var \Doctrine\ORM\UnitOfWork The UnitOfWork this factory uses to retrieve persisters */ private $uow; /** * @var string */ private $proxyNs; /** * Initializes a new instance of the ProxyFactory class that is * connected to the given EntityManager. * * @param \Doctrine\ORM\EntityManager $em The EntityManager the new factory works for. * @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. */ public function __construct(EntityManager $em, $proxyDir, $proxyNs, $autoGenerate = false) { $proxyGenerator = new ProxyGenerator($proxyDir, $proxyNs); $proxyGenerator->setPlaceholder('baseProxyInterface', 'Doctrine\ORM\Proxy\Proxy'); parent::__construct($proxyGenerator, $em->getMetadataFactory(), $autoGenerate); $this->em = $em; $this->uow = $em->getUnitOfWork(); $this->proxyNs = $proxyNs; } /** * {@inheritDoc} */ protected function skipClass(ClassMetadata $metadata) { /* @var $metadata \Doctrine\ORM\Mapping\ClassMetadataInfo */ return $metadata->isMappedSuperclass || $metadata->getReflectionClass()->isAbstract(); } /** * {@inheritDoc} */ protected function createProxyDefinition($className) { $classMetadata = $this->em->getClassMetadata($className); $entityPersister = $this->uow->getEntityPersister($className); return new ProxyDefinition( ClassUtils::generateProxyClassName($className, $this->proxyNs), $classMetadata->getIdentifierFieldNames(), $classMetadata->getReflectionProperties(), $this->createInitializer($classMetadata, $entityPersister), $this->createCloner($classMetadata, $entityPersister) ); } /** * Creates a closure capable of initializing a proxy * * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata * @param \Doctrine\ORM\Persisters\BasicEntityPersister $entityPersister * * @return \Closure * * @throws \Doctrine\ORM\EntityNotFoundException */ private function createInitializer(ClassMetadata $classMetadata, BasicEntityPersister $entityPersister) { if ($classMetadata->getReflectionClass()->hasMethod('__wakeup')) { return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { $proxy->__setInitializer(null); $proxy->__setCloner(null); if ($proxy->__isInitialized()) { return; } $properties = $proxy->__getLazyProperties(); foreach ($properties as $propertyName => $property) { if (!isset($proxy->$propertyName)) { $proxy->$propertyName = $properties[$propertyName]; } } $proxy->__setInitialized(true); $proxy->__wakeup(); if (null === $entityPersister->load($classMetadata->getIdentifierValues($proxy), $proxy)) { throw new EntityNotFoundException(); } }; } return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { $proxy->__setInitializer(null); $proxy->__setCloner(null); if ($proxy->__isInitialized()) { return; } $properties = $proxy->__getLazyProperties(); 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(); } }; } /** * Creates a closure capable of finalizing state a cloned proxy * * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $classMetadata * @param \Doctrine\ORM\Persisters\BasicEntityPersister $entityPersister * * @return \Closure * * @throws \Doctrine\ORM\EntityNotFoundException */ private function createCloner(ClassMetadata $classMetadata, BasicEntityPersister $entityPersister) { return function (BaseProxy $proxy) use ($entityPersister, $classMetadata) { if ($proxy->__isInitialized()) { return; } $proxy->__setInitialized(true); $proxy->__setInitializer(null); $class = $entityPersister->getClassMetadata(); $original = $entityPersister->load($classMetadata->getIdentifierValues($proxy)); if (null === $original) { throw new EntityNotFoundException(); } foreach ($class->getReflectionClass()->getProperties() as $reflectionProperty) { $propertyName = $reflectionProperty->getName(); if ($class->hasField($propertyName) || $class->hasAssociation($propertyName)) { $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($proxy, $reflectionProperty->getValue($original)); } } }; } }