vendor/doctrine/common/src/Proxy/AbstractProxyFactory.php line 107

Open in your IDE?
  1. <?php
  2. namespace Doctrine\Common\Proxy;
  3. use Doctrine\Common\Proxy\Exception\InvalidArgumentException;
  4. use Doctrine\Common\Proxy\Exception\OutOfBoundsException;
  5. use Doctrine\Common\Util\ClassUtils;
  6. use Doctrine\Persistence\Mapping\ClassMetadata;
  7. use Doctrine\Persistence\Mapping\ClassMetadataFactory;
  8. use function class_exists;
  9. use function file_exists;
  10. use function filemtime;
  11. use function in_array;
  12. /**
  13.  * Abstract factory for proxy objects.
  14.  */
  15. abstract class AbstractProxyFactory
  16. {
  17.     /**
  18.      * Never autogenerate a proxy and rely that it was generated by some
  19.      * process before deployment.
  20.      */
  21.     public const AUTOGENERATE_NEVER 0;
  22.     /**
  23.      * Always generates a new proxy in every request.
  24.      *
  25.      * This is only sane during development.
  26.      */
  27.     public const AUTOGENERATE_ALWAYS 1;
  28.     /**
  29.      * Autogenerate the proxy class when the proxy file does not exist.
  30.      *
  31.      * This strategy causes a file_exists() call whenever any proxy is used the
  32.      * first time in a request.
  33.      */
  34.     public const AUTOGENERATE_FILE_NOT_EXISTS 2;
  35.     /**
  36.      * Generate the proxy classes using eval().
  37.      *
  38.      * This strategy is only sane for development, and even then it gives me
  39.      * the creeps a little.
  40.      */
  41.     public const AUTOGENERATE_EVAL 3;
  42.     /**
  43.      * Autogenerate the proxy class when the proxy file does not exist or
  44.      * when the proxied file changed.
  45.      *
  46.      * This strategy causes a file_exists() call whenever any proxy is used the
  47.      * first time in a request. When the proxied file is changed, the proxy will
  48.      * be updated.
  49.      */
  50.     public const AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED 4;
  51.     private const AUTOGENERATE_MODES = [
  52.         self::AUTOGENERATE_NEVER,
  53.         self::AUTOGENERATE_ALWAYS,
  54.         self::AUTOGENERATE_FILE_NOT_EXISTS,
  55.         self::AUTOGENERATE_EVAL,
  56.         self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED,
  57.     ];
  58.     /** @var ClassMetadataFactory */
  59.     private $metadataFactory;
  60.     /** @var ProxyGenerator the proxy generator responsible for creating the proxy classes/files. */
  61.     private $proxyGenerator;
  62.     /** @var int Whether to automatically (re)generate proxy classes. */
  63.     private $autoGenerate;
  64.     /** @var ProxyDefinition[] */
  65.     private $definitions = [];
  66.     /**
  67.      * @param bool|int $autoGenerate
  68.      *
  69.      * @throws InvalidArgumentException When auto generate mode is not valid.
  70.      */
  71.     public function __construct(ProxyGenerator $proxyGeneratorClassMetadataFactory $metadataFactory$autoGenerate)
  72.     {
  73.         $this->proxyGenerator  $proxyGenerator;
  74.         $this->metadataFactory $metadataFactory;
  75.         $this->autoGenerate    = (int) $autoGenerate;
  76.         if (! in_array($this->autoGenerateself::AUTOGENERATE_MODEStrue)) {
  77.             throw InvalidArgumentException::invalidAutoGenerateMode($autoGenerate);
  78.         }
  79.     }
  80.     /**
  81.      * Gets a reference proxy instance for the entity of the given type and identified by
  82.      * the given identifier.
  83.      *
  84.      * @param  string       $className
  85.      * @param  array<mixed> $identifier
  86.      *
  87.      * @return Proxy
  88.      *
  89.      * @throws OutOfBoundsException
  90.      */
  91.     public function getProxy($className, array $identifier)
  92.     {
  93.         $definition $this->definitions[$className] ?? $this->getProxyDefinition($className);
  94.         $fqcn       $definition->proxyClassName;
  95.         $proxy      = new $fqcn($definition->initializer$definition->cloner);
  96.         foreach ($definition->identifierFields as $idField) {
  97.             if (! isset($identifier[$idField])) {
  98.                 throw OutOfBoundsException::missingPrimaryKeyValue($className$idField);
  99.             }
  100.             $definition->reflectionFields[$idField]->setValue($proxy$identifier[$idField]);
  101.         }
  102.         return $proxy;
  103.     }
  104.     /**
  105.      * Generates proxy classes for all given classes.
  106.      *
  107.      * @param ClassMetadata[] $classes  The classes (ClassMetadata instances)
  108.      *  for which to generate proxies.
  109.      * @param string          $proxyDir The target directory of the proxy classes. If not specified, the
  110.      *                                  directory configured on the Configuration of the EntityManager used
  111.      *                                  by this factory is used.
  112.      *
  113.      * @return int Number of generated proxies.
  114.      */
  115.     public function generateProxyClasses(array $classes$proxyDir null)
  116.     {
  117.         $generated 0;
  118.         foreach ($classes as $class) {
  119.             if ($this->skipClass($class)) {
  120.                 continue;
  121.             }
  122.             $proxyFileName $this->proxyGenerator->getProxyFileName($class->getName(), $proxyDir);
  123.             $this->proxyGenerator->generateProxyClass($class$proxyFileName);
  124.             $generated += 1;
  125.         }
  126.         return $generated;
  127.     }
  128.     /**
  129.      * Reset initialization/cloning logic for an un-initialized proxy
  130.      *
  131.      * @return Proxy
  132.      *
  133.      * @throws InvalidArgumentException
  134.      */
  135.     public function resetUninitializedProxy(Proxy $proxy)
  136.     {
  137.         if ($proxy->__isInitialized()) {
  138.             throw InvalidArgumentException::unitializedProxyExpected($proxy);
  139.         }
  140.         $className  ClassUtils::getClass($proxy);
  141.         $definition $this->definitions[$className] ?? $this->getProxyDefinition($className);
  142.         $proxy->__setInitializer($definition->initializer);
  143.         $proxy->__setCloner($definition->cloner);
  144.         return $proxy;
  145.     }
  146.     /**
  147.      * Get a proxy definition for the given class name.
  148.      *
  149.      * @param string $className
  150.      * @psalm-param class-string $className
  151.      *
  152.      * @return ProxyDefinition
  153.      */
  154.     private function getProxyDefinition($className)
  155.     {
  156.         $classMetadata $this->metadataFactory->getMetadataFor($className);
  157.         $className     $classMetadata->getName(); // aliases and case sensitivity
  158.         $this->definitions[$className] = $this->createProxyDefinition($className);
  159.         $proxyClassName                $this->definitions[$className]->proxyClassName;
  160.         if (! class_exists($proxyClassNamefalse)) {
  161.             $fileName $this->proxyGenerator->getProxyFileName($className);
  162.             switch ($this->autoGenerate) {
  163.                 case self::AUTOGENERATE_NEVER:
  164.                     require $fileName;
  165.                     break;
  166.                 case self::AUTOGENERATE_FILE_NOT_EXISTS:
  167.                     if (! file_exists($fileName)) {
  168.                         $this->proxyGenerator->generateProxyClass($classMetadata$fileName);
  169.                     }
  170.                     require $fileName;
  171.                     break;
  172.                 case self::AUTOGENERATE_ALWAYS:
  173.                     $this->proxyGenerator->generateProxyClass($classMetadata$fileName);
  174.                     require $fileName;
  175.                     break;
  176.                 case self::AUTOGENERATE_EVAL:
  177.                     $this->proxyGenerator->generateProxyClass($classMetadatafalse);
  178.                     break;
  179.                 case self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED:
  180.                     if (! file_exists($fileName) || filemtime($fileName) < filemtime($classMetadata->getReflectionClass()->getFileName())) {
  181.                         $this->proxyGenerator->generateProxyClass($classMetadata$fileName);
  182.                     }
  183.                     require $fileName;
  184.                     break;
  185.             }
  186.         }
  187.         return $this->definitions[$className];
  188.     }
  189.     /**
  190.      * Determine if this class should be skipped during proxy generation.
  191.      *
  192.      * @return bool
  193.      */
  194.     abstract protected function skipClass(ClassMetadata $metadata);
  195.     /**
  196.      * @param string $className
  197.      * @psalm-param class-string $className
  198.      *
  199.      * @return ProxyDefinition
  200.      */
  201.     abstract protected function createProxyDefinition($className);
  202. }