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

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 BaseProxy;
  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\UnitOfWork;
  14. use Doctrine\ORM\Utility\IdentifierFlattener;
  15. use Doctrine\Persistence\Mapping\ClassMetadata;
  16. /**
  17.  * This factory is used to create proxy objects for entities at runtime.
  18.  *
  19.  * @psalm-type AutogenerateMode = AbstractProxyFactory::AUTOGENERATE_NEVER|AbstractProxyFactory::AUTOGENERATE_ALWAYS|AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS|AbstractProxyFactory::AUTOGENERATE_EVAL|AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED
  20.  */
  21. class ProxyFactory extends AbstractProxyFactory
  22. {
  23.     /** @var EntityManagerInterface The EntityManager this factory is bound to. */
  24.     private $em;
  25.     /** @var UnitOfWork The UnitOfWork this factory uses to retrieve persisters */
  26.     private $uow;
  27.     /** @var string */
  28.     private $proxyNs;
  29.     /**
  30.      * The IdentifierFlattener used for manipulating identifiers
  31.      *
  32.      * @var IdentifierFlattener
  33.      */
  34.     private $identifierFlattener;
  35.     /**
  36.      * Initializes a new instance of the <tt>ProxyFactory</tt> class that is
  37.      * connected to the given <tt>EntityManager</tt>.
  38.      *
  39.      * @param EntityManagerInterface $em           The EntityManager the new factory works for.
  40.      * @param string                 $proxyDir     The directory to use for the proxy classes. It must exist.
  41.      * @param string                 $proxyNs      The namespace to use for the proxy classes.
  42.      * @param bool|int               $autoGenerate The strategy for automatically generating proxy classes. Possible
  43.      *                                             values are constants of {@see AbstractProxyFactory}.
  44.      * @psalm-param bool|AutogenerateMode $autoGenerate
  45.      */
  46.     public function __construct(EntityManagerInterface $em$proxyDir$proxyNs$autoGenerate AbstractProxyFactory::AUTOGENERATE_NEVER)
  47.     {
  48.         $proxyGenerator = new ProxyGenerator($proxyDir$proxyNs);
  49.         $proxyGenerator->setPlaceholder('baseProxyInterface'Proxy::class);
  50.         parent::__construct($proxyGenerator$em->getMetadataFactory(), $autoGenerate);
  51.         $this->em                  $em;
  52.         $this->uow                 $em->getUnitOfWork();
  53.         $this->proxyNs             $proxyNs;
  54.         $this->identifierFlattener = new IdentifierFlattener($this->uow$em->getMetadataFactory());
  55.     }
  56.     /**
  57.      * {@inheritDoc}
  58.      */
  59.     protected function skipClass(ClassMetadata $metadata)
  60.     {
  61.         return $metadata->isMappedSuperclass
  62.             || $metadata->isEmbeddedClass
  63.             || $metadata->getReflectionClass()->isAbstract();
  64.     }
  65.     /**
  66.      * {@inheritDoc}
  67.      */
  68.     protected function createProxyDefinition($className)
  69.     {
  70.         $classMetadata   $this->em->getClassMetadata($className);
  71.         $entityPersister $this->uow->getEntityPersister($className);
  72.         return new ProxyDefinition(
  73.             ClassUtils::generateProxyClassName($className$this->proxyNs),
  74.             $classMetadata->getIdentifierFieldNames(),
  75.             $classMetadata->getReflectionProperties(),
  76.             $this->createInitializer($classMetadata$entityPersister),
  77.             $this->createCloner($classMetadata$entityPersister)
  78.         );
  79.     }
  80.     /**
  81.      * Creates a closure capable of initializing a proxy
  82.      *
  83.      * @psalm-return Closure(BaseProxy):void
  84.      *
  85.      * @throws EntityNotFoundException
  86.      */
  87.     private function createInitializer(ClassMetadata $classMetadataEntityPersister $entityPersister): Closure
  88.     {
  89.         $wakeupProxy $classMetadata->getReflectionClass()->hasMethod('__wakeup');
  90.         return function (BaseProxy $proxy) use ($entityPersister$classMetadata$wakeupProxy): void {
  91.             $initializer $proxy->__getInitializer();
  92.             $cloner      $proxy->__getCloner();
  93.             $proxy->__setInitializer(null);
  94.             $proxy->__setCloner(null);
  95.             if ($proxy->__isInitialized()) {
  96.                 return;
  97.             }
  98.             $properties $proxy->__getLazyProperties();
  99.             foreach ($properties as $propertyName => $property) {
  100.                 if (! isset($proxy->$propertyName)) {
  101.                     $proxy->$propertyName $properties[$propertyName];
  102.                 }
  103.             }
  104.             $proxy->__setInitialized(true);
  105.             if ($wakeupProxy) {
  106.                 $proxy->__wakeup();
  107.             }
  108.             $identifier $classMetadata->getIdentifierValues($proxy);
  109.             if ($entityPersister->loadById($identifier$proxy) === null) {
  110.                 $proxy->__setInitializer($initializer);
  111.                 $proxy->__setCloner($cloner);
  112.                 $proxy->__setInitialized(false);
  113.                 throw EntityNotFoundException::fromClassNameAndIdentifier(
  114.                     $classMetadata->getName(),
  115.                     $this->identifierFlattener->flattenIdentifier($classMetadata$identifier)
  116.                 );
  117.             }
  118.         };
  119.     }
  120.     /**
  121.      * Creates a closure capable of finalizing state a cloned proxy
  122.      *
  123.      * @psalm-return Closure(BaseProxy):void
  124.      *
  125.      * @throws EntityNotFoundException
  126.      */
  127.     private function createCloner(ClassMetadata $classMetadataEntityPersister $entityPersister): Closure
  128.     {
  129.         return function (BaseProxy $proxy) use ($entityPersister$classMetadata): void {
  130.             if ($proxy->__isInitialized()) {
  131.                 return;
  132.             }
  133.             $proxy->__setInitialized(true);
  134.             $proxy->__setInitializer(null);
  135.             $class      $entityPersister->getClassMetadata();
  136.             $identifier $classMetadata->getIdentifierValues($proxy);
  137.             $original   $entityPersister->loadById($identifier);
  138.             if ($original === null) {
  139.                 throw EntityNotFoundException::fromClassNameAndIdentifier(
  140.                     $classMetadata->getName(),
  141.                     $this->identifierFlattener->flattenIdentifier($classMetadata$identifier)
  142.                 );
  143.             }
  144.             foreach ($class->getReflectionProperties() as $property) {
  145.                 if (! $class->hasField($property->name) && ! $class->hasAssociation($property->name)) {
  146.                     continue;
  147.                 }
  148.                 $property->setAccessible(true);
  149.                 $property->setValue($proxy$property->getValue($original));
  150.             }
  151.         };
  152.     }
  153. }