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

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