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

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