Chris@0: . Chris@0: */ Chris@0: Chris@0: namespace Doctrine\Common\Proxy; Chris@0: Chris@0: use Doctrine\Common\Persistence\Mapping\ClassMetadata; Chris@0: use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory; Chris@0: use Doctrine\Common\Proxy\Exception\InvalidArgumentException; Chris@0: use Doctrine\Common\Proxy\Exception\OutOfBoundsException; Chris@0: use Doctrine\Common\Util\ClassUtils; Chris@0: Chris@0: /** Chris@0: * Abstract factory for proxy objects. Chris@0: * Chris@0: * @author Benjamin Eberlei Chris@0: */ Chris@0: abstract class AbstractProxyFactory Chris@0: { Chris@0: /** Chris@0: * Never autogenerate a proxy and rely that it was generated by some Chris@0: * process before deployment. Chris@0: * Chris@0: * @var integer Chris@0: */ Chris@0: const AUTOGENERATE_NEVER = 0; Chris@0: Chris@0: /** Chris@0: * Always generates a new proxy in every request. Chris@0: * Chris@0: * This is only sane during development. Chris@0: * Chris@0: * @var integer Chris@0: */ Chris@0: const AUTOGENERATE_ALWAYS = 1; Chris@0: Chris@0: /** Chris@0: * Autogenerate the proxy class when the proxy file does not exist. Chris@0: * Chris@0: * This strategy causes a file exists call whenever any proxy is used the Chris@0: * first time in a request. Chris@0: * Chris@0: * @var integer Chris@0: */ Chris@0: const AUTOGENERATE_FILE_NOT_EXISTS = 2; Chris@0: Chris@0: /** Chris@0: * Generate the proxy classes using eval(). Chris@0: * Chris@0: * This strategy is only sane for development, and even then it gives me Chris@0: * the creeps a little. Chris@0: * Chris@0: * @var integer Chris@0: */ Chris@0: const AUTOGENERATE_EVAL = 3; Chris@0: Chris@0: /** Chris@0: * @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory Chris@0: */ Chris@0: private $metadataFactory; Chris@0: Chris@0: /** Chris@0: * @var \Doctrine\Common\Proxy\ProxyGenerator the proxy generator responsible for creating the proxy classes/files. Chris@0: */ Chris@0: private $proxyGenerator; Chris@0: Chris@0: /** Chris@0: * @var bool Whether to automatically (re)generate proxy classes. Chris@0: */ Chris@0: private $autoGenerate; Chris@0: Chris@0: /** Chris@0: * @var \Doctrine\Common\Proxy\ProxyDefinition[] Chris@0: */ Chris@0: private $definitions = []; Chris@0: Chris@0: /** Chris@0: * @param \Doctrine\Common\Proxy\ProxyGenerator $proxyGenerator Chris@0: * @param \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory Chris@0: * @param bool|int $autoGenerate Chris@0: */ Chris@0: public function __construct(ProxyGenerator $proxyGenerator, ClassMetadataFactory $metadataFactory, $autoGenerate) Chris@0: { Chris@0: $this->proxyGenerator = $proxyGenerator; Chris@0: $this->metadataFactory = $metadataFactory; Chris@0: $this->autoGenerate = (int)$autoGenerate; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets a reference proxy instance for the entity of the given type and identified by Chris@0: * the given identifier. Chris@0: * Chris@0: * @param string $className Chris@0: * @param array $identifier Chris@0: * Chris@0: * @return \Doctrine\Common\Proxy\Proxy Chris@0: * Chris@0: * @throws \Doctrine\Common\Proxy\Exception\OutOfBoundsException Chris@0: */ Chris@0: public function getProxy($className, array $identifier) Chris@0: { Chris@0: $definition = isset($this->definitions[$className]) Chris@0: ? $this->definitions[$className] Chris@0: : $this->getProxyDefinition($className); Chris@0: $fqcn = $definition->proxyClassName; Chris@0: $proxy = new $fqcn($definition->initializer, $definition->cloner); Chris@0: Chris@0: foreach ($definition->identifierFields as $idField) { Chris@0: if (! isset($identifier[$idField])) { Chris@0: throw OutOfBoundsException::missingPrimaryKeyValue($className, $idField); Chris@0: } Chris@0: Chris@0: $definition->reflectionFields[$idField]->setValue($proxy, $identifier[$idField]); Chris@0: } Chris@0: Chris@0: return $proxy; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Generates proxy classes for all given classes. Chris@0: * Chris@0: * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata[] $classes The classes (ClassMetadata instances) Chris@0: * for which to generate proxies. Chris@0: * @param string $proxyDir The target directory of the proxy classes. If not specified, the Chris@0: * directory configured on the Configuration of the EntityManager used Chris@0: * by this factory is used. Chris@0: * @return int Number of generated proxies. Chris@0: */ Chris@0: public function generateProxyClasses(array $classes, $proxyDir = null) Chris@0: { Chris@0: $generated = 0; Chris@0: Chris@0: foreach ($classes as $class) { Chris@0: if ($this->skipClass($class)) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: $proxyFileName = $this->proxyGenerator->getProxyFileName($class->getName(), $proxyDir); Chris@0: Chris@0: $this->proxyGenerator->generateProxyClass($class, $proxyFileName); Chris@0: Chris@0: $generated += 1; Chris@0: } Chris@0: Chris@0: return $generated; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Reset initialization/cloning logic for an un-initialized proxy Chris@0: * Chris@0: * @param \Doctrine\Common\Proxy\Proxy $proxy Chris@0: * Chris@0: * @return \Doctrine\Common\Proxy\Proxy Chris@0: * Chris@0: * @throws \Doctrine\Common\Proxy\Exception\InvalidArgumentException Chris@0: */ Chris@0: public function resetUninitializedProxy(Proxy $proxy) Chris@0: { Chris@0: if ($proxy->__isInitialized()) { Chris@0: throw InvalidArgumentException::unitializedProxyExpected($proxy); Chris@0: } Chris@0: Chris@0: $className = ClassUtils::getClass($proxy); Chris@0: $definition = isset($this->definitions[$className]) Chris@0: ? $this->definitions[$className] Chris@0: : $this->getProxyDefinition($className); Chris@0: Chris@0: $proxy->__setInitializer($definition->initializer); Chris@0: $proxy->__setCloner($definition->cloner); Chris@0: Chris@0: return $proxy; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Get a proxy definition for the given class name. Chris@0: * Chris@0: * @param string $className Chris@0: * Chris@0: * @return ProxyDefinition Chris@0: */ Chris@0: private function getProxyDefinition($className) Chris@0: { Chris@0: $classMetadata = $this->metadataFactory->getMetadataFor($className); Chris@0: $className = $classMetadata->getName(); // aliases and case sensitivity Chris@0: Chris@0: $this->definitions[$className] = $this->createProxyDefinition($className); Chris@0: $proxyClassName = $this->definitions[$className]->proxyClassName; Chris@0: Chris@0: if ( ! class_exists($proxyClassName, false)) { Chris@0: $fileName = $this->proxyGenerator->getProxyFileName($className); Chris@0: Chris@0: switch ($this->autoGenerate) { Chris@0: case self::AUTOGENERATE_NEVER: Chris@0: require $fileName; Chris@0: break; Chris@0: Chris@0: case self::AUTOGENERATE_FILE_NOT_EXISTS: Chris@0: if ( ! file_exists($fileName)) { Chris@0: $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); Chris@0: } Chris@0: require $fileName; Chris@0: break; Chris@0: Chris@0: case self::AUTOGENERATE_ALWAYS: Chris@0: $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); Chris@0: require $fileName; Chris@0: break; Chris@0: Chris@0: case self::AUTOGENERATE_EVAL: Chris@0: $this->proxyGenerator->generateProxyClass($classMetadata, false); Chris@0: break; Chris@0: } Chris@0: } Chris@0: Chris@0: return $this->definitions[$className]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Determine if this class should be skipped during proxy generation. Chris@0: * Chris@0: * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $metadata Chris@0: * Chris@0: * @return bool Chris@0: */ Chris@0: abstract protected function skipClass(ClassMetadata $metadata); Chris@0: Chris@0: /** Chris@0: * @param string $className Chris@0: * Chris@0: * @return ProxyDefinition Chris@0: */ Chris@0: abstract protected function createProxyDefinition($className); Chris@0: }