vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php line 207

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\ORM\Proxy;
  4. use Closure;
  5. use Doctrine\Common\Proxy\AbstractProxyFactory;
  6. use Doctrine\Common\Proxy\Proxy as CommonProxy;
  7. use Doctrine\Common\Proxy\ProxyDefinition;
  8. use Doctrine\Common\Proxy\ProxyGenerator;
  9. use Doctrine\Common\Util\ClassUtils;
  10. use Doctrine\ORM\EntityManagerInterface;
  11. use Doctrine\ORM\EntityNotFoundException;
  12. use Doctrine\ORM\Persisters\Entity\EntityPersister;
  13. use Doctrine\ORM\Proxy\Proxy as LegacyProxy;
  14. use Doctrine\ORM\UnitOfWork;
  15. use Doctrine\ORM\Utility\IdentifierFlattener;
  16. use Doctrine\Persistence\Mapping\ClassMetadata;
  17. use Doctrine\Persistence\Proxy;
  18. use ReflectionProperty;
  19. use Symfony\Component\VarExporter\ProxyHelper;
  20. use Symfony\Component\VarExporter\VarExporter;
  21. use function array_flip;
  22. use function str_replace;
  23. use function strpos;
  24. use function substr;
  25. use function uksort;
  26. /**
  27.  * This factory is used to create proxy objects for entities at runtime.
  28.  *
  29.  * @psalm-type AutogenerateMode = ProxyFactory::AUTOGENERATE_NEVER|ProxyFactory::AUTOGENERATE_ALWAYS|ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS|ProxyFactory::AUTOGENERATE_EVAL|ProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED
  30.  */
  31. class ProxyFactory extends AbstractProxyFactory
  32. {
  33.     private const PROXY_CLASS_TEMPLATE = <<<'EOPHP'
  34. <?php
  35. namespace <namespace>;
  36. /**
  37.  * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE'S PROXY GENERATOR
  38.  */
  39. class <proxyShortClassName> extends \<className> implements \<baseProxyInterface>
  40. {
  41.     <useLazyGhostTrait>
  42.     /**
  43.      * @internal
  44.      */
  45.     public bool $__isCloning = false;
  46.     public function __construct(?\Closure $initializer = null)
  47.     {
  48.         self::createLazyGhost($initializer, <skippedProperties>, $this);
  49.     }
  50.     public function __isInitialized(): bool
  51.     {
  52.         return isset($this->lazyObjectState) && $this->isLazyObjectInitialized();
  53.     }
  54.     public function __clone()
  55.     {
  56.         $this->__isCloning = true;
  57.         try {
  58.             $this->__doClone();
  59.         } finally {
  60.             $this->__isCloning = false;
  61.         }
  62.     }
  63.     public function __serialize(): array
  64.     {
  65.         <serializeImpl>
  66.     }
  67. }
  68. EOPHP;
  69.     /** @var EntityManagerInterface The EntityManager this factory is bound to. */
  70.     private $em;
  71.     /** @var UnitOfWork The UnitOfWork this factory uses to retrieve persisters */
  72.     private $uow;
  73.     /** @var string */
  74.     private $proxyNs;
  75.     /**
  76.      * The IdentifierFlattener used for manipulating identifiers
  77.      *
  78.      * @var IdentifierFlattener
  79.      */
  80.     private $identifierFlattener;
  81.     /**
  82.      * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
  83.      * connected to the given <tt>EntityManager</tt>.
  84.      *
  85.      * @param EntityManagerInterface $em           The EntityManager the new factory works for.
  86.      * @param string                 $proxyDir     The directory to use for the proxy classes. It must exist.
  87.      * @param string                 $proxyNs      The namespace to use for the proxy classes.
  88.      * @param bool|int               $autoGenerate The strategy for automatically generating proxy classes. Possible
  89.      *                                             values are constants of {@see ProxyFactory::AUTOGENERATE_*}.
  90.      * @psalm-param bool|AutogenerateMode $autoGenerate
  91.      */
  92.     public function __construct(EntityManagerInterface $em$proxyDir$proxyNs$autoGenerate self::AUTOGENERATE_NEVER)
  93.     {
  94.         $proxyGenerator = new ProxyGenerator($proxyDir$proxyNs);
  95.         if ($em->getConfiguration()->isLazyGhostObjectEnabled()) {
  96.             $proxyGenerator->setPlaceholder('baseProxyInterface'Proxy::class);
  97.             $proxyGenerator->setPlaceholder('useLazyGhostTrait'Closure::fromCallable([$this'generateUseLazyGhostTrait']));
  98.             $proxyGenerator->setPlaceholder('skippedProperties'Closure::fromCallable([$this'generateSkippedProperties']));
  99.             $proxyGenerator->setPlaceholder('serializeImpl'Closure::fromCallable([$this'generateSerializeImpl']));
  100.             $proxyGenerator->setProxyClassTemplate(self::PROXY_CLASS_TEMPLATE);
  101.         } else {
  102.             $proxyGenerator->setPlaceholder('baseProxyInterface'LegacyProxy::class);
  103.         }
  104.         parent::__construct($proxyGenerator$em->getMetadataFactory(), $autoGenerate);
  105.         $this->em                  $em;
  106.         $this->uow                 $em->getUnitOfWork();
  107.         $this->proxyNs             $proxyNs;
  108.         $this->identifierFlattener = new IdentifierFlattener($this->uow$em->getMetadataFactory());
  109.     }
  110.     /**
  111.      * {@inheritDoc}
  112.      */
  113.     protected function skipClass(ClassMetadata $metadata)
  114.     {
  115.         return $metadata->isMappedSuperclass
  116.             || $metadata->isEmbeddedClass
  117.             || $metadata->getReflectionClass()->isAbstract();
  118.     }
  119.     /**
  120.      * {@inheritDoc}
  121.      */
  122.     protected function createProxyDefinition($className)
  123.     {
  124.         $classMetadata   $this->em->getClassMetadata($className);
  125.         $entityPersister $this->uow->getEntityPersister($className);
  126.         if ($this->em->getConfiguration()->isLazyGhostObjectEnabled()) {
  127.             $initializer $this->createLazyInitializer($classMetadata$entityPersister);
  128.             $cloner      = static function (): void {
  129.             };
  130.         } else {
  131.             $initializer $this->createInitializer($classMetadata$entityPersister);
  132.             $cloner      $this->createCloner($classMetadata$entityPersister);
  133.         }
  134.         return new ProxyDefinition(
  135.             ClassUtils::generateProxyClassName($className$this->proxyNs),
  136.             $classMetadata->getIdentifierFieldNames(),
  137.             $classMetadata->getReflectionProperties(),
  138.             $initializer,
  139.             $cloner
  140.         );
  141.     }
  142.     /**
  143.      * Creates a closure capable of initializing a proxy
  144.      *
  145.      * @psalm-return Closure(CommonProxy):void
  146.      *
  147.      * @throws EntityNotFoundException
  148.      */
  149.     private function createInitializer(ClassMetadata $classMetadataEntityPersister $entityPersister): Closure
  150.     {
  151.         $wakeupProxy $classMetadata->getReflectionClass()->hasMethod('__wakeup');
  152.         return function (CommonProxy $proxy) use ($entityPersister$classMetadata$wakeupProxy): void {
  153.             $initializer $proxy->__getInitializer();
  154.             $cloner      $proxy->__getCloner();
  155.             $proxy->__setInitializer(null);
  156.             $proxy->__setCloner(null);
  157.             if ($proxy->__isInitialized()) {
  158.                 return;
  159.             }
  160.             $properties $proxy->__getLazyProperties();
  161.             foreach ($properties as $propertyName => $property) {
  162.                 if (! isset($proxy->$propertyName)) {
  163.                     $proxy->$propertyName $properties[$propertyName];
  164.                 }
  165.             }
  166.             $proxy->__setInitialized(true);
  167.             if ($wakeupProxy) {
  168.                 $proxy->__wakeup();
  169.             }
  170.             $identifier $classMetadata->getIdentifierValues($proxy);
  171.             if ($entityPersister->loadById($identifier$proxy) === null) {
  172.                 $proxy->__setInitializer($initializer);
  173.                 $proxy->__setCloner($cloner);
  174.                 $proxy->__setInitialized(false);
  175.                 throw EntityNotFoundException::fromClassNameAndIdentifier(
  176.                     $classMetadata->getName(),
  177.                     $this->identifierFlattener->flattenIdentifier($classMetadata$identifier)
  178.                 );
  179.             }
  180.         };
  181.     }
  182.     /**
  183.      * Creates a closure capable of initializing a proxy
  184.      *
  185.      * @return Closure(Proxy):void
  186.      *
  187.      * @throws EntityNotFoundException
  188.      */
  189.     private function createLazyInitializer(ClassMetadata $classMetadataEntityPersister $entityPersister): Closure
  190.     {
  191.         return function (Proxy $proxy) use ($entityPersister$classMetadata): void {
  192.             $identifier $classMetadata->getIdentifierValues($proxy);
  193.             $entity     $entityPersister->loadById($identifier$proxy->__isCloning null $proxy);
  194.             if ($entity === null) {
  195.                 throw EntityNotFoundException::fromClassNameAndIdentifier(
  196.                     $classMetadata->getName(),
  197.                     $this->identifierFlattener->flattenIdentifier($classMetadata$identifier)
  198.                 );
  199.             }
  200.             if (! $proxy->__isCloning) {
  201.                 return;
  202.             }
  203.             $class $entityPersister->getClassMetadata();
  204.             foreach ($class->getReflectionProperties() as $property) {
  205.                 if (! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
  206.                     continue;
  207.                 }
  208.                 $property->setAccessible(true);
  209.                 $property->setValue($proxy$property->getValue($entity));
  210.             }
  211.         };
  212.     }
  213.     /**
  214.      * Creates a closure capable of finalizing state a cloned proxy
  215.      *
  216.      * @psalm-return Closure(CommonProxy):void
  217.      *
  218.      * @throws EntityNotFoundException
  219.      */
  220.     private function createCloner(ClassMetadata $classMetadataEntityPersister $entityPersister): Closure
  221.     {
  222.         return function (CommonProxy $proxy) use ($entityPersister$classMetadata): void {
  223.             if ($proxy->__isInitialized()) {
  224.                 return;
  225.             }
  226.             $proxy->__setInitialized(true);
  227.             $proxy->__setInitializer(null);
  228.             $class      $entityPersister->getClassMetadata();
  229.             $identifier $classMetadata->getIdentifierValues($proxy);
  230.             $original   $entityPersister->loadById($identifier);
  231.             if ($original === null) {
  232.                 throw EntityNotFoundException::fromClassNameAndIdentifier(
  233.                     $classMetadata->getName(),
  234.                     $this->identifierFlattener->flattenIdentifier($classMetadata$identifier)
  235.                 );
  236.             }
  237.             foreach ($class->getReflectionProperties() as $property) {
  238.                 if (! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
  239.                     continue;
  240.                 }
  241.                 $property->setAccessible(true);
  242.                 $property->setValue($proxy$property->getValue($original));
  243.             }
  244.         };
  245.     }
  246.     private function generateUseLazyGhostTrait(ClassMetadata $class): string
  247.     {
  248.         $code ProxyHelper::generateLazyGhost($class->getReflectionClass());
  249.         $code substr($code+ (int) strpos($code"\n{"));
  250.         $code substr($code0, (int) strpos($code"\n}"));
  251.         $code str_replace('LazyGhostTrait;'str_replace("\n    ""\n"'LazyGhostTrait {
  252.             initializeLazyObject as __load;
  253.             setLazyObjectAsInitialized as public __setInitialized;
  254.             isLazyObjectInitialized as private;
  255.             createLazyGhost as private;
  256.             resetLazyObject as private;
  257.             __clone as private __doClone;
  258.         }'), $code);
  259.         return $code;
  260.     }
  261.     private function generateSkippedProperties(ClassMetadata $class): string
  262.     {
  263.         $skippedProperties = ['__isCloning' => true];
  264.         $identifiers       array_flip($class->getIdentifierFieldNames());
  265.         $filter            ReflectionProperty::IS_PUBLIC ReflectionProperty::IS_PROTECTED ReflectionProperty::IS_PRIVATE;
  266.         $reflector         $class->getReflectionClass();
  267.         while ($reflector) {
  268.             foreach ($reflector->getProperties($filter) as $property) {
  269.                 $name $property->getName();
  270.                 if ($property->isStatic() || (($class->hasField($name) || $class->hasAssociation($name)) && ! isset($identifiers[$name]))) {
  271.                     continue;
  272.                 }
  273.                 $prefix $property->isPrivate() ? "\0" $property->getDeclaringClass()->getName() . "\0" : ($property->isProtected() ? "\0*\0" '');
  274.                 $skippedProperties[$prefix $name] = true;
  275.             }
  276.             $filter    ReflectionProperty::IS_PRIVATE;
  277.             $reflector $reflector->getParentClass();
  278.         }
  279.         uksort($skippedProperties'strnatcmp');
  280.         $code VarExporter::export($skippedProperties);
  281.         $code str_replace(VarExporter::export($class->getName()), 'parent::class'$code);
  282.         $code str_replace("\n""\n        "$code);
  283.         return $code;
  284.     }
  285.     private function generateSerializeImpl(ClassMetadata $class): string
  286.     {
  287.         $reflector  $class->getReflectionClass();
  288.         $properties $reflector->hasMethod('__serialize') ? 'parent::__serialize()' '(array) $this';
  289.         $code '$properties = ' $properties ';
  290.         unset($properties["\0" . self::class . "\0lazyObjectState"], $properties[\'__isCloning\']);
  291.         ';
  292.         if ($reflector->hasMethod('__serialize') || ! $reflector->hasMethod('__sleep')) {
  293.             return $code 'return $properties;';
  294.         }
  295.         return $code '$data = [];
  296.         foreach (parent::__sleep() as $name) {
  297.             $value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0' $reflector->getName() . '\0$name"] ?? $k = null;
  298.             if (null === $k) {
  299.                 trigger_error(sprintf(\'serialize(): "%s" returned as member variable from __sleep() but does not exist\', $name), \E_USER_NOTICE);
  300.             } else {
  301.                 $data[$k] = $value;
  302.             }
  303.         }
  304.         return $data;';
  305.     }
  306. }