; /** * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE'S PROXY GENERATOR */ class extends \ implements \ { public function __isInitialized(): bool { return isset($this->lazyObjectState) && $this->isLazyObjectInitialized(); } public function __serialize(): array { } } EOPHP; /** The UnitOfWork this factory uses to retrieve persisters */ private readonly UnitOfWork $uow; /** @var self::AUTOGENERATE_* */ private $autoGenerate; /** The IdentifierFlattener used for manipulating identifiers */ private readonly IdentifierFlattener $identifierFlattener; /** @var array */ private array $proxyFactories = []; /** * Initializes a new instance of the ProxyFactory class that is * connected to the given EntityManager. * * @param EntityManagerInterface $em The EntityManager the new factory works for. * @param string $proxyDir The directory to use for the proxy classes. It must exist. * @param string $proxyNs The namespace to use for the proxy classes. * @param bool|self::AUTOGENERATE_* $autoGenerate The strategy for automatically generating proxy classes. */ public function __construct( private readonly EntityManagerInterface $em, private readonly string $proxyDir, private readonly string $proxyNs, bool|int $autoGenerate = self::AUTOGENERATE_NEVER, ) { if (! $proxyDir) { throw ORMInvalidArgumentException::proxyDirectoryRequired(); } if (! $proxyNs) { throw ORMInvalidArgumentException::proxyNamespaceRequired(); } if (is_int($autoGenerate) ? $autoGenerate < 0 || $autoGenerate > 4 : ! is_bool($autoGenerate)) { throw ORMInvalidArgumentException::invalidAutoGenerateMode($autoGenerate); } $this->uow = $em->getUnitOfWork(); $this->autoGenerate = (int) $autoGenerate; $this->identifierFlattener = new IdentifierFlattener($this->uow, $em->getMetadataFactory()); } /** * @param class-string $className * @param array $identifier */ public function getProxy(string $className, array $identifier): InternalProxy { $proxyFactory = $this->proxyFactories[$className] ?? $this->getProxyFactory($className); return $proxyFactory($identifier); } /** * Generates proxy classes for all given classes. * * @param ClassMetadata[] $classes The classes (ClassMetadata instances) for which to generate proxies. * @param string|null $proxyDir The target directory of the proxy classes. If not specified, the * directory configured on the Configuration of the EntityManager used * by this factory is used. * * @return int Number of generated proxies. */ public function generateProxyClasses(array $classes, string|null $proxyDir = null): int { $generated = 0; foreach ($classes as $class) { if ($this->skipClass($class)) { continue; } $proxyFileName = $this->getProxyFileName($class->getName(), $proxyDir ?: $this->proxyDir); $proxyClassName = self::generateProxyClassName($class->getName(), $this->proxyNs); $this->generateProxyClass($class, $proxyFileName, $proxyClassName); ++$generated; } return $generated; } protected function skipClass(ClassMetadata $metadata): bool { return $metadata->isMappedSuperclass || $metadata->isEmbeddedClass || $metadata->getReflectionClass()->isAbstract(); } /** * Creates a closure capable of initializing a proxy * * @return Closure(InternalProxy, array):void * * @throws EntityNotFoundException */ private function createLazyInitializer(ClassMetadata $classMetadata, EntityPersister $entityPersister, IdentifierFlattener $identifierFlattener): Closure { return static function (InternalProxy $proxy, array $identifier) use ($entityPersister, $classMetadata, $identifierFlattener): void { $original = $entityPersister->loadById($identifier); if ($original === null) { throw EntityNotFoundException::fromClassNameAndIdentifier( $classMetadata->getName(), $identifierFlattener->flattenIdentifier($classMetadata, $identifier), ); } if ($proxy === $original) { return; } $class = $entityPersister->getClassMetadata(); foreach ($class->getReflectionProperties() as $property) { if (! $property || isset($identifier[$property->getName()]) || ! $class->hasField($property->getName()) && ! $class->hasAssociation($property->getName())) { continue; } $property->setValue($proxy, $property->getValue($original)); } }; } private function getProxyFileName(string $className, string $baseDirectory): string { $baseDirectory = $baseDirectory ?: $this->proxyDir; return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . InternalProxy::MARKER . str_replace('\\', '', $className) . '.php'; } private function getProxyFactory(string $className): Closure { $skippedProperties = []; $class = $this->em->getClassMetadata($className); $identifiers = array_flip($class->getIdentifierFieldNames()); $filter = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE; $reflector = $class->getReflectionClass(); while ($reflector) { foreach ($reflector->getProperties($filter) as $property) { $name = $property->name; if ($property->isStatic() || (($class->hasField($name) || $class->hasAssociation($name)) && ! isset($identifiers[$name]))) { continue; } $prefix = $property->isPrivate() ? "\0" . $property->class . "\0" : ($property->isProtected() ? "\0*\0" : ''); $skippedProperties[$prefix . $name] = true; } $filter = ReflectionProperty::IS_PRIVATE; $reflector = $reflector->getParentClass(); } $className = $class->getName(); // aliases and case sensitivity $entityPersister = $this->uow->getEntityPersister($className); $initializer = $this->createLazyInitializer($class, $entityPersister, $this->identifierFlattener); $proxyClassName = $this->loadProxyClass($class); $identifierFields = array_intersect_key($class->getReflectionProperties(), $identifiers); $proxyFactory = Closure::bind(static function (array $identifier) use ($initializer, $skippedProperties, $identifierFields, $className): InternalProxy { $proxy = self::createLazyGhost(static function (InternalProxy $object) use ($initializer, $identifier): void { $initializer($object, $identifier); }, $skippedProperties); foreach ($identifierFields as $idField => $reflector) { if (! isset($identifier[$idField])) { throw ORMInvalidArgumentException::missingPrimaryKeyValue($className, $idField); } assert($reflector !== null); $reflector->setValue($proxy, $identifier[$idField]); } return $proxy; }, null, $proxyClassName); return $this->proxyFactories[$className] = $proxyFactory; } private function loadProxyClass(ClassMetadata $class): string { $proxyClassName = self::generateProxyClassName($class->getName(), $this->proxyNs); if (class_exists($proxyClassName, false)) { return $proxyClassName; } if ($this->autoGenerate === self::AUTOGENERATE_EVAL) { $this->generateProxyClass($class, null, $proxyClassName); return $proxyClassName; } $fileName = $this->getProxyFileName($class->getName(), $this->proxyDir); switch ($this->autoGenerate) { case self::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED: if (file_exists($fileName) && filemtime($fileName) >= filemtime($class->getReflectionClass()->getFileName())) { break; } // no break case self::AUTOGENERATE_FILE_NOT_EXISTS: if (file_exists($fileName)) { break; } // no break case self::AUTOGENERATE_ALWAYS: $this->generateProxyClass($class, $fileName, $proxyClassName); break; } require $fileName; return $proxyClassName; } private function generateProxyClass(ClassMetadata $class, string|null $fileName, string $proxyClassName): void { $i = strrpos($proxyClassName, '\\'); $placeholders = [ '' => $class->getName(), '' => substr($proxyClassName, 0, $i), '' => substr($proxyClassName, 1 + $i), '' => InternalProxy::class, ]; preg_match_all('(<([a-zA-Z]+)>)', self::PROXY_CLASS_TEMPLATE, $placeholderMatches); foreach (array_combine($placeholderMatches[0], $placeholderMatches[1]) as $placeholder => $name) { $placeholders[$placeholder] ?? $placeholders[$placeholder] = $this->{'generate' . ucfirst($name)}($class); } $proxyCode = strtr(self::PROXY_CLASS_TEMPLATE, $placeholders); if (! $fileName) { if (! class_exists($proxyClassName)) { eval(substr($proxyCode, 5)); } return; } $parentDirectory = dirname($fileName); if (! is_dir($parentDirectory) && ! @mkdir($parentDirectory, 0775, true)) { throw ORMInvalidArgumentException::proxyDirectoryNotWritable($this->proxyDir); } if (! is_writable($parentDirectory)) { throw ORMInvalidArgumentException::proxyDirectoryNotWritable($this->proxyDir); } $tmpFileName = $fileName . '.' . bin2hex(random_bytes(12)); file_put_contents($tmpFileName, $proxyCode); @chmod($tmpFileName, 0664); rename($tmpFileName, $fileName); } private function generateUseLazyGhostTrait(ClassMetadata $class): string { $code = ProxyHelper::generateLazyGhost($class->getReflectionClass()); $code = substr($code, 7 + (int) strpos($code, "\n{")); $code = substr($code, 0, (int) strpos($code, "\n}")); $code = str_replace('LazyGhostTrait;', str_replace("\n ", "\n", 'LazyGhostTrait { initializeLazyObject as private; setLazyObjectAsInitialized as public __setInitialized; isLazyObjectInitialized as private; createLazyGhost as private; resetLazyObject as private; } public function __load(): void { $this->initializeLazyObject(); } '), $code); return $code; } private function generateSerializeImpl(ClassMetadata $class): string { $reflector = $class->getReflectionClass(); $properties = $reflector->hasMethod('__serialize') ? 'parent::__serialize()' : '(array) $this'; $code = '$properties = ' . $properties . '; unset($properties["\0" . self::class . "\0lazyObjectState"]); '; if ($reflector->hasMethod('__serialize') || ! $reflector->hasMethod('__sleep')) { return $code . 'return $properties;'; } return $code . '$data = []; foreach (parent::__sleep() as $name) { $value = $properties[$k = $name] ?? $properties[$k = "\0*\0$name"] ?? $properties[$k = "\0' . $reflector->name . '\0$name"] ?? $k = null; if (null === $k) { trigger_error(sprintf(\'serialize(): "%s" returned as member variable from __sleep() but does not exist\', $name), \E_USER_NOTICE); } else { $data[$k] = $value; } } return $data;'; } private static function generateProxyClassName(string $className, string $proxyNamespace): string { return rtrim($proxyNamespace, '\\') . '\\' . Proxy::MARKER . '\\' . ltrim($className, '\\'); } }