From d297a49f5ccbb3644958695cc16e86f4a0297246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Sat, 23 Nov 2024 15:35:40 +0100 Subject: [PATCH 1/9] Generate proxy classes using symfony/var-exporter --- composer.json | 3 +- lib/Doctrine/ODM/MongoDB/Configuration.php | 87 ++-- lib/Doctrine/ODM/MongoDB/DocumentManager.php | 4 +- .../ODM/MongoDB/Hydrator/HydratorFactory.php | 7 + .../ODM/MongoDB/Mapping/ClassMetadata.php | 9 +- lib/Doctrine/ODM/MongoDB/Proxy/Autoloader.php | 90 ++++ .../Proxy/Factory/LazyGhostProxyFactory.php | 390 ++++++++++++++++++ .../MongoDB/Proxy/Factory/ProxyFactory.php | 5 +- .../Proxy/Factory/StaticProxyFactory.php | 2 + .../ODM/MongoDB/Proxy/InternalProxy.php | 35 ++ .../LazyGhostProxyClassNameResolver.php | 31 ++ lib/Doctrine/ODM/MongoDB/UnitOfWork.php | 10 +- phpstan-baseline.neon | 5 + .../Tests/Functional/IdentifiersTest.php | 5 +- .../Tests/Functional/ReferencePrimerTest.php | 48 +-- .../Tests/Functional/ReferencesTest.php | 5 +- .../Tests/Functional/SimpleReferencesTest.php | 3 +- .../Tests/Functional/Ticket/GH520Test.php | 3 +- .../Tests/Functional/Ticket/GH593Test.php | 5 +- .../Tests/Functional/Ticket/GH602Test.php | 5 +- .../Tests/Functional/Ticket/GH852Test.php | 7 +- .../Tests/Functional/Ticket/GH936Test.php | 3 +- .../ODM/MongoDB/Tests/Functional/ViewTest.php | 5 +- .../ODM/MongoDB/Tests/HydratorTest.php | 9 +- .../Tests/Mapping/ClassMetadataTest.php | 9 +- .../ODM/MongoDB/Tests/UnitOfWorkTest.php | 3 +- 26 files changed, 689 insertions(+), 99 deletions(-) create mode 100644 lib/Doctrine/ODM/MongoDB/Proxy/Autoloader.php create mode 100644 lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php create mode 100644 lib/Doctrine/ODM/MongoDB/Proxy/InternalProxy.php create mode 100644 lib/Doctrine/ODM/MongoDB/Proxy/Resolver/LazyGhostProxyClassNameResolver.php diff --git a/composer.json b/composer.json index 1bdaf6c08d..c1d762040f 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,8 @@ "psr/cache": "^1.0 || ^2.0 || ^3.0", "symfony/console": "^5.4 || ^6.0 || ^7.0", "symfony/deprecation-contracts": "^2.2 || ^3.0", - "symfony/var-dumper": "^5.4 || ^6.0 || ^7.0" + "symfony/var-dumper": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^5.4 || ^6.0 || ^7.0" }, "require-dev": { "ext-bcmath": "*", diff --git a/lib/Doctrine/ODM/MongoDB/Configuration.php b/lib/Doctrine/ODM/MongoDB/Configuration.php index adc486991f..8de0ab1add 100644 --- a/lib/Doctrine/ODM/MongoDB/Configuration.php +++ b/lib/Doctrine/ODM/MongoDB/Configuration.php @@ -82,12 +82,23 @@ class Configuration */ public const AUTOGENERATE_EVAL = 3; + /** + * Autogenerate the proxy class when the proxy file does not exist or + * when the proxied file changed. + * + * This strategy causes a file_exists() call whenever any proxy is used the + * first time in a request. When the proxied file is changed, the proxy will + * be updated. + */ + public const AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED = 4; + /** * Array of attributes for this configuration instance. * * @phpstan-var array{ * autoGenerateHydratorClasses?: self::AUTOGENERATE_*, * autoGeneratePersistentCollectionClasses?: self::AUTOGENERATE_*, + * autoGenerateProxyClasses?: self::AUTOGENERATE_*, * classMetadataFactoryName?: class-string, * defaultCommitOptions?: CommitOptions, * defaultDocumentRepositoryClassName?: class-string>, @@ -106,6 +117,8 @@ class Configuration * persistentCollectionGenerator?: PersistentCollectionGenerator, * persistentCollectionDir?: string, * persistentCollectionNamespace?: string, + * proxyDir?: string, + * proxyNamespace?: string, * repositoryFactory?: RepositoryFactory * } */ @@ -113,18 +126,8 @@ class Configuration private ?CacheItemPoolInterface $metadataCache = null; - private ProxyManagerConfiguration $proxyManagerConfiguration; - - private int $autoGenerateProxyClasses = self::AUTOGENERATE_EVAL; - private bool $useTransactionalFlush = false; - public function __construct() - { - $this->proxyManagerConfiguration = new ProxyManagerConfiguration(); - $this->setAutoGenerateProxyClasses(self::AUTOGENERATE_FILE_NOT_EXISTS); - } - /** * Adds a namespace under a certain alias. */ @@ -248,14 +251,7 @@ public function setMetadataCache(CacheItemPoolInterface $cache): void */ public function setProxyDir(string $dir): void { - $this->getProxyManagerConfiguration()->setProxiesTargetDir($dir); - - // Recreate proxy generator to ensure its path was updated - if ($this->autoGenerateProxyClasses !== self::AUTOGENERATE_FILE_NOT_EXISTS) { - return; - } - - $this->setAutoGenerateProxyClasses($this->autoGenerateProxyClasses); + $this->attributes['proxyDir'] = $dir; } /** @@ -263,53 +259,41 @@ public function setProxyDir(string $dir): void */ public function getProxyDir(): ?string { - return $this->getProxyManagerConfiguration()->getProxiesTargetDir(); + return $this->attributes['proxyDir'] ?? null; } /** * Gets an int flag that indicates whether proxy classes should always be regenerated * during each script execution. + * + * @return self::AUTOGENERATE_* */ public function getAutoGenerateProxyClasses(): int { - return $this->autoGenerateProxyClasses; + return $this->attributes['autoGenerateProxyClasses'] ?? self::AUTOGENERATE_FILE_NOT_EXISTS; } /** * Sets an int flag that indicates whether proxy classes should always be regenerated * during each script execution. * + * @param self::AUTOGENERATE_* $mode + * * @throws InvalidArgumentException If an invalid mode was given. */ public function setAutoGenerateProxyClasses(int $mode): void { - $this->autoGenerateProxyClasses = $mode; - $proxyManagerConfig = $this->getProxyManagerConfiguration(); - - switch ($mode) { - case self::AUTOGENERATE_FILE_NOT_EXISTS: - $proxyManagerConfig->setGeneratorStrategy(new FileWriterGeneratorStrategy( - new FileLocator($proxyManagerConfig->getProxiesTargetDir()), - )); - - break; - case self::AUTOGENERATE_EVAL: - $proxyManagerConfig->setGeneratorStrategy(new EvaluatingGeneratorStrategy()); - - break; - default: - throw new InvalidArgumentException('Invalid proxy generation strategy given - only AUTOGENERATE_FILE_NOT_EXISTS and AUTOGENERATE_EVAL are supported.'); - } + $this->attributes['autoGenerateProxyClasses'] = $mode; } public function getProxyNamespace(): ?string { - return $this->getProxyManagerConfiguration()->getProxiesNamespace(); + return $this->attributes['proxyNamespace'] ?? null; } public function setProxyNamespace(string $ns): void { - $this->getProxyManagerConfiguration()->setProxiesNamespace($ns); + $this->attributes['proxyNamespace'] = $ns; } public function setHydratorDir(string $dir): void @@ -589,14 +573,35 @@ public function getPersistentCollectionGenerator(): PersistentCollectionGenerato return $this->attributes['persistentCollectionGenerator']; } + /** @deprecated */ public function buildGhostObjectFactory(): LazyLoadingGhostFactory { - return new LazyLoadingGhostFactory(clone $this->getProxyManagerConfiguration()); + return new LazyLoadingGhostFactory($this->getProxyManagerConfiguration()); } + /** @deprecated */ public function getProxyManagerConfiguration(): ProxyManagerConfiguration { - return $this->proxyManagerConfiguration; + $proxyManagerConfiguration = new ProxyManagerConfiguration(); + $proxyManagerConfiguration->setProxiesTargetDir($this->getProxyDir()); + $proxyManagerConfiguration->setProxiesNamespace($this->getProxyNamespace()); + + switch ($this->getAutoGenerateProxyClasses()) { + case self::AUTOGENERATE_FILE_NOT_EXISTS: + $proxyManagerConfiguration->setGeneratorStrategy(new FileWriterGeneratorStrategy( + new FileLocator($proxyManagerConfiguration->getProxiesTargetDir()), + )); + + break; + case self::AUTOGENERATE_EVAL: + $proxyManagerConfiguration->setGeneratorStrategy(new EvaluatingGeneratorStrategy()); + + break; + default: + throw new InvalidArgumentException('Invalid proxy generation strategy given - only AUTOGENERATE_FILE_NOT_EXISTS and AUTOGENERATE_EVAL are supported.'); + } + + return $proxyManagerConfiguration; } public function setUseTransactionalFlush(bool $useTransactionalFlush): void diff --git a/lib/Doctrine/ODM/MongoDB/DocumentManager.php b/lib/Doctrine/ODM/MongoDB/DocumentManager.php index b8c40aea3b..14a010f6cf 100644 --- a/lib/Doctrine/ODM/MongoDB/DocumentManager.php +++ b/lib/Doctrine/ODM/MongoDB/DocumentManager.php @@ -9,8 +9,8 @@ use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactoryInterface; use Doctrine\ODM\MongoDB\Mapping\MappingException; +use Doctrine\ODM\MongoDB\Proxy\Factory\LazyGhostProxyFactory; use Doctrine\ODM\MongoDB\Proxy\Factory\ProxyFactory; -use Doctrine\ODM\MongoDB\Proxy\Factory\StaticProxyFactory; use Doctrine\ODM\MongoDB\Proxy\Resolver\CachingClassNameResolver; use Doctrine\ODM\MongoDB\Proxy\Resolver\ClassNameResolver; use Doctrine\ODM\MongoDB\Proxy\Resolver\ProxyManagerClassNameResolver; @@ -181,7 +181,7 @@ protected function __construct(?Client $client = null, ?Configuration $config = $this->unitOfWork = new UnitOfWork($this, $this->eventManager, $this->hydratorFactory); $this->schemaManager = new SchemaManager($this, $this->metadataFactory); - $this->proxyFactory = new StaticProxyFactory($this); + $this->proxyFactory = new LazyGhostProxyFactory($this, $config->getProxyDir(), $config->getProxyNamespace(), $config->getAutoGenerateProxyClasses()); $this->repositoryFactory = $this->config->getRepositoryFactory(); } diff --git a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php index 2b91220d5a..1e8135b6aa 100644 --- a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php @@ -11,6 +11,7 @@ use Doctrine\ODM\MongoDB\Event\PreLoadEventArgs; use Doctrine\ODM\MongoDB\Events; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Types\Type; use Doctrine\ODM\MongoDB\UnitOfWork; use ProxyManager\Proxy\GhostObjectInterface; @@ -448,6 +449,12 @@ public function hydrate(object $document, array $data, array $hints = []): array } } + if ($document instanceof InternalProxy) { + // Skip initialization to not load any object data + $document->__setInitialized(true); + } + + // Support for legacy proxy-manager-lts if ($document instanceof GhostObjectInterface && $document->getProxyInitializer() !== null) { // Inject an empty initialiser to not load any object data $document->setProxyInitializer(static function ( diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php index 85dddf1486..ff3b977728 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php @@ -14,6 +14,7 @@ use Doctrine\ODM\MongoDB\Id\IdGenerator; use Doctrine\ODM\MongoDB\LockException; use Doctrine\ODM\MongoDB\Mapping\Annotations\TimeSeries; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Types\Incrementable; use Doctrine\ODM\MongoDB\Types\Type; use Doctrine\ODM\MongoDB\Types\Versionable; @@ -1893,9 +1894,11 @@ public function getIdentifierObject(object $document) */ public function setFieldValue(object $document, string $field, $value): void { - if ($document instanceof GhostObjectInterface && ! $document->isProxyInitialized()) { + if ($document instanceof InternalProxy && ! $document->__isInitialized()) { //property changes to an uninitialized proxy will not be tracked or persisted, //so the proxy needs to be loaded first. + $document->__load(); + } elseif ($document instanceof GhostObjectInterface && ! $document->isProxyInitialized()) { $document->initializeProxy(); } @@ -1909,7 +1912,9 @@ public function setFieldValue(object $document, string $field, $value): void */ public function getFieldValue(object $document, string $field) { - if ($document instanceof GhostObjectInterface && $field !== $this->identifier && ! $document->isProxyInitialized()) { + if ($document instanceof InternalProxy && $field !== $this->identifier && ! $document->__isInitialized()) { + $document->__load(); + } elseif ($document instanceof GhostObjectInterface && $field !== $this->identifier && ! $document->isProxyInitialized()) { $document->initializeProxy(); } diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Autoloader.php b/lib/Doctrine/ODM/MongoDB/Proxy/Autoloader.php new file mode 100644 index 0000000000..d346a0c194 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Autoloader.php @@ -0,0 +1,90 @@ +; + +/** + * 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 Configuration::AUTOGENERATE_* */ + private $autoGenerate; + + /** @var array */ + private array $proxyFactories = []; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given EntityManager. + * + * @param DocumentManager $dm 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|Configuration::AUTOGENERATE_* $autoGenerate The strategy for automatically generating proxy classes. + */ + public function __construct( + private readonly DocumentManager $dm, + private readonly string $proxyDir, + private readonly string $proxyNs, + bool|int $autoGenerate = Configuration::AUTOGENERATE_NEVER, + ) { + if (! $proxyDir) { + throw new InvalidArgumentException('You must configure a proxy directory. See docs for details'); + } + + if (! $proxyNs) { + throw new InvalidArgumentException('You must configure a proxy namespace'); + } + + if (is_int($autoGenerate) && ($autoGenerate < 0 || $autoGenerate > 4)) { + throw new InvalidArgumentException(sprintf('Invalid auto generate mode "%s" given.', is_scalar($autoGenerate) ? (string) $autoGenerate : get_debug_type($autoGenerate))); + } + + $this->uow = $dm->getUnitOfWork(); + $this->autoGenerate = (int) $autoGenerate; + } + + /** @param array $identifier */ + public function getProxy(ClassMetadata $metadata, $identifier): InternalProxy + { + $className = $metadata->getName(); + + $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->isEmbeddedDocument + || $metadata->getReflectionClass()->isAbstract(); + } + + /** + * Creates a closure capable of initializing a proxy + * + * @param ClassMetadata $classMetadata + * + * @return Closure(InternalProxy&T, array):void + * + * @throws DocumentNotFoundException + * + * @template T of object + */ + private function createLazyInitializer(ClassMetadata $classMetadata, DocumentPersister $persister): Closure + { + return static function (InternalProxy $proxy, mixed $identifier) use ($persister, $classMetadata): void { + $original = $persister->load([$classMetadata->identifier => $identifier]); + + if ($original === null) { + throw DocumentNotFoundException::documentNotFound( + $classMetadata->getName(), + $identifier, + ); + } + + if ($proxy === $original) { + return; + } + + $class = $persister->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'; + } + + /** @param class-string $className */ + private function getProxyFactory(string $className): Closure + { + $skippedProperties = []; + $class = $this->dm->getClassMetadata($className); + $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)))) { + 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->getDocumentPersister($className); + $initializer = $this->createLazyInitializer($class, $entityPersister); + $proxyClassName = $this->loadProxyClass($class); + + $proxyFactory = Closure::bind(static function (mixed $identifier) use ($initializer, $skippedProperties, $class): InternalProxy { + /** @see LazyGhostTrait::createLazyGhost() */ + $proxy = static::createLazyGhost(static function (InternalProxy $object) use ($initializer, $identifier): void { + $initializer($object, $identifier); + }, $skippedProperties); + + $class->setIdentifierValue($proxy, $identifier); + + 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 === Configuration::AUTOGENERATE_EVAL) { + $this->generateProxyClass($class, null, $proxyClassName); + + return $proxyClassName; + } + + $fileName = $this->getProxyFileName($class->getName(), $this->proxyDir); + + switch ($this->autoGenerate) { + case Configuration::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED: + if (file_exists($fileName) && filemtime($fileName) >= filemtime($class->getReflectionClass()->getFileName())) { + break; + } + // no break + case Configuration::AUTOGENERATE_FILE_NOT_EXISTS: + if (file_exists($fileName)) { + break; + } + // no break + case Configuration::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) || ! is_writable($parentDirectory)) { + throw new InvalidArgumentException(sprintf('Your proxy directory "%s" must be writable', $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, '\\'); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/ProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/ProxyFactory.php index 1540e40e9e..4a8d93aeeb 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/ProxyFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/ProxyFactory.php @@ -5,7 +5,6 @@ namespace Doctrine\ODM\MongoDB\Proxy\Factory; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; -use ProxyManager\Proxy\GhostObjectInterface; interface ProxyFactory { @@ -19,9 +18,9 @@ public function generateProxyClasses(array $classes): int; * @param mixed $identifier * @phpstan-param ClassMetadata $metadata * - * @return T&GhostObjectInterface + * @return T * * @template T of object */ - public function getProxy(ClassMetadata $metadata, $identifier): GhostObjectInterface; + public function getProxy(ClassMetadata $metadata, $identifier): object; } diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php index e13bbfb14f..b8b5954d82 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php @@ -22,6 +22,8 @@ /** * This factory is used to create proxy objects for documents at runtime. + * + * @deprecated */ final class StaticProxyFactory implements ProxyFactory { diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/InternalProxy.php b/lib/Doctrine/ODM/MongoDB/Proxy/InternalProxy.php new file mode 100644 index 0000000000..93019d8791 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Proxy/InternalProxy.php @@ -0,0 +1,35 @@ + + */ + interface InternalProxy extends Proxy, GhostObjectInterface + { + public function __setInitialized(bool $initialized): void; + } + +} else { + /** + * @internal + * + * @template T of object + * @template-extends Proxy + */ + interface InternalProxy extends Proxy + { + public function __setInitialized(bool $initialized): void; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/LazyGhostProxyClassNameResolver.php b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/LazyGhostProxyClassNameResolver.php new file mode 100644 index 0000000000..e9bb658cf7 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/LazyGhostProxyClassNameResolver.php @@ -0,0 +1,31 @@ +resolveClassName($class); + } + + public function resolveClassName(string $className): string + { + $pos = strrpos($className, '\\' . Proxy::MARKER . '\\'); + + if ($pos === false) { + return $className; + } + + return substr($className, $pos + Proxy::MARKER_LENGTH + 2); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/UnitOfWork.php b/lib/Doctrine/ODM/MongoDB/UnitOfWork.php index f7a728693e..fece78f3a9 100644 --- a/lib/Doctrine/ODM/MongoDB/UnitOfWork.php +++ b/lib/Doctrine/ODM/MongoDB/UnitOfWork.php @@ -14,6 +14,7 @@ use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionInterface; use Doctrine\ODM\MongoDB\Persisters\CollectionPersister; use Doctrine\ODM\MongoDB\Persisters\PersistenceBuilder; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Query\Query; use Doctrine\ODM\MongoDB\Types\DateType; use Doctrine\ODM\MongoDB\Types\Type; @@ -970,6 +971,10 @@ private function computeAssociationChanges(object $parentDocument, array $assoc, $class = $this->dm->getClassMetadata($parentDocument::class); $topOrExistingDocument = ( ! $isNewParentDocument || ! $class->isEmbeddedDocument); + if ($value instanceof InternalProxy && ! $value->__isInitialized()) { + return; + } + if ($value instanceof GhostObjectInterface && ! $value->isProxyInitialized()) { return; } @@ -3057,7 +3062,9 @@ public function getScheduledCollectionUpdates(): array */ public function initializeObject(object $obj): void { - if ($obj instanceof GhostObjectInterface && $obj->isProxyInitialized() === false) { + if ($obj instanceof InternalProxy && ! $obj->__isInitialized()) { + $obj->__load(); + } elseif ($obj instanceof GhostObjectInterface && $obj->isProxyInitialized() === false) { $obj->initializeProxy(); } elseif ($obj instanceof PersistentCollectionInterface) { $obj->initialize(); @@ -3072,6 +3079,7 @@ public function initializeObject(object $obj): void public function isUninitializedObject(object $obj): bool { return match (true) { + $obj instanceof InternalProxy => $obj->__isInitialized() === false, $obj instanceof GhostObjectInterface => $obj->isProxyInitialized() === false, $obj instanceof PersistentCollectionInterface => $obj->isInitialized() === false, default => false diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e2c6ab2d83..6a889b8a5c 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -545,6 +545,11 @@ parameters: count: 1 path: lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php + - + message: "#^Call to an undefined static method Doctrine\\\\ODM\\\\MongoDB\\\\Proxy\\\\Factory\\\\LazyGhostProxyFactory\\:\\:createLazyGhost\\(\\)\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php + - message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Proxy\\\\Factory\\\\StaticProxyFactory\\:\\:createInitializer\\(\\) should return Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface\\&TDocument\\=, string\\=, array\\\\=, Closure\\|null\\=, array\\\\=\\)\\: bool but returns Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface, string, array, mixed, array\\)\\: true\\.$#" count: 1 diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php index 66cec01810..b329f25a49 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php @@ -4,6 +4,7 @@ namespace Doctrine\ODM\MongoDB\Tests\Functional; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\Event; use Documents\User; @@ -30,7 +31,7 @@ public function testGetIdentifierValue(): void $userTest = $test->getUser(); self::assertEquals($user->getId(), $userTest->getId()); - self::assertInstanceOf(LazyLoadingInterface::class, $userTest); + self::assertInstanceOf(InternalProxy::class, $userTest); self::assertTrue($this->uow->isUninitializedObject($userTest)); $this->dm->clear(); @@ -42,7 +43,7 @@ public function testGetIdentifierValue(): void $foundUser = $test->getUser(); self::assertEquals($user->getId(), $class->getIdentifierValue($user)); self::assertEquals($user->getId(), $class->getFieldValue($foundUser, 'id')); - self::assertInstanceOf(LazyLoadingInterface::class, $foundUser); + self::assertInstanceOf(InternalProxy::class, $foundUser); self::assertTrue($this->uow->isUninitializedObject($foundUser)); self::assertEquals('jwage', $foundUser->getUsername()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php index 00468ea54d..f1f59072a4 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php @@ -7,6 +7,7 @@ use DateTime; use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Query\Query; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Doctrine\ODM\MongoDB\Tests\ClassMetadataTestUtil; @@ -33,7 +34,6 @@ use InvalidArgumentException; use MongoDB\Driver\ReadPreference; use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; -use ProxyManager\Proxy\GhostObjectInterface; use function assert; use function func_get_args; @@ -95,7 +95,7 @@ public function testPrimeReferencesWithDBRefObjects(): void ->field('groups')->prime(true); foreach ($qb->getQuery() as $user) { - self::assertInstanceOf(GhostObjectInterface::class, $user->getAccount()); + self::assertInstanceOf(InternalProxy::class, $user->getAccount()); self::assertFalse($this->uow->isUninitializedObject($user->getAccount())); self::assertCount(2, $user->getGroups()); @@ -104,7 +104,7 @@ public function testPrimeReferencesWithDBRefObjects(): void * initialized, they will not be hydrated as proxy objects. */ foreach ($user->getGroups() as $group) { - self::assertNotInstanceOf(GhostObjectInterface::class, $group); + self::assertNotInstanceOf(InternalProxy::class, $group); self::assertInstanceOf(Group::class, $group); } } @@ -133,13 +133,13 @@ public function testPrimeReferencesWithSimpleReferences(): void ->field('users')->prime(true); foreach ($qb->getQuery() as $simpleUser) { - self::assertInstanceOf(GhostObjectInterface::class, $simpleUser->getUser()); + self::assertInstanceOf(InternalProxy::class, $simpleUser->getUser()); self::assertFalse($this->uow->isUninitializedObject($simpleUser->getUser())); self::assertCount(2, $simpleUser->getUsers()); foreach ($simpleUser->getUsers() as $user) { - self::assertNotInstanceOf(GhostObjectInterface::class, $user); + self::assertNotInstanceOf(InternalProxy::class, $user); self::assertInstanceOf(User::class, $user); } } @@ -188,20 +188,20 @@ public function testPrimeReferencesNestedInNamedEmbeddedReference(): void ->field('embeddedDocs.referencedDocs')->prime(true); foreach ($qb->getQuery() as $root) { - self::assertNotInstanceOf(GhostObjectInterface::class, $root->embeddedDoc); + self::assertNotInstanceOf(InternalProxy::class, $root->embeddedDoc); self::assertInstanceOf(EmbeddedWhichReferences::class, $root->embeddedDoc); self::assertCount(2, $root->embeddedDocs); foreach ($root->embeddedDocs as $embeddedDoc) { - self::assertNotInstanceOf(GhostObjectInterface::class, $embeddedDoc); + self::assertNotInstanceOf(InternalProxy::class, $embeddedDoc); self::assertInstanceOf(EmbeddedWhichReferences::class, $embeddedDoc); - self::assertInstanceOf(GhostObjectInterface::class, $embeddedDoc->referencedDoc); + self::assertInstanceOf(InternalProxy::class, $embeddedDoc->referencedDoc); self::assertFalse($this->uow->isUninitializedObject($embeddedDoc->referencedDoc)); self::assertCount(2, $embeddedDoc->referencedDocs); foreach ($embeddedDoc->referencedDocs as $referencedDoc) { - self::assertNotInstanceOf(GhostObjectInterface::class, $referencedDoc); + self::assertNotInstanceOf(InternalProxy::class, $referencedDoc); self::assertInstanceOf(Reference::class, $referencedDoc); } } @@ -252,37 +252,37 @@ public function testPrimeReferencesWithDifferentStoreAsReferences(): void assert($referenceUser instanceof ReferenceUser); $user = $referenceUser->getUser(); self::assertInstanceOf(User::class, $user); - self::assertInstanceOf(GhostObjectInterface::class, $user); + self::assertInstanceOf(InternalProxy::class, $user); self::assertFalse($this->uow->isUninitializedObject($user)); self::assertCount(1, $referenceUser->getUsers()); foreach ($referenceUser->getUsers() as $user) { - self::assertNotInstanceOf(GhostObjectInterface::class, $user); + self::assertNotInstanceOf(InternalProxy::class, $user); self::assertInstanceOf(User::class, $user); } $parentUser = $referenceUser->getParentUser(); - self::assertInstanceOf(GhostObjectInterface::class, $parentUser); + self::assertInstanceOf(InternalProxy::class, $parentUser); self::assertInstanceOf(User::class, $parentUser); self::assertFalse($this->uow->isUninitializedObject($parentUser)); self::assertCount(1, $referenceUser->getParentUsers()); foreach ($referenceUser->getParentUsers() as $user) { - self::assertNotInstanceOf(GhostObjectInterface::class, $user); + self::assertNotInstanceOf(InternalProxy::class, $user); self::assertInstanceOf(User::class, $user); } $otherUser = $referenceUser->getOtherUser(); self::assertInstanceOf(User::class, $otherUser); - self::assertInstanceOf(GhostObjectInterface::class, $otherUser); + self::assertInstanceOf(InternalProxy::class, $otherUser); self::assertFalse($this->uow->isUninitializedObject($otherUser)); self::assertCount(1, $referenceUser->getOtherUsers()); foreach ($referenceUser->getOtherUsers() as $user) { - self::assertNotInstanceOf(GhostObjectInterface::class, $user); + self::assertNotInstanceOf(InternalProxy::class, $user); self::assertInstanceOf(User::class, $user); } } @@ -309,10 +309,10 @@ public function testPrimeReferencesWithDiscriminatedReferenceMany(): void foreach ($qb->getQuery() as $user) { $favorites = $user->getFavorites()->toArray(); - self::assertNotInstanceOf(GhostObjectInterface::class, $favorites[0]); + self::assertNotInstanceOf(InternalProxy::class, $favorites[0]); self::assertInstanceOf(Group::class, $favorites[0]); - self::assertNotInstanceOf(GhostObjectInterface::class, $favorites[1]); + self::assertNotInstanceOf(InternalProxy::class, $favorites[1]); self::assertInstanceOf(Project::class, $favorites[1]); } } @@ -331,7 +331,7 @@ public function testPrimeReferencesWithDiscriminatedReferenceOne(): void ->field('server')->prime(true); foreach ($qb->getQuery() as $agent) { - self::assertInstanceOf(GhostObjectInterface::class, $agent->server); + self::assertInstanceOf(InternalProxy::class, $agent->server); self::assertFalse($this->uow->isUninitializedObject($agent->server)); } } @@ -360,7 +360,7 @@ public function testPrimeReferencesIgnoresInitializedProxyObjects(): void self::assertCount(2, $user->getGroups()); foreach ($user->getGroups() as $group) { - self::assertNotInstanceOf(GhostObjectInterface::class, $group); + self::assertNotInstanceOf(InternalProxy::class, $group); self::assertInstanceOf(Group::class, $group); } } @@ -440,7 +440,7 @@ public function testPrimeReferencesInFindAndModifyResult(): void self::assertCount(1, $user->getGroups()); foreach ($user->getGroups() as $group) { - self::assertNotInstanceOf(GhostObjectInterface::class, $group); + self::assertNotInstanceOf(InternalProxy::class, $group); self::assertInstanceOf(Group::class, $group); } } @@ -472,7 +472,7 @@ public function testPrimeEmbeddedReferenceOneLevelDeep(): void $phonenumber = $phonenumbers->current(); - self::assertNotInstanceOf(GhostObjectInterface::class, $phonenumber); + self::assertNotInstanceOf(InternalProxy::class, $phonenumber); self::assertInstanceOf(Phonenumber::class, $phonenumber); } @@ -523,7 +523,7 @@ public function testPrimeEmbeddedReferenceTwoLevelsDeep(): void $currency = $money->getCurrency(); - self::assertInstanceOf(GhostObjectInterface::class, $currency); + self::assertInstanceOf(InternalProxy::class, $currency); self::assertInstanceOf(Currency::class, $currency); self::assertFalse($this->uow->isUninitializedObject($currency)); } @@ -551,7 +551,7 @@ public function testPrimeReferencesInReferenceMany(): void self::assertInstanceOf(BlogPost::class, $post); $comment = $post->comments->first(); - self::assertInstanceOf(GhostObjectInterface::class, $comment->author); + self::assertInstanceOf(InternalProxy::class, $comment->author); self::assertFalse($this->uow->isUninitializedObject($comment->author)); } @@ -578,7 +578,7 @@ public function testPrimeReferencesInReferenceManyWithRepositoryMethodEager(): v self::assertInstanceOf(BlogPost::class, $post); $comment = $post->repoCommentsWithPrimer->first(); - self::assertInstanceOf(GhostObjectInterface::class, $comment->author); + self::assertInstanceOf(InternalProxy::class, $comment->author); self::assertFalse($this->uow->isUninitializedObject($comment->author)); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php index 10de475900..2ba1ffcba6 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php @@ -12,6 +12,7 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\PersistentCollection; use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionInterface; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\Account; use Documents\Address; @@ -82,7 +83,7 @@ public function testLazyLoadReference(): void assert($profile instanceof Profile); self::assertInstanceOf(Profile::class, $profile); - self::assertInstanceOf(GhostObjectInterface::class, $profile); + self::assertInstanceOf(InternalProxy::class, $profile); $profile->getFirstName(); @@ -104,7 +105,7 @@ public function testLazyLoadedWithNotifyPropertyChanged(): void $user = $this->dm->find($user::class, $user->getId()); $profile = $user->getProfileNotify(); - self::assertInstanceOf(GhostObjectInterface::class, $profile); + self::assertInstanceOf(InternalProxy::class, $profile); self::assertTrue($this->uow->isUninitializedObject($profile)); $user->getProfileNotify()->setLastName('Malarz'); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php index 4d69bff53f..4c403e04fc 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php @@ -4,6 +4,7 @@ namespace Doctrine\ODM\MongoDB\Tests\Functional; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\SimpleReferenceUser; use Documents\User; @@ -87,7 +88,7 @@ public function testProxy(): void assert($user instanceof User && $user instanceof GhostObjectInterface); self::assertNotNull($user); self::assertInstanceOf(User::class, $user); - self::assertInstanceOf(GhostObjectInterface::class, $user); + self::assertInstanceOf(InternalProxy::class, $user); self::assertTrue($this->uow->isUninitializedObject($user)); self::assertEquals('jwage', $user->getUsername()); self::assertFalse($this->uow->isUninitializedObject($user)); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php index 6ff6348414..f2c17a0f07 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php @@ -7,6 +7,7 @@ use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use ProxyManager\Proxy\GhostObjectInterface; @@ -29,7 +30,7 @@ public function testPrimeWithGetSingleResult(): void $document = $query->getSingleResult(); self::assertInstanceOf(GH520Document::class, $document); - self::assertInstanceOf(GhostObjectInterface::class, $document->ref); + self::assertInstanceOf(InternalProxy::class, $document->ref); self::assertFalse($this->uow->isUninitializedObject($document->ref)); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php index 0248d9fd87..33b64deb20 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php @@ -8,6 +8,7 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ODM\MongoDB\DocumentNotFoundException; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use ProxyManager\Proxy\GhostObjectInterface; @@ -58,11 +59,11 @@ public function testReferenceManyOwningSidePreparesFilterCriteria(): void */ self::assertCount(2, $user1following); - self::assertInstanceOf(GhostObjectInterface::class, $user1following[0]); + self::assertInstanceOf(InternalProxy::class, $user1following[0]); self::assertFalse($this->uow->isUninitializedObject($user1following[0])); self::assertEquals($user2->getId(), $user1following[0]->getId()); - self::assertInstanceOf(GhostObjectInterface::class, $user1following[1]); + self::assertInstanceOf(InternalProxy::class, $user1following[1]); self::assertTrue($this->uow->isUninitializedObject($user1following[1])); self::assertEquals($user3->getId(), $user1following[1]->getId()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php index ce38f44edb..9e709a6148 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php @@ -8,6 +8,7 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ODM\MongoDB\DocumentNotFoundException; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use ProxyManager\Proxy\GhostObjectInterface; @@ -49,11 +50,11 @@ public function testReferenceManyOwningSidePreparesFilterCriteriaForDifferentCla */ self::assertCount(2, $user1likes); - self::assertInstanceOf(GhostObjectInterface::class, $user1likes[0]); + self::assertInstanceOf(InternalProxy::class, $user1likes[0]); self::assertFalse($this->uow->isUninitializedObject($user1likes[0])); self::assertEquals($thing1->getId(), $user1likes[0]->getId()); - self::assertInstanceOf(GhostObjectInterface::class, $user1likes[1]); + self::assertInstanceOf(InternalProxy::class, $user1likes[1]); self::assertTrue($this->uow->isUninitializedObject($user1likes[1])); self::assertEquals($thing2->getId(), $user1likes[1]->getId()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php index 0b4e912e82..a670dccdcb 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php @@ -10,6 +10,7 @@ use Doctrine\Common\Collections\Criteria; use Doctrine\ODM\MongoDB\Iterator\Iterator; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use MongoDB\BSON\Binary; use PHPUnit\Framework\Attributes\DataProvider; @@ -49,7 +50,7 @@ public function testA(Closure $idGenerator): void self::assertEquals($idGenerator('parent'), $parent->id); self::assertEquals('parent', $parent->name); - self::assertInstanceOf(GhostObjectInterface::class, $parent->refOne); + self::assertInstanceOf(InternalProxy::class, $parent->refOne); self::assertInstanceOf(GH852Document::class, $parent->refOne); self::assertTrue($this->uow->isUninitializedObject($parent->refOne)); self::assertEquals($idGenerator('childA'), $parent->refOne->id); @@ -61,13 +62,13 @@ public function testA(Closure $idGenerator): void /* These proxies will be initialized when we first access the collection * by DocumentPersister::loadReferenceManyCollectionOwningSide(). */ - self::assertInstanceOf(GhostObjectInterface::class, $parent->refMany[0]); + self::assertInstanceOf(InternalProxy::class, $parent->refMany[0]); self::assertInstanceOf(GH852Document::class, $parent->refMany[0]); self::assertFalse($this->uow->isUninitializedObject($parent->refMany[0])); self::assertEquals($idGenerator('childB'), $parent->refMany[0]->id); self::assertEquals('childB', $parent->refMany[0]->name); - self::assertInstanceOf(GhostObjectInterface::class, $parent->refMany[1]); + self::assertInstanceOf(InternalProxy::class, $parent->refMany[1]); self::assertInstanceOf(GH852Document::class, $parent->refMany[1]); self::assertFalse($this->uow->isUninitializedObject($parent->refMany[1])); self::assertEquals($idGenerator('childC'), $parent->refMany[1]->id); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php index b2eb71704c..3cbff3d5ac 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php @@ -7,6 +7,7 @@ use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs; use Doctrine\ODM\MongoDB\Events; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use ProxyManager\Proxy\GhostObjectInterface; @@ -27,7 +28,7 @@ public function testRemoveCascadesThroughProxyDocuments(): void $foo = $this->dm->find(GH936Document::class, $foo->id); - self::assertInstanceOf(GhostObjectInterface::class, $foo->ref); + self::assertInstanceOf(InternalProxy::class, $foo->ref); $this->dm->remove($foo); $this->dm->flush(); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php index 608fc45a40..a34c00302e 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php @@ -4,6 +4,7 @@ namespace Doctrine\ODM\MongoDB\Tests\Functional; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Repository\ViewRepository; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Doctrine\ODM\MongoDB\UnitOfWork; @@ -112,7 +113,7 @@ public function testViewReferences(): void $viewReference = $this->dm->find(ViewReference::class, $alcaeus->getId()); self::assertInstanceOf(ViewReference::class, $viewReference); - self::assertInstanceOf(GhostObjectInterface::class, $viewReference->getReferenceOneView()); + self::assertInstanceOf(InternalProxy::class, $viewReference->getReferenceOneView()); self::assertSame($malarzm->getId(), $viewReference->getReferenceOneView()->getId()); // No proxies for inverse referenceOne @@ -120,7 +121,7 @@ public function testViewReferences(): void self::assertSame($alcaeus->getId(), $viewReference->getReferenceOneViewMappedBy()->getId()); self::assertCount(1, $viewReference->getReferenceManyView()); - self::assertInstanceOf(GhostObjectInterface::class, $viewReference->getReferenceManyView()[0]); + self::assertInstanceOf(InternalProxy::class, $viewReference->getReferenceManyView()[0]); self::assertSame($malarzm->getId(), $viewReference->getReferenceManyView()[0]->getId()); // No proxies for inverse referenceMany diff --git a/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php b/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php index 30191a6da6..5433ae9df9 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php @@ -10,6 +10,7 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\PersistentCollection; use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionInterface; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Query\Query; use ProxyManager\Proxy\GhostObjectInterface; @@ -41,10 +42,10 @@ public function testHydrator(): void self::assertEquals('jon', $user->name); self::assertInstanceOf(DateTime::class, $user->birthdate); self::assertInstanceOf(HydrationClosureReferenceOne::class, $user->referenceOne); - self::assertInstanceOf(GhostObjectInterface::class, $user->referenceOne); + self::assertInstanceOf(InternalProxy::class, $user->referenceOne); self::assertInstanceOf(PersistentCollection::class, $user->referenceMany); - self::assertInstanceOf(GhostObjectInterface::class, $user->referenceMany[0]); - self::assertInstanceOf(GhostObjectInterface::class, $user->referenceMany[1]); + self::assertInstanceOf(InternalProxy::class, $user->referenceMany[0]); + self::assertInstanceOf(InternalProxy::class, $user->referenceMany[1]); self::assertInstanceOf(HydrationClosureEmbedOne::class, $user->embedOne); self::assertInstanceOf(PersistentCollection::class, $user->embedMany); self::assertEquals('jon', $user->embedOne->name); @@ -54,7 +55,7 @@ public function testHydrator(): void public function testHydrateProxyWithMissingAssociations(): void { $user = $this->dm->getReference(HydrationClosureUser::class, 1); - self::assertInstanceOf(GhostObjectInterface::class, $user); + self::assertInstanceOf(InternalProxy::class, $user); $this->dm->getHydratorFactory()->hydrate($user, [ '_id' => 1, diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php index c8f74769d4..d77a2c7150 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php @@ -10,6 +10,7 @@ use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\MappingException; use Doctrine\ODM\MongoDB\Mapping\TimeSeries\Granularity; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Repository\DocumentRepository; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Doctrine\ODM\MongoDB\Tests\ClassMetadataTestUtil; @@ -496,7 +497,7 @@ public function testGetFieldValueInitializesProxy(): void $metadata = $this->dm->getClassMetadata(Album::class); self::assertEquals($document->getName(), $metadata->getFieldValue($proxy, 'name')); - self::assertInstanceOf(GhostObjectInterface::class, $proxy); + self::assertInstanceOf(InternalProxy::class, $proxy); self::assertFalse($this->uow->isUninitializedObject($proxy)); } @@ -511,7 +512,7 @@ public function testGetFieldValueOfIdentifierDoesNotInitializeProxy(): void $metadata = $this->dm->getClassMetadata(Album::class); self::assertEquals($document->getId(), $metadata->getFieldValue($proxy, 'id')); - self::assertInstanceOf(GhostObjectInterface::class, $proxy); + self::assertInstanceOf(InternalProxy::class, $proxy); self::assertTrue($this->uow->isUninitializedObject($proxy)); } @@ -533,7 +534,7 @@ public function testSetFieldValueWithProxy(): void $this->dm->clear(); $proxy = $this->dm->getReference(Album::class, $document->getId()); - self::assertInstanceOf(GhostObjectInterface::class, $proxy); + self::assertInstanceOf(InternalProxy::class, $proxy); $metadata = $this->dm->getClassMetadata(Album::class); $metadata->setFieldValue($proxy, 'name', 'nevermind'); @@ -542,7 +543,7 @@ public function testSetFieldValueWithProxy(): void $this->dm->clear(); $proxy = $this->dm->getReference(Album::class, $document->getId()); - self::assertInstanceOf(GhostObjectInterface::class, $proxy); + self::assertInstanceOf(InternalProxy::class, $proxy); self::assertInstanceOf(Album::class, $proxy); self::assertEquals('nevermind', $proxy->getName()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php index e79c0ef931..fbb6fcfb2c 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php @@ -11,6 +11,7 @@ use Doctrine\ODM\MongoDB\APM\CommandLogger; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\MongoDBException; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\Mocks\ExceptionThrowingListenerMock; use Doctrine\ODM\MongoDB\Tests\Mocks\PreUpdateListenerMock; use Doctrine\ODM\MongoDB\UnitOfWork; @@ -489,7 +490,7 @@ public function testRecomputeChangesetForUninitializedProxyDoesNotCreateChangese $user = $this->dm->find(ForumUser::class, $id); self::assertInstanceOf(ForumUser::class, $user); - self::assertInstanceOf(GhostObjectInterface::class, $user->getAvatar()); + self::assertInstanceOf(InternalProxy::class, $user->getAvatar()); $classMetadata = $this->dm->getClassMetadata(ForumAvatar::class); From 6b404b5bc6cb081186f13f311bf8af96fb74d3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Sat, 23 Nov 2024 18:23:51 +0100 Subject: [PATCH 2/9] Leverage UOW::initializeObject in tests --- lib/Doctrine/ODM/MongoDB/DocumentManager.php | 4 +-- .../ODM/MongoDB/Proxy/InternalProxy.php | 35 +++++-------------- lib/Doctrine/ODM/MongoDB/UnitOfWork.php | 11 ++++-- .../ODM/MongoDB/Tests/BaseTestCase.php | 10 ++++++ .../Tests/Functional/IdentifiersTest.php | 1 - .../Tests/Functional/ReferencesTest.php | 18 +++++----- .../Tests/Functional/SimpleReferencesTest.php | 5 +-- .../Tests/Functional/Ticket/GH520Test.php | 1 - .../Tests/Functional/Ticket/GH593Test.php | 3 +- .../Tests/Functional/Ticket/GH602Test.php | 3 +- .../Tests/Functional/Ticket/GH852Test.php | 1 - .../Tests/Functional/Ticket/GH936Test.php | 1 - .../ODM/MongoDB/Tests/Functional/ViewTest.php | 1 - .../ODM/MongoDB/Tests/HydratorTest.php | 1 - .../Tests/Mapping/ClassMetadataTest.php | 1 - .../Proxy/Factory/StaticProxyFactoryTest.php | 7 ++-- .../ODM/MongoDB/Tests/UnitOfWorkTest.php | 1 - 17 files changed, 44 insertions(+), 60 deletions(-) diff --git a/lib/Doctrine/ODM/MongoDB/DocumentManager.php b/lib/Doctrine/ODM/MongoDB/DocumentManager.php index 14a010f6cf..61e421c65e 100644 --- a/lib/Doctrine/ODM/MongoDB/DocumentManager.php +++ b/lib/Doctrine/ODM/MongoDB/DocumentManager.php @@ -13,7 +13,7 @@ use Doctrine\ODM\MongoDB\Proxy\Factory\ProxyFactory; use Doctrine\ODM\MongoDB\Proxy\Resolver\CachingClassNameResolver; use Doctrine\ODM\MongoDB\Proxy\Resolver\ClassNameResolver; -use Doctrine\ODM\MongoDB\Proxy\Resolver\ProxyManagerClassNameResolver; +use Doctrine\ODM\MongoDB\Proxy\Resolver\LazyGhostProxyClassNameResolver; use Doctrine\ODM\MongoDB\Query\FilterCollection; use Doctrine\ODM\MongoDB\Repository\DocumentRepository; use Doctrine\ODM\MongoDB\Repository\GridFSRepository; @@ -156,7 +156,7 @@ protected function __construct(?Client $client = null, ?Configuration $config = ], ); - $this->classNameResolver = new CachingClassNameResolver(new ProxyManagerClassNameResolver($this->config)); + $this->classNameResolver = new CachingClassNameResolver(new LazyGhostProxyClassNameResolver()); $metadataFactoryClassName = $this->config->getClassMetadataFactoryName(); $this->metadataFactory = new $metadataFactoryClassName(); diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/InternalProxy.php b/lib/Doctrine/ODM/MongoDB/Proxy/InternalProxy.php index 93019d8791..90af928ca8 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/InternalProxy.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/InternalProxy.php @@ -5,31 +5,14 @@ namespace Doctrine\ODM\MongoDB\Proxy; use Doctrine\Persistence\Proxy; -use ProxyManager\Proxy\GhostObjectInterface; -use function class_exists; - -if (class_exists(GhostObjectInterface::class)) { - /** - * @internal - * - * @template T of object - * @template-extends Proxy - */ - interface InternalProxy extends Proxy, GhostObjectInterface - { - public function __setInitialized(bool $initialized): void; - } - -} else { - /** - * @internal - * - * @template T of object - * @template-extends Proxy - */ - interface InternalProxy extends Proxy - { - public function __setInitialized(bool $initialized): void; - } +/** + * @internal + * + * @template T of object + * @template-extends Proxy + */ +interface InternalProxy extends Proxy +{ + public function __setInitialized(bool $initialized): void; } diff --git a/lib/Doctrine/ODM/MongoDB/UnitOfWork.php b/lib/Doctrine/ODM/MongoDB/UnitOfWork.php index fece78f3a9..c6ca92c954 100644 --- a/lib/Doctrine/ODM/MongoDB/UnitOfWork.php +++ b/lib/Doctrine/ODM/MongoDB/UnitOfWork.php @@ -2782,7 +2782,14 @@ public function getOrCreateDocument(string $className, array $data, array &$hint $document = $this->identityMap[$class->name][$serializedId]; $oid = spl_object_hash($document); if ($this->isUninitializedObject($document)) { - $document->setProxyInitializer(null); + if ($document instanceof InternalProxy) { + $document->__setInitialized(true); + } elseif ($document instanceof GhostObjectInterface) { + $document->setProxyInitializer(null); + } else { + throw new \RuntimeException(sprintf('Expected uninitialized proxy or ghost object from class "%s"', $document::name)); + } + $overrideLocalValues = true; if ($document instanceof NotifyPropertyChanged) { $document->addPropertyChangedListener($this); @@ -3062,7 +3069,7 @@ public function getScheduledCollectionUpdates(): array */ public function initializeObject(object $obj): void { - if ($obj instanceof InternalProxy && ! $obj->__isInitialized()) { + if ($obj instanceof InternalProxy && $obj->__isInitialized() === false) { $obj->__load(); } elseif ($obj instanceof GhostObjectInterface && $obj->isProxyInitialized() === false) { $obj->initializeProxy(); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php b/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php index b24c54c3fe..204fb9e7ba 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php @@ -7,6 +7,7 @@ use Doctrine\ODM\MongoDB\Configuration; use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\Query\Filter\Filter; use Doctrine\ODM\MongoDB\UnitOfWork; use Doctrine\Persistence\Mapping\Driver\MappingDriver; @@ -15,6 +16,7 @@ use MongoDB\Driver\Server; use MongoDB\Model\DatabaseInfo; use PHPUnit\Framework\TestCase; +use ProxyManager\Proxy\LazyLoadingInterface; use function array_key_exists; use function array_map; @@ -114,6 +116,14 @@ public static function assertArraySubset(array $subset, array $array, bool $chec } } + public static function assertIsLazyObject(object $document): void + { + self::logicalOr( + self::isInstanceOf(InternalProxy::class), + self::isInstanceOf(LazyLoadingInterface::class), + )->evaluate($document); + } + protected static function createMetadataDriverImpl(): MappingDriver { return AttributeDriver::create(__DIR__ . '/../../../../Documents'); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php index b329f25a49..0b60b44874 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php @@ -8,7 +8,6 @@ use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\Event; use Documents\User; -use ProxyManager\Proxy\LazyLoadingInterface; use function assert; use function get_class; diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php index 2ba1ffcba6..d28ad801e6 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php @@ -23,8 +23,6 @@ use Documents\User; use MongoDB\BSON\Binary; use MongoDB\BSON\ObjectId; -use ProxyManager\Proxy\GhostObjectInterface; -use ProxyManager\Proxy\LazyLoadingInterface; use function assert; @@ -397,13 +395,13 @@ public function testDocumentNotFoundExceptionWithArrayId(): void ); $test = $this->dm->find($test::class, $test->id); - self::assertInstanceOf(LazyLoadingInterface::class, $test->referenceOne); + self::assertIsLazyObject($test->referenceOne); $this->expectException(DocumentNotFoundException::class); $this->expectExceptionMessage( 'The "Doctrine\ODM\MongoDB\Tests\Functional\DocumentWithArrayId" document with identifier ' . '{"identifier":2} could not be found.', ); - $test->referenceOne->initializeProxy(); + $this->uow->initializeObject($test->referenceOne); } public function testDocumentNotFoundExceptionWithObjectId(): void @@ -430,12 +428,12 @@ public function testDocumentNotFoundExceptionWithObjectId(): void $user = $this->dm->find($user::class, $user->getId()); $profile = $user->getProfile(); - self::assertInstanceOf(LazyLoadingInterface::class, $profile); + self::assertIsLazyObject($profile); $this->expectException(DocumentNotFoundException::class); $this->expectExceptionMessage( 'The "Documents\Profile" document with identifier "abcdefabcdefabcdefabcdef" could not be found.', ); - $profile->initializeProxy(); + $this->uow->initializeObject($profile); } public function testDocumentNotFoundExceptionWithMongoBinDataId(): void @@ -461,13 +459,13 @@ public function testDocumentNotFoundExceptionWithMongoBinDataId(): void ); $test = $this->dm->find($test::class, $test->id); - self::assertInstanceOf(LazyLoadingInterface::class, $test->referenceOne); + self::assertIsLazyObject($test->referenceOne); $this->expectException(DocumentNotFoundException::class); $this->expectExceptionMessage( 'The "Doctrine\ODM\MongoDB\Tests\Functional\DocumentWithMongoBinDataId" document with identifier ' . '"testbindata" could not be found.', ); - $test->referenceOne->initializeProxy(); + $this->uow->initializeObject($test->referenceOne); } public function testDocumentNotFoundEvent(): void @@ -503,8 +501,8 @@ public function testDocumentNotFoundEvent(): void $this->dm->getEventManager()->addEventListener(Events::documentNotFound, new DocumentNotFoundListener($closure)); - self::assertInstanceOf(LazyLoadingInterface::class, $profile); - $profile->initializeProxy(); + self::assertIsLazyObject($profile); + $this->uow->initializeObject($profile); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php index 4c403e04fc..98d15e9fc5 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php @@ -4,12 +4,10 @@ namespace Doctrine\ODM\MongoDB\Tests\Functional; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\SimpleReferenceUser; use Documents\User; use MongoDB\BSON\ObjectId; -use ProxyManager\Proxy\GhostObjectInterface; use stdClass; use function assert; @@ -85,10 +83,9 @@ public function testProxy(): void self::assertNotNull($test); $user = $test->getUser(); - assert($user instanceof User && $user instanceof GhostObjectInterface); self::assertNotNull($user); self::assertInstanceOf(User::class, $user); - self::assertInstanceOf(InternalProxy::class, $user); + self::assertIsLazyObject($user); self::assertTrue($this->uow->isUninitializedObject($user)); self::assertEquals('jwage', $user->getUsername()); self::assertFalse($this->uow->isUninitializedObject($user)); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php index f2c17a0f07..c0aa3c18d7 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php @@ -9,7 +9,6 @@ use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use ProxyManager\Proxy\GhostObjectInterface; class GH520Test extends BaseTestCase { diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php index 33b64deb20..0b48112ab1 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php @@ -10,7 +10,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use ProxyManager\Proxy\GhostObjectInterface; use function iterator_to_array; @@ -68,7 +67,7 @@ public function testReferenceManyOwningSidePreparesFilterCriteria(): void self::assertEquals($user3->getId(), $user1following[1]->getId()); $this->expectException(DocumentNotFoundException::class); - $user1following[1]->initializeProxy(); + $this->uow->initializeObject($user1following[1]); } public function testReferenceManyInverseSidePreparesFilterCriteria(): void diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php index 9e709a6148..356a70da90 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php @@ -10,7 +10,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use ProxyManager\Proxy\GhostObjectInterface; use function iterator_to_array; @@ -59,7 +58,7 @@ public function testReferenceManyOwningSidePreparesFilterCriteriaForDifferentCla self::assertEquals($thing2->getId(), $user1likes[1]->getId()); $this->expectException(DocumentNotFoundException::class); - $user1likes[1]->initializeProxy(); + $this->uow->initializeObject($user1likes[1]); } public function testReferenceManyInverseSidePreparesFilterCriteriaForDifferentClass(): void diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php index a670dccdcb..8d2927da73 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php @@ -14,7 +14,6 @@ use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use MongoDB\BSON\Binary; use PHPUnit\Framework\Attributes\DataProvider; -use ProxyManager\Proxy\GhostObjectInterface; class GH852Test extends BaseTestCase { diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php index 3cbff3d5ac..480c8a19ec 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php @@ -9,7 +9,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use ProxyManager\Proxy\GhostObjectInterface; class GH936Test extends BaseTestCase { diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php index a34c00302e..768440338b 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php @@ -11,7 +11,6 @@ use Documents\CmsUser; use Documents\UserName; use Documents\ViewReference; -use ProxyManager\Proxy\GhostObjectInterface; use function assert; diff --git a/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php b/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php index 5433ae9df9..560e6e391c 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php @@ -12,7 +12,6 @@ use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionInterface; use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Query\Query; -use ProxyManager\Proxy\GhostObjectInterface; class HydratorTest extends BaseTestCase { diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php index d77a2c7150..5a4302abd1 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php @@ -40,7 +40,6 @@ use InvalidArgumentException; use MongoDB\BSON\Document; use PHPUnit\Framework\Attributes\DataProvider; -use ProxyManager\Proxy\GhostObjectInterface; use ReflectionClass; use ReflectionException; use stdClass; diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/StaticProxyFactoryTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/StaticProxyFactoryTest.php index 5a1db88dc5..12337dd71f 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/StaticProxyFactoryTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/StaticProxyFactoryTest.php @@ -16,7 +16,6 @@ use MongoDB\Collection; use MongoDB\Database; use PHPUnit\Framework\MockObject\MockObject; -use ProxyManager\Proxy\GhostObjectInterface; class StaticProxyFactoryTest extends BaseTestCase { @@ -45,7 +44,7 @@ public function testProxyInitializeWithException(): void $uow = $this->dm->getUnitOfWork(); $proxy = $this->dm->getReference(Cart::class, '123'); - self::assertInstanceOf(GhostObjectInterface::class, $proxy); + self::assertIsLazyObject($proxy); $closure = static function (DocumentNotFoundEventArgs $eventArgs) { self::fail('DocumentNotFoundListener should not be called'); @@ -53,7 +52,7 @@ public function testProxyInitializeWithException(): void $this->dm->getEventManager()->addEventListener(Events::documentNotFound, new DocumentNotFoundListener($closure)); try { - $proxy->initializeProxy(); + $this->uow->initializeObject($proxy); self::fail('An exception should have been thrown'); } catch (LockException $exception) { self::assertInstanceOf(LockException::class, $exception); @@ -61,7 +60,7 @@ public function testProxyInitializeWithException(): void $uow->computeChangeSets(); - self::assertFalse($proxy->isProxyInitialized(), 'Proxy should not be initialized'); + self::assertTrue($this->uow->isUninitializedObject($proxy), 'Proxy should not be initialized'); } public function tearDown(): void diff --git a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php index fbb6fcfb2c..793476b2b4 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php @@ -29,7 +29,6 @@ use MongoDB\Driver\WriteConcern; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; -use ProxyManager\Proxy\GhostObjectInterface; use ReflectionProperty; use Throwable; From e528f8cbbadd8a6dcbe686806d807adec5028a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Sat, 23 Nov 2024 19:32:53 +0100 Subject: [PATCH 3/9] Trigger lifecycleEventManager from lazy ghost object --- .../Proxy/Factory/LazyGhostProxyFactory.php | 37 ++++++++----------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php index ff706d0021..691b6a7695 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php @@ -12,6 +12,8 @@ use Doctrine\ODM\MongoDB\Persisters\DocumentPersister; use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\UnitOfWork; +use Doctrine\ODM\MongoDB\Utility\LifecycleEventManager; +use Doctrine\Persistence\NotifyPropertyChanged; use Doctrine\Persistence\Proxy; use InvalidArgumentException; use ReflectionProperty; @@ -87,6 +89,8 @@ public function __serialize(): array /** @var array */ private array $proxyFactories = []; + private LifecycleEventManager $lifecycleEventManager; + /** * Initializes a new instance of the ProxyFactory class that is * connected to the given EntityManager. @@ -114,8 +118,9 @@ public function __construct( throw new InvalidArgumentException(sprintf('Invalid auto generate mode "%s" given.', is_scalar($autoGenerate) ? (string) $autoGenerate : get_debug_type($autoGenerate))); } - $this->uow = $dm->getUnitOfWork(); - $this->autoGenerate = (int) $autoGenerate; + $this->uow = $dm->getUnitOfWork(); + $this->autoGenerate = (int) $autoGenerate; + $this->lifecycleEventManager = new LifecycleEventManager($dm, $this->uow, $dm->getEventManager()); } /** @param array $identifier */ @@ -178,28 +183,18 @@ protected function skipClass(ClassMetadata $metadata): bool */ private function createLazyInitializer(ClassMetadata $classMetadata, DocumentPersister $persister): Closure { - return static function (InternalProxy $proxy, mixed $identifier) use ($persister, $classMetadata): void { - $original = $persister->load([$classMetadata->identifier => $identifier]); - - if ($original === null) { - throw DocumentNotFoundException::documentNotFound( - $classMetadata->getName(), - $identifier, - ); - } - - if ($proxy === $original) { - return; - } + $factory = $this; - $class = $persister->getClassMetadata(); + return static function (InternalProxy $proxy, mixed $identifier) use ($persister, $classMetadata, $factory): void { + $original = $persister->load([$classMetadata->identifier => $identifier], $proxy); - foreach ($class->getReflectionProperties() as $property) { - if (! $property || isset($identifier[$property->getName()]) || ! $class->hasField($property->getName()) && ! $class->hasAssociation($property->getName())) { - continue; - } + if (! $original && ! $factory->lifecycleEventManager->documentNotFound($proxy, $identifier)) { + throw DocumentNotFoundException::documentNotFound($classMetadata->getName(), $identifier); + } - $property->setValue($proxy, $property->getValue($original)); + // phpcs:ignore SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed + if ($proxy instanceof NotifyPropertyChanged) { + $proxy->addPropertyChangedListener($factory->uow); } }; } From 4e343864cf681a7af2cc42d8646e3207ffcced56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Sat, 23 Nov 2024 22:12:59 +0100 Subject: [PATCH 4/9] Run the full test suite with proxy manager --- .github/workflows/continuous-integration.yml | 21 +++++++++++++ composer.json | 4 +-- lib/Doctrine/ODM/MongoDB/Configuration.php | 30 +++++++++++++++++++ lib/Doctrine/ODM/MongoDB/DocumentManager.php | 10 +++++-- .../Proxy/Factory/LazyGhostProxyFactory.php | 2 +- .../Proxy/Factory/StaticProxyFactory.php | 2 +- phpunit.xml.dist | 1 + .../ODM/MongoDB/Tests/BaseTestCase.php | 14 ++++++--- .../Tests/Functional/IdentifiersTest.php | 5 ++-- .../Tests/Functional/ReferencePrimerTest.php | 20 ++++++------- .../Tests/Functional/ReferencesTest.php | 5 ++-- .../Tests/Functional/Ticket/GH520Test.php | 3 +- .../Tests/Functional/Ticket/GH593Test.php | 5 ++-- .../Tests/Functional/Ticket/GH602Test.php | 5 ++-- .../Tests/Functional/Ticket/GH852Test.php | 7 ++--- .../Tests/Functional/Ticket/GH936Test.php | 3 +- .../ODM/MongoDB/Tests/Functional/ViewTest.php | 5 ++-- .../ODM/MongoDB/Tests/HydratorTest.php | 9 +++--- .../Tests/Mapping/ClassMetadataTest.php | 9 +++--- ...xyFactoryTest.php => ProxyFactoryTest.php} | 2 +- .../ODM/MongoDB/Tests/UnitOfWorkTest.php | 3 +- 21 files changed, 109 insertions(+), 56 deletions(-) rename tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/{StaticProxyFactoryTest.php => ProxyFactoryTest.php} (98%) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 244d7faf7c..62f56d0e97 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -34,6 +34,8 @@ jobs: - "highest" symfony-version: - "stable" + proxy: + - "lazy-ghost" include: # Test against lowest dependencies - dependencies: "lowest" @@ -42,6 +44,7 @@ jobs: driver-version: "1.17.0" topology: "server" symfony-version: "stable" + proxy: "lazy-ghost" # Test with highest dependencies - topology: "server" php-version: "8.2" @@ -49,6 +52,7 @@ jobs: driver-version: "stable" dependencies: "highest" symfony-version: "7" + proxy: "lazy-ghost" # Test with a 5.0 replica set - topology: "replica_set" php-version: "8.2" @@ -56,6 +60,14 @@ jobs: driver-version: "stable" dependencies: "highest" symfony-version: "stable" + proxy: "lazy-ghost" + # Test with ProxyManager + - php-version: "8.2" + mongodb-version: "5.0" + driver-version: "stable" + dependencies: "highest" + symfony-version: "stable" + proxy: "proxy-manager" # Test with a 5.0 sharded cluster # Currently disabled due to a bug where MongoDB reports "sharding status unknown" # - topology: "sharded_cluster" @@ -64,6 +76,7 @@ jobs: # driver-version: "stable" # dependencies: "highest" # symfony-version: "stable" +# proxy: "lazy-ghost" steps: - name: "Checkout" @@ -111,6 +124,13 @@ jobs: composer require --no-update symfony/var-dumper:^7@dev composer require --no-update --dev symfony/cache:^7@dev + - name: "Remove proxy-manager-lts" + if: "${{ matrix.proxy != 'proxy-manager' }}" + run: | + # proxy-manager-lts is not installed by default and must not be used + # unless explicitly requested + composer remove --no-update --dev friendsofphp/proxy-manager-lts + - name: "Install dependencies with Composer" uses: "ramsey/composer-install@v3" with: @@ -132,3 +152,4 @@ jobs: run: "vendor/bin/phpunit" env: DOCTRINE_MONGODB_SERVER: ${{ steps.setup-mongodb.outputs.cluster-uri }} + USE_LAZY_GHOST_OBJECTS: ${{ matrix.proxy == 'lazy-ghost' && '1' || '0' }}" diff --git a/composer.json b/composer.json index c1d762040f..3ed8c5c0d1 100644 --- a/composer.json +++ b/composer.json @@ -28,20 +28,20 @@ "doctrine/event-manager": "^1.0 || ^2.0", "doctrine/instantiator": "^1.1 || ^2", "doctrine/persistence": "^3.2", - "friendsofphp/proxy-manager-lts": "^1.0", "jean85/pretty-package-versions": "^1.3.0 || ^2.0.1", "mongodb/mongodb": "^1.17.0", "psr/cache": "^1.0 || ^2.0 || ^3.0", "symfony/console": "^5.4 || ^6.0 || ^7.0", "symfony/deprecation-contracts": "^2.2 || ^3.0", "symfony/var-dumper": "^5.4 || ^6.0 || ^7.0", - "symfony/var-exporter": "^5.4 || ^6.0 || ^7.0" + "symfony/var-exporter": "^6.2 || ^7.0" }, "require-dev": { "ext-bcmath": "*", "doctrine/annotations": "^1.12 || ^2.0", "doctrine/coding-standard": "^12.0", "doctrine/orm": "^3.2", + "friendsofphp/proxy-manager-lts": "^1.0", "jmikola/geojson": "^1.0", "phpbench/phpbench": "^1.0.0", "phpstan/phpstan": "~1.10.67", diff --git a/lib/Doctrine/ODM/MongoDB/Configuration.php b/lib/Doctrine/ODM/MongoDB/Configuration.php index 8de0ab1add..768d32b9b4 100644 --- a/lib/Doctrine/ODM/MongoDB/Configuration.php +++ b/lib/Doctrine/ODM/MongoDB/Configuration.php @@ -24,6 +24,7 @@ use Doctrine\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Persistence\ObjectRepository; use InvalidArgumentException; +use LogicException; use MongoDB\Driver\WriteConcern; use ProxyManager\Configuration as ProxyManagerConfiguration; use ProxyManager\Factory\LazyLoadingGhostFactory; @@ -33,6 +34,7 @@ use ReflectionClass; use function array_key_exists; +use function class_exists; use function interface_exists; use function trigger_deprecation; use function trim; @@ -128,6 +130,8 @@ class Configuration private bool $useTransactionalFlush = false; + private bool $useLazyGhostObject = true; + /** * Adds a namespace under a certain alias. */ @@ -613,6 +617,32 @@ public function isTransactionalFlushEnabled(): bool { return $this->useTransactionalFlush; } + + /** + * Generate proxy classes using Symfony VarExporter's LazyGhostTrait if true. + * Otherwise, use ProxyManager's LazyLoadingGhostFactory (deprecated) + */ + public function setUseLazyGhostObject(bool $flag): void + { + if ($flag === false) { + if (! class_exists(ProxyManagerConfiguration::class)) { + throw new LogicException('Package "friendsofphp/proxy-manager-lts" is required to disable LazyGhostObject.'); + } + + trigger_deprecation( + 'doctrine/mongodb-odm', + '2.6', + 'Using "friendsofphp/proxy-manager-lts" is deprecated. Use "symfony/var-exporter" LazyGhostObjects instead.', + ); + } + + $this->useLazyGhostObject = $flag; + } + + public function isLazyGhostObjectEnabled(): bool + { + return $this->useLazyGhostObject ?? true; + } } interface_exists(MappingDriver::class); diff --git a/lib/Doctrine/ODM/MongoDB/DocumentManager.php b/lib/Doctrine/ODM/MongoDB/DocumentManager.php index 61e421c65e..69e81954ff 100644 --- a/lib/Doctrine/ODM/MongoDB/DocumentManager.php +++ b/lib/Doctrine/ODM/MongoDB/DocumentManager.php @@ -11,9 +11,11 @@ use Doctrine\ODM\MongoDB\Mapping\MappingException; use Doctrine\ODM\MongoDB\Proxy\Factory\LazyGhostProxyFactory; use Doctrine\ODM\MongoDB\Proxy\Factory\ProxyFactory; +use Doctrine\ODM\MongoDB\Proxy\Factory\StaticProxyFactory; use Doctrine\ODM\MongoDB\Proxy\Resolver\CachingClassNameResolver; use Doctrine\ODM\MongoDB\Proxy\Resolver\ClassNameResolver; use Doctrine\ODM\MongoDB\Proxy\Resolver\LazyGhostProxyClassNameResolver; +use Doctrine\ODM\MongoDB\Proxy\Resolver\ProxyManagerClassNameResolver; use Doctrine\ODM\MongoDB\Query\FilterCollection; use Doctrine\ODM\MongoDB\Repository\DocumentRepository; use Doctrine\ODM\MongoDB\Repository\GridFSRepository; @@ -156,7 +158,9 @@ protected function __construct(?Client $client = null, ?Configuration $config = ], ); - $this->classNameResolver = new CachingClassNameResolver(new LazyGhostProxyClassNameResolver()); + $this->classNameResolver = $config->isLazyGhostObjectEnabled() + ? new CachingClassNameResolver(new LazyGhostProxyClassNameResolver()) + : new CachingClassNameResolver(new ProxyManagerClassNameResolver($this->config)); $metadataFactoryClassName = $this->config->getClassMetadataFactoryName(); $this->metadataFactory = new $metadataFactoryClassName(); @@ -181,7 +185,9 @@ protected function __construct(?Client $client = null, ?Configuration $config = $this->unitOfWork = new UnitOfWork($this, $this->eventManager, $this->hydratorFactory); $this->schemaManager = new SchemaManager($this, $this->metadataFactory); - $this->proxyFactory = new LazyGhostProxyFactory($this, $config->getProxyDir(), $config->getProxyNamespace(), $config->getAutoGenerateProxyClasses()); + $this->proxyFactory = $config->isLazyGhostObjectEnabled() + ? new LazyGhostProxyFactory($this, $config->getProxyDir(), $config->getProxyNamespace(), $config->getAutoGenerateProxyClasses()) + : new StaticProxyFactory($this); $this->repositoryFactory = $this->config->getRepositoryFactory(); } diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php index 691b6a7695..d22534c608 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php @@ -84,7 +84,7 @@ public function __serialize(): array private readonly UnitOfWork $uow; /** @var Configuration::AUTOGENERATE_* */ - private $autoGenerate; + private int $autoGenerate; /** @var array */ private array $proxyFactories = []; diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php index b8b5954d82..d3d6d2dc11 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php @@ -23,7 +23,7 @@ /** * This factory is used to create proxy objects for documents at runtime. * - * @deprecated + * @deprecated since 2.10, use LazyGhostProxyFactory instead */ final class StaticProxyFactory implements ProxyFactory { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c16a73401a..3860390bdc 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -29,5 +29,6 @@ + diff --git a/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php b/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php index 204fb9e7ba..622ff8603c 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php @@ -4,6 +4,7 @@ namespace Doctrine\ODM\MongoDB\Tests; +use Composer\InstalledVersions; use Doctrine\ODM\MongoDB\Configuration; use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver; @@ -89,6 +90,7 @@ protected static function getConfiguration(): Configuration $config->setPersistentCollectionNamespace('PersistentCollections'); $config->setDefaultDB(DOCTRINE_MONGODB_DATABASE); $config->setMetadataDriverImpl(static::createMetadataDriverImpl()); + $config->setUseLazyGhostObject((bool) $_ENV['USE_LAZY_GHOST_OBJECTS']); $config->addFilter('testFilter', Filter::class); $config->addFilter('testFilter2', Filter::class); @@ -118,10 +120,14 @@ public static function assertArraySubset(array $subset, array $array, bool $chec public static function assertIsLazyObject(object $document): void { - self::logicalOr( - self::isInstanceOf(InternalProxy::class), - self::isInstanceOf(LazyLoadingInterface::class), - )->evaluate($document); + if (InstalledVersions::isInstalled('friendsofphp/proxy-manager')) { + self::logicalOr( + self::isInstanceOf(InternalProxy::class), + self::isInstanceOf(LazyLoadingInterface::class), + )->evaluate($document); + } else { + self::assertInstanceOf(InternalProxy::class, $document); + } } protected static function createMetadataDriverImpl(): MappingDriver diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php index 0b60b44874..24da4f568a 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php @@ -4,7 +4,6 @@ namespace Doctrine\ODM\MongoDB\Tests\Functional; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\Event; use Documents\User; @@ -30,7 +29,7 @@ public function testGetIdentifierValue(): void $userTest = $test->getUser(); self::assertEquals($user->getId(), $userTest->getId()); - self::assertInstanceOf(InternalProxy::class, $userTest); + self::assertIsLazyObject($userTest); self::assertTrue($this->uow->isUninitializedObject($userTest)); $this->dm->clear(); @@ -42,7 +41,7 @@ public function testGetIdentifierValue(): void $foundUser = $test->getUser(); self::assertEquals($user->getId(), $class->getIdentifierValue($user)); self::assertEquals($user->getId(), $class->getFieldValue($foundUser, 'id')); - self::assertInstanceOf(InternalProxy::class, $foundUser); + self::assertIsLazyObject($foundUser); self::assertTrue($this->uow->isUninitializedObject($foundUser)); self::assertEquals('jwage', $foundUser->getUsername()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php index f1f59072a4..3ffe267e31 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php @@ -95,7 +95,7 @@ public function testPrimeReferencesWithDBRefObjects(): void ->field('groups')->prime(true); foreach ($qb->getQuery() as $user) { - self::assertInstanceOf(InternalProxy::class, $user->getAccount()); + self::assertIsLazyObject($user->getAccount()); self::assertFalse($this->uow->isUninitializedObject($user->getAccount())); self::assertCount(2, $user->getGroups()); @@ -133,7 +133,7 @@ public function testPrimeReferencesWithSimpleReferences(): void ->field('users')->prime(true); foreach ($qb->getQuery() as $simpleUser) { - self::assertInstanceOf(InternalProxy::class, $simpleUser->getUser()); + self::assertIsLazyObject($simpleUser->getUser()); self::assertFalse($this->uow->isUninitializedObject($simpleUser->getUser())); self::assertCount(2, $simpleUser->getUsers()); @@ -196,7 +196,7 @@ public function testPrimeReferencesNestedInNamedEmbeddedReference(): void self::assertNotInstanceOf(InternalProxy::class, $embeddedDoc); self::assertInstanceOf(EmbeddedWhichReferences::class, $embeddedDoc); - self::assertInstanceOf(InternalProxy::class, $embeddedDoc->referencedDoc); + self::assertIsLazyObject($embeddedDoc->referencedDoc); self::assertFalse($this->uow->isUninitializedObject($embeddedDoc->referencedDoc)); self::assertCount(2, $embeddedDoc->referencedDocs); @@ -252,7 +252,7 @@ public function testPrimeReferencesWithDifferentStoreAsReferences(): void assert($referenceUser instanceof ReferenceUser); $user = $referenceUser->getUser(); self::assertInstanceOf(User::class, $user); - self::assertInstanceOf(InternalProxy::class, $user); + self::assertIsLazyObject($user); self::assertFalse($this->uow->isUninitializedObject($user)); self::assertCount(1, $referenceUser->getUsers()); @@ -263,7 +263,7 @@ public function testPrimeReferencesWithDifferentStoreAsReferences(): void } $parentUser = $referenceUser->getParentUser(); - self::assertInstanceOf(InternalProxy::class, $parentUser); + self::assertIsLazyObject($parentUser); self::assertInstanceOf(User::class, $parentUser); self::assertFalse($this->uow->isUninitializedObject($parentUser)); @@ -276,7 +276,7 @@ public function testPrimeReferencesWithDifferentStoreAsReferences(): void $otherUser = $referenceUser->getOtherUser(); self::assertInstanceOf(User::class, $otherUser); - self::assertInstanceOf(InternalProxy::class, $otherUser); + self::assertIsLazyObject($otherUser); self::assertFalse($this->uow->isUninitializedObject($otherUser)); self::assertCount(1, $referenceUser->getOtherUsers()); @@ -331,7 +331,7 @@ public function testPrimeReferencesWithDiscriminatedReferenceOne(): void ->field('server')->prime(true); foreach ($qb->getQuery() as $agent) { - self::assertInstanceOf(InternalProxy::class, $agent->server); + self::assertIsLazyObject($agent->server); self::assertFalse($this->uow->isUninitializedObject($agent->server)); } } @@ -523,7 +523,7 @@ public function testPrimeEmbeddedReferenceTwoLevelsDeep(): void $currency = $money->getCurrency(); - self::assertInstanceOf(InternalProxy::class, $currency); + self::assertIsLazyObject($currency); self::assertInstanceOf(Currency::class, $currency); self::assertFalse($this->uow->isUninitializedObject($currency)); } @@ -551,7 +551,7 @@ public function testPrimeReferencesInReferenceMany(): void self::assertInstanceOf(BlogPost::class, $post); $comment = $post->comments->first(); - self::assertInstanceOf(InternalProxy::class, $comment->author); + self::assertIsLazyObject($comment->author); self::assertFalse($this->uow->isUninitializedObject($comment->author)); } @@ -578,7 +578,7 @@ public function testPrimeReferencesInReferenceManyWithRepositoryMethodEager(): v self::assertInstanceOf(BlogPost::class, $post); $comment = $post->repoCommentsWithPrimer->first(); - self::assertInstanceOf(InternalProxy::class, $comment->author); + self::assertIsLazyObject($comment->author); self::assertFalse($this->uow->isUninitializedObject($comment->author)); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php index d28ad801e6..7d988507b9 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php @@ -12,7 +12,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\PersistentCollection; use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionInterface; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\Account; use Documents\Address; @@ -81,7 +80,7 @@ public function testLazyLoadReference(): void assert($profile instanceof Profile); self::assertInstanceOf(Profile::class, $profile); - self::assertInstanceOf(InternalProxy::class, $profile); + self::assertIsLazyObject($profile); $profile->getFirstName(); @@ -103,7 +102,7 @@ public function testLazyLoadedWithNotifyPropertyChanged(): void $user = $this->dm->find($user::class, $user->getId()); $profile = $user->getProfileNotify(); - self::assertInstanceOf(InternalProxy::class, $profile); + self::assertIsLazyObject($profile); self::assertTrue($this->uow->isUninitializedObject($profile)); $user->getProfileNotify()->setLastName('Malarz'); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php index c0aa3c18d7..fac852a6bd 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php @@ -7,7 +7,6 @@ use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; class GH520Test extends BaseTestCase @@ -29,7 +28,7 @@ public function testPrimeWithGetSingleResult(): void $document = $query->getSingleResult(); self::assertInstanceOf(GH520Document::class, $document); - self::assertInstanceOf(InternalProxy::class, $document->ref); + self::assertIsLazyObject($document->ref); self::assertFalse($this->uow->isUninitializedObject($document->ref)); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php index 0b48112ab1..16c361b6bd 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php @@ -8,7 +8,6 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ODM\MongoDB\DocumentNotFoundException; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use function iterator_to_array; @@ -58,11 +57,11 @@ public function testReferenceManyOwningSidePreparesFilterCriteria(): void */ self::assertCount(2, $user1following); - self::assertInstanceOf(InternalProxy::class, $user1following[0]); + self::assertIsLazyObject($user1following[0]); self::assertFalse($this->uow->isUninitializedObject($user1following[0])); self::assertEquals($user2->getId(), $user1following[0]->getId()); - self::assertInstanceOf(InternalProxy::class, $user1following[1]); + self::assertIsLazyObject($user1following[1]); self::assertTrue($this->uow->isUninitializedObject($user1following[1])); self::assertEquals($user3->getId(), $user1following[1]->getId()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php index 356a70da90..9207ae9805 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php @@ -8,7 +8,6 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ODM\MongoDB\DocumentNotFoundException; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use function iterator_to_array; @@ -49,11 +48,11 @@ public function testReferenceManyOwningSidePreparesFilterCriteriaForDifferentCla */ self::assertCount(2, $user1likes); - self::assertInstanceOf(InternalProxy::class, $user1likes[0]); + self::assertIsLazyObject($user1likes[0]); self::assertFalse($this->uow->isUninitializedObject($user1likes[0])); self::assertEquals($thing1->getId(), $user1likes[0]->getId()); - self::assertInstanceOf(InternalProxy::class, $user1likes[1]); + self::assertIsLazyObject($user1likes[1]); self::assertTrue($this->uow->isUninitializedObject($user1likes[1])); self::assertEquals($thing2->getId(), $user1likes[1]->getId()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php index 8d2927da73..b848f5142d 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php @@ -10,7 +10,6 @@ use Doctrine\Common\Collections\Criteria; use Doctrine\ODM\MongoDB\Iterator\Iterator; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use MongoDB\BSON\Binary; use PHPUnit\Framework\Attributes\DataProvider; @@ -49,7 +48,7 @@ public function testA(Closure $idGenerator): void self::assertEquals($idGenerator('parent'), $parent->id); self::assertEquals('parent', $parent->name); - self::assertInstanceOf(InternalProxy::class, $parent->refOne); + self::assertIsLazyObject($parent->refOne); self::assertInstanceOf(GH852Document::class, $parent->refOne); self::assertTrue($this->uow->isUninitializedObject($parent->refOne)); self::assertEquals($idGenerator('childA'), $parent->refOne->id); @@ -61,13 +60,13 @@ public function testA(Closure $idGenerator): void /* These proxies will be initialized when we first access the collection * by DocumentPersister::loadReferenceManyCollectionOwningSide(). */ - self::assertInstanceOf(InternalProxy::class, $parent->refMany[0]); + self::assertIsLazyObject($parent->refMany[0]); self::assertInstanceOf(GH852Document::class, $parent->refMany[0]); self::assertFalse($this->uow->isUninitializedObject($parent->refMany[0])); self::assertEquals($idGenerator('childB'), $parent->refMany[0]->id); self::assertEquals('childB', $parent->refMany[0]->name); - self::assertInstanceOf(InternalProxy::class, $parent->refMany[1]); + self::assertIsLazyObject($parent->refMany[1]); self::assertInstanceOf(GH852Document::class, $parent->refMany[1]); self::assertFalse($this->uow->isUninitializedObject($parent->refMany[1])); self::assertEquals($idGenerator('childC'), $parent->refMany[1]->id); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php index 480c8a19ec..c465795dc7 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php @@ -7,7 +7,6 @@ use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs; use Doctrine\ODM\MongoDB\Events; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; class GH936Test extends BaseTestCase @@ -27,7 +26,7 @@ public function testRemoveCascadesThroughProxyDocuments(): void $foo = $this->dm->find(GH936Document::class, $foo->id); - self::assertInstanceOf(InternalProxy::class, $foo->ref); + self::assertIsLazyObject($foo->ref); $this->dm->remove($foo); $this->dm->flush(); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php index 768440338b..918e72db79 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php @@ -4,7 +4,6 @@ namespace Doctrine\ODM\MongoDB\Tests\Functional; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Repository\ViewRepository; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Doctrine\ODM\MongoDB\UnitOfWork; @@ -112,7 +111,7 @@ public function testViewReferences(): void $viewReference = $this->dm->find(ViewReference::class, $alcaeus->getId()); self::assertInstanceOf(ViewReference::class, $viewReference); - self::assertInstanceOf(InternalProxy::class, $viewReference->getReferenceOneView()); + self::assertIsLazyObject($viewReference->getReferenceOneView()); self::assertSame($malarzm->getId(), $viewReference->getReferenceOneView()->getId()); // No proxies for inverse referenceOne @@ -120,7 +119,7 @@ public function testViewReferences(): void self::assertSame($alcaeus->getId(), $viewReference->getReferenceOneViewMappedBy()->getId()); self::assertCount(1, $viewReference->getReferenceManyView()); - self::assertInstanceOf(InternalProxy::class, $viewReference->getReferenceManyView()[0]); + self::assertIsLazyObject($viewReference->getReferenceManyView()[0]); self::assertSame($malarzm->getId(), $viewReference->getReferenceManyView()[0]->getId()); // No proxies for inverse referenceMany diff --git a/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php b/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php index 560e6e391c..e360225454 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php @@ -10,7 +10,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\PersistentCollection; use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionInterface; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Query\Query; class HydratorTest extends BaseTestCase @@ -41,10 +40,10 @@ public function testHydrator(): void self::assertEquals('jon', $user->name); self::assertInstanceOf(DateTime::class, $user->birthdate); self::assertInstanceOf(HydrationClosureReferenceOne::class, $user->referenceOne); - self::assertInstanceOf(InternalProxy::class, $user->referenceOne); + self::assertIsLazyObject($user->referenceOne); self::assertInstanceOf(PersistentCollection::class, $user->referenceMany); - self::assertInstanceOf(InternalProxy::class, $user->referenceMany[0]); - self::assertInstanceOf(InternalProxy::class, $user->referenceMany[1]); + self::assertIsLazyObject($user->referenceMany[0]); + self::assertIsLazyObject($user->referenceMany[1]); self::assertInstanceOf(HydrationClosureEmbedOne::class, $user->embedOne); self::assertInstanceOf(PersistentCollection::class, $user->embedMany); self::assertEquals('jon', $user->embedOne->name); @@ -54,7 +53,7 @@ public function testHydrator(): void public function testHydrateProxyWithMissingAssociations(): void { $user = $this->dm->getReference(HydrationClosureUser::class, 1); - self::assertInstanceOf(InternalProxy::class, $user); + self::assertIsLazyObject($user); $this->dm->getHydratorFactory()->hydrate($user, [ '_id' => 1, diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php index 5a4302abd1..08d54136f4 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php @@ -10,7 +10,6 @@ use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\MappingException; use Doctrine\ODM\MongoDB\Mapping\TimeSeries\Granularity; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Repository\DocumentRepository; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Doctrine\ODM\MongoDB\Tests\ClassMetadataTestUtil; @@ -496,7 +495,7 @@ public function testGetFieldValueInitializesProxy(): void $metadata = $this->dm->getClassMetadata(Album::class); self::assertEquals($document->getName(), $metadata->getFieldValue($proxy, 'name')); - self::assertInstanceOf(InternalProxy::class, $proxy); + self::assertIsLazyObject($proxy); self::assertFalse($this->uow->isUninitializedObject($proxy)); } @@ -511,7 +510,7 @@ public function testGetFieldValueOfIdentifierDoesNotInitializeProxy(): void $metadata = $this->dm->getClassMetadata(Album::class); self::assertEquals($document->getId(), $metadata->getFieldValue($proxy, 'id')); - self::assertInstanceOf(InternalProxy::class, $proxy); + self::assertIsLazyObject($proxy); self::assertTrue($this->uow->isUninitializedObject($proxy)); } @@ -533,7 +532,7 @@ public function testSetFieldValueWithProxy(): void $this->dm->clear(); $proxy = $this->dm->getReference(Album::class, $document->getId()); - self::assertInstanceOf(InternalProxy::class, $proxy); + self::assertIsLazyObject($proxy); $metadata = $this->dm->getClassMetadata(Album::class); $metadata->setFieldValue($proxy, 'name', 'nevermind'); @@ -542,7 +541,7 @@ public function testSetFieldValueWithProxy(): void $this->dm->clear(); $proxy = $this->dm->getReference(Album::class, $document->getId()); - self::assertInstanceOf(InternalProxy::class, $proxy); + self::assertIsLazyObject($proxy); self::assertInstanceOf(Album::class, $proxy); self::assertEquals('nevermind', $proxy->getName()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/StaticProxyFactoryTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php similarity index 98% rename from tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/StaticProxyFactoryTest.php rename to tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php index 12337dd71f..eeb852935a 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/StaticProxyFactoryTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php @@ -17,7 +17,7 @@ use MongoDB\Database; use PHPUnit\Framework\MockObject\MockObject; -class StaticProxyFactoryTest extends BaseTestCase +class ProxyFactoryTest extends BaseTestCase { /** @var Client|MockObject */ private Client $client; diff --git a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php index 793476b2b4..45d3b2adc0 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php @@ -11,7 +11,6 @@ use Doctrine\ODM\MongoDB\APM\CommandLogger; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\MongoDBException; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\Mocks\ExceptionThrowingListenerMock; use Doctrine\ODM\MongoDB\Tests\Mocks\PreUpdateListenerMock; use Doctrine\ODM\MongoDB\UnitOfWork; @@ -489,7 +488,7 @@ public function testRecomputeChangesetForUninitializedProxyDoesNotCreateChangese $user = $this->dm->find(ForumUser::class, $id); self::assertInstanceOf(ForumUser::class, $user); - self::assertInstanceOf(InternalProxy::class, $user->getAvatar()); + self::assertIsLazyObject($user->getAvatar()); $classMetadata = $this->dm->getClassMetadata(ForumAvatar::class); From 041b12fc40014b8d1f78a017cb493f0452c4ee0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Sat, 23 Nov 2024 23:19:46 +0100 Subject: [PATCH 5/9] Fix assert not lazy object --- .../ODM/MongoDB/Tests/BaseTestCase.php | 12 +---- .../Tests/Functional/IdentifiersTest.php | 4 +- .../Tests/Functional/ReferencePrimerTest.php | 47 +++++++++---------- .../Tests/Functional/ReferencesTest.php | 12 ++--- .../Tests/Functional/SimpleReferencesTest.php | 2 +- .../Tests/Functional/Ticket/GH520Test.php | 2 +- .../Tests/Functional/Ticket/GH593Test.php | 4 +- .../Tests/Functional/Ticket/GH602Test.php | 4 +- .../Tests/Functional/Ticket/GH852Test.php | 6 +-- .../Tests/Functional/Ticket/GH936Test.php | 2 +- .../ODM/MongoDB/Tests/Functional/ViewTest.php | 4 +- .../ODM/MongoDB/Tests/HydratorTest.php | 8 ++-- .../Tests/Mapping/ClassMetadataTest.php | 8 ++-- .../Tests/Proxy/Factory/ProxyFactoryTest.php | 2 +- .../ODM/MongoDB/Tests/UnitOfWorkTest.php | 2 +- 15 files changed, 55 insertions(+), 64 deletions(-) diff --git a/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php b/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php index 622ff8603c..67652d5c2a 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php @@ -4,7 +4,6 @@ namespace Doctrine\ODM\MongoDB\Tests; -use Composer\InstalledVersions; use Doctrine\ODM\MongoDB\Configuration; use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver; @@ -118,16 +117,9 @@ public static function assertArraySubset(array $subset, array $array, bool $chec } } - public static function assertIsLazyObject(object $document): void + public static function isLazyObject(object $document): bool { - if (InstalledVersions::isInstalled('friendsofphp/proxy-manager')) { - self::logicalOr( - self::isInstanceOf(InternalProxy::class), - self::isInstanceOf(LazyLoadingInterface::class), - )->evaluate($document); - } else { - self::assertInstanceOf(InternalProxy::class, $document); - } + return $document instanceof InternalProxy || $document instanceof LazyLoadingInterface; } protected static function createMetadataDriverImpl(): MappingDriver diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php index 24da4f568a..3994f0a290 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php @@ -29,7 +29,7 @@ public function testGetIdentifierValue(): void $userTest = $test->getUser(); self::assertEquals($user->getId(), $userTest->getId()); - self::assertIsLazyObject($userTest); + self::assertTrue(self::isLazyObject($userTest)); self::assertTrue($this->uow->isUninitializedObject($userTest)); $this->dm->clear(); @@ -41,7 +41,7 @@ public function testGetIdentifierValue(): void $foundUser = $test->getUser(); self::assertEquals($user->getId(), $class->getIdentifierValue($user)); self::assertEquals($user->getId(), $class->getFieldValue($foundUser, 'id')); - self::assertIsLazyObject($foundUser); + self::assertTrue(self::isLazyObject($foundUser)); self::assertTrue($this->uow->isUninitializedObject($foundUser)); self::assertEquals('jwage', $foundUser->getUsername()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php index 3ffe267e31..8017b89f31 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php @@ -7,7 +7,6 @@ use DateTime; use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; -use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Query\Query; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Doctrine\ODM\MongoDB\Tests\ClassMetadataTestUtil; @@ -95,7 +94,7 @@ public function testPrimeReferencesWithDBRefObjects(): void ->field('groups')->prime(true); foreach ($qb->getQuery() as $user) { - self::assertIsLazyObject($user->getAccount()); + self::assertTrue(self::isLazyObject($user->getAccount())); self::assertFalse($this->uow->isUninitializedObject($user->getAccount())); self::assertCount(2, $user->getGroups()); @@ -104,7 +103,7 @@ public function testPrimeReferencesWithDBRefObjects(): void * initialized, they will not be hydrated as proxy objects. */ foreach ($user->getGroups() as $group) { - self::assertNotInstanceOf(InternalProxy::class, $group); + self::assertFalse(self::isLazyObject($group)); self::assertInstanceOf(Group::class, $group); } } @@ -133,13 +132,13 @@ public function testPrimeReferencesWithSimpleReferences(): void ->field('users')->prime(true); foreach ($qb->getQuery() as $simpleUser) { - self::assertIsLazyObject($simpleUser->getUser()); + self::assertTrue(self::isLazyObject($simpleUser->getUser())); self::assertFalse($this->uow->isUninitializedObject($simpleUser->getUser())); self::assertCount(2, $simpleUser->getUsers()); foreach ($simpleUser->getUsers() as $user) { - self::assertNotInstanceOf(InternalProxy::class, $user); + self::assertFalse(self::isLazyObject($user)); self::assertInstanceOf(User::class, $user); } } @@ -188,20 +187,20 @@ public function testPrimeReferencesNestedInNamedEmbeddedReference(): void ->field('embeddedDocs.referencedDocs')->prime(true); foreach ($qb->getQuery() as $root) { - self::assertNotInstanceOf(InternalProxy::class, $root->embeddedDoc); + self::assertFalse(self::isLazyObject($root->embeddedDoc)); self::assertInstanceOf(EmbeddedWhichReferences::class, $root->embeddedDoc); self::assertCount(2, $root->embeddedDocs); foreach ($root->embeddedDocs as $embeddedDoc) { - self::assertNotInstanceOf(InternalProxy::class, $embeddedDoc); + self::assertFalse(self::isLazyObject($embeddedDoc)); self::assertInstanceOf(EmbeddedWhichReferences::class, $embeddedDoc); - self::assertIsLazyObject($embeddedDoc->referencedDoc); + self::assertTrue(self::isLazyObject($embeddedDoc->referencedDoc)); self::assertFalse($this->uow->isUninitializedObject($embeddedDoc->referencedDoc)); self::assertCount(2, $embeddedDoc->referencedDocs); foreach ($embeddedDoc->referencedDocs as $referencedDoc) { - self::assertNotInstanceOf(InternalProxy::class, $referencedDoc); + self::assertFalse(self::isLazyObject($referencedDoc)); self::assertInstanceOf(Reference::class, $referencedDoc); } } @@ -252,37 +251,37 @@ public function testPrimeReferencesWithDifferentStoreAsReferences(): void assert($referenceUser instanceof ReferenceUser); $user = $referenceUser->getUser(); self::assertInstanceOf(User::class, $user); - self::assertIsLazyObject($user); + self::assertTrue(self::isLazyObject($user)); self::assertFalse($this->uow->isUninitializedObject($user)); self::assertCount(1, $referenceUser->getUsers()); foreach ($referenceUser->getUsers() as $user) { - self::assertNotInstanceOf(InternalProxy::class, $user); + self::assertFalse(self::isLazyObject($user)); self::assertInstanceOf(User::class, $user); } $parentUser = $referenceUser->getParentUser(); - self::assertIsLazyObject($parentUser); + self::assertTrue(self::isLazyObject($parentUser)); self::assertInstanceOf(User::class, $parentUser); self::assertFalse($this->uow->isUninitializedObject($parentUser)); self::assertCount(1, $referenceUser->getParentUsers()); foreach ($referenceUser->getParentUsers() as $user) { - self::assertNotInstanceOf(InternalProxy::class, $user); + self::assertFalse(self::isLazyObject($user)); self::assertInstanceOf(User::class, $user); } $otherUser = $referenceUser->getOtherUser(); self::assertInstanceOf(User::class, $otherUser); - self::assertIsLazyObject($otherUser); + self::assertTrue(self::isLazyObject($otherUser)); self::assertFalse($this->uow->isUninitializedObject($otherUser)); self::assertCount(1, $referenceUser->getOtherUsers()); foreach ($referenceUser->getOtherUsers() as $user) { - self::assertNotInstanceOf(InternalProxy::class, $user); + self::assertFalse(self::isLazyObject($user)); self::assertInstanceOf(User::class, $user); } } @@ -309,10 +308,10 @@ public function testPrimeReferencesWithDiscriminatedReferenceMany(): void foreach ($qb->getQuery() as $user) { $favorites = $user->getFavorites()->toArray(); - self::assertNotInstanceOf(InternalProxy::class, $favorites[0]); + self::assertFalse(self::isLazyObject($favorites[0])); self::assertInstanceOf(Group::class, $favorites[0]); - self::assertNotInstanceOf(InternalProxy::class, $favorites[1]); + self::assertFalse(self::isLazyObject($favorites[1])); self::assertInstanceOf(Project::class, $favorites[1]); } } @@ -331,7 +330,7 @@ public function testPrimeReferencesWithDiscriminatedReferenceOne(): void ->field('server')->prime(true); foreach ($qb->getQuery() as $agent) { - self::assertIsLazyObject($agent->server); + self::assertTrue(self::isLazyObject($agent->server)); self::assertFalse($this->uow->isUninitializedObject($agent->server)); } } @@ -360,7 +359,7 @@ public function testPrimeReferencesIgnoresInitializedProxyObjects(): void self::assertCount(2, $user->getGroups()); foreach ($user->getGroups() as $group) { - self::assertNotInstanceOf(InternalProxy::class, $group); + self::assertFalse(self::isLazyObject($group)); self::assertInstanceOf(Group::class, $group); } } @@ -440,7 +439,7 @@ public function testPrimeReferencesInFindAndModifyResult(): void self::assertCount(1, $user->getGroups()); foreach ($user->getGroups() as $group) { - self::assertNotInstanceOf(InternalProxy::class, $group); + self::assertFalse(self::isLazyObject($group)); self::assertInstanceOf(Group::class, $group); } } @@ -472,7 +471,7 @@ public function testPrimeEmbeddedReferenceOneLevelDeep(): void $phonenumber = $phonenumbers->current(); - self::assertNotInstanceOf(InternalProxy::class, $phonenumber); + self::assertFalse(self::isLazyObject($phonenumber)); self::assertInstanceOf(Phonenumber::class, $phonenumber); } @@ -523,7 +522,7 @@ public function testPrimeEmbeddedReferenceTwoLevelsDeep(): void $currency = $money->getCurrency(); - self::assertIsLazyObject($currency); + self::assertTrue(self::isLazyObject($currency)); self::assertInstanceOf(Currency::class, $currency); self::assertFalse($this->uow->isUninitializedObject($currency)); } @@ -551,7 +550,7 @@ public function testPrimeReferencesInReferenceMany(): void self::assertInstanceOf(BlogPost::class, $post); $comment = $post->comments->first(); - self::assertIsLazyObject($comment->author); + self::assertTrue(self::isLazyObject($comment->author)); self::assertFalse($this->uow->isUninitializedObject($comment->author)); } @@ -578,7 +577,7 @@ public function testPrimeReferencesInReferenceManyWithRepositoryMethodEager(): v self::assertInstanceOf(BlogPost::class, $post); $comment = $post->repoCommentsWithPrimer->first(); - self::assertIsLazyObject($comment->author); + self::assertTrue(self::isLazyObject($comment->author)); self::assertFalse($this->uow->isUninitializedObject($comment->author)); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php index 7d988507b9..88cc979f03 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php @@ -80,7 +80,7 @@ public function testLazyLoadReference(): void assert($profile instanceof Profile); self::assertInstanceOf(Profile::class, $profile); - self::assertIsLazyObject($profile); + self::assertTrue(self::isLazyObject($profile)); $profile->getFirstName(); @@ -102,7 +102,7 @@ public function testLazyLoadedWithNotifyPropertyChanged(): void $user = $this->dm->find($user::class, $user->getId()); $profile = $user->getProfileNotify(); - self::assertIsLazyObject($profile); + self::assertTrue(self::isLazyObject($profile)); self::assertTrue($this->uow->isUninitializedObject($profile)); $user->getProfileNotify()->setLastName('Malarz'); @@ -394,7 +394,7 @@ public function testDocumentNotFoundExceptionWithArrayId(): void ); $test = $this->dm->find($test::class, $test->id); - self::assertIsLazyObject($test->referenceOne); + self::assertTrue(self::isLazyObject($test->referenceOne)); $this->expectException(DocumentNotFoundException::class); $this->expectExceptionMessage( 'The "Doctrine\ODM\MongoDB\Tests\Functional\DocumentWithArrayId" document with identifier ' . @@ -427,7 +427,7 @@ public function testDocumentNotFoundExceptionWithObjectId(): void $user = $this->dm->find($user::class, $user->getId()); $profile = $user->getProfile(); - self::assertIsLazyObject($profile); + self::assertTrue(self::isLazyObject($profile)); $this->expectException(DocumentNotFoundException::class); $this->expectExceptionMessage( 'The "Documents\Profile" document with identifier "abcdefabcdefabcdefabcdef" could not be found.', @@ -458,7 +458,7 @@ public function testDocumentNotFoundExceptionWithMongoBinDataId(): void ); $test = $this->dm->find($test::class, $test->id); - self::assertIsLazyObject($test->referenceOne); + self::assertTrue(self::isLazyObject($test->referenceOne)); $this->expectException(DocumentNotFoundException::class); $this->expectExceptionMessage( 'The "Doctrine\ODM\MongoDB\Tests\Functional\DocumentWithMongoBinDataId" document with identifier ' . @@ -500,7 +500,7 @@ public function testDocumentNotFoundEvent(): void $this->dm->getEventManager()->addEventListener(Events::documentNotFound, new DocumentNotFoundListener($closure)); - self::assertIsLazyObject($profile); + self::assertTrue(self::isLazyObject($profile)); $this->uow->initializeObject($profile); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php index 98d15e9fc5..c164321f57 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php @@ -85,7 +85,7 @@ public function testProxy(): void $user = $test->getUser(); self::assertNotNull($user); self::assertInstanceOf(User::class, $user); - self::assertIsLazyObject($user); + self::assertTrue(self::isLazyObject($user)); self::assertTrue($this->uow->isUninitializedObject($user)); self::assertEquals('jwage', $user->getUsername()); self::assertFalse($this->uow->isUninitializedObject($user)); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php index fac852a6bd..bd44068911 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php @@ -28,7 +28,7 @@ public function testPrimeWithGetSingleResult(): void $document = $query->getSingleResult(); self::assertInstanceOf(GH520Document::class, $document); - self::assertIsLazyObject($document->ref); + self::assertTrue(self::isLazyObject($document->ref)); self::assertFalse($this->uow->isUninitializedObject($document->ref)); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php index 16c361b6bd..b468dd2a86 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php @@ -57,11 +57,11 @@ public function testReferenceManyOwningSidePreparesFilterCriteria(): void */ self::assertCount(2, $user1following); - self::assertIsLazyObject($user1following[0]); + self::assertTrue(self::isLazyObject($user1following[0])); self::assertFalse($this->uow->isUninitializedObject($user1following[0])); self::assertEquals($user2->getId(), $user1following[0]->getId()); - self::assertIsLazyObject($user1following[1]); + self::assertTrue(self::isLazyObject($user1following[1])); self::assertTrue($this->uow->isUninitializedObject($user1following[1])); self::assertEquals($user3->getId(), $user1following[1]->getId()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php index 9207ae9805..f1e3cfe355 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php @@ -48,11 +48,11 @@ public function testReferenceManyOwningSidePreparesFilterCriteriaForDifferentCla */ self::assertCount(2, $user1likes); - self::assertIsLazyObject($user1likes[0]); + self::assertTrue(self::isLazyObject($user1likes[0])); self::assertFalse($this->uow->isUninitializedObject($user1likes[0])); self::assertEquals($thing1->getId(), $user1likes[0]->getId()); - self::assertIsLazyObject($user1likes[1]); + self::assertTrue(self::isLazyObject($user1likes[1])); self::assertTrue($this->uow->isUninitializedObject($user1likes[1])); self::assertEquals($thing2->getId(), $user1likes[1]->getId()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php index b848f5142d..3895f43250 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php @@ -48,7 +48,7 @@ public function testA(Closure $idGenerator): void self::assertEquals($idGenerator('parent'), $parent->id); self::assertEquals('parent', $parent->name); - self::assertIsLazyObject($parent->refOne); + self::assertTrue(self::isLazyObject($parent->refOne)); self::assertInstanceOf(GH852Document::class, $parent->refOne); self::assertTrue($this->uow->isUninitializedObject($parent->refOne)); self::assertEquals($idGenerator('childA'), $parent->refOne->id); @@ -60,13 +60,13 @@ public function testA(Closure $idGenerator): void /* These proxies will be initialized when we first access the collection * by DocumentPersister::loadReferenceManyCollectionOwningSide(). */ - self::assertIsLazyObject($parent->refMany[0]); + self::assertTrue(self::isLazyObject($parent->refMany[0])); self::assertInstanceOf(GH852Document::class, $parent->refMany[0]); self::assertFalse($this->uow->isUninitializedObject($parent->refMany[0])); self::assertEquals($idGenerator('childB'), $parent->refMany[0]->id); self::assertEquals('childB', $parent->refMany[0]->name); - self::assertIsLazyObject($parent->refMany[1]); + self::assertTrue(self::isLazyObject($parent->refMany[1])); self::assertInstanceOf(GH852Document::class, $parent->refMany[1]); self::assertFalse($this->uow->isUninitializedObject($parent->refMany[1])); self::assertEquals($idGenerator('childC'), $parent->refMany[1]->id); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php index c465795dc7..fd25150b8a 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php @@ -26,7 +26,7 @@ public function testRemoveCascadesThroughProxyDocuments(): void $foo = $this->dm->find(GH936Document::class, $foo->id); - self::assertIsLazyObject($foo->ref); + self::assertTrue(self::isLazyObject($foo->ref)); $this->dm->remove($foo); $this->dm->flush(); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php index 918e72db79..63ab535122 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php @@ -111,7 +111,7 @@ public function testViewReferences(): void $viewReference = $this->dm->find(ViewReference::class, $alcaeus->getId()); self::assertInstanceOf(ViewReference::class, $viewReference); - self::assertIsLazyObject($viewReference->getReferenceOneView()); + self::assertTrue(self::isLazyObject($viewReference->getReferenceOneView())); self::assertSame($malarzm->getId(), $viewReference->getReferenceOneView()->getId()); // No proxies for inverse referenceOne @@ -119,7 +119,7 @@ public function testViewReferences(): void self::assertSame($alcaeus->getId(), $viewReference->getReferenceOneViewMappedBy()->getId()); self::assertCount(1, $viewReference->getReferenceManyView()); - self::assertIsLazyObject($viewReference->getReferenceManyView()[0]); + self::assertTrue(self::isLazyObject($viewReference->getReferenceManyView()[0])); self::assertSame($malarzm->getId(), $viewReference->getReferenceManyView()[0]->getId()); // No proxies for inverse referenceMany diff --git a/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php b/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php index e360225454..0982d629bb 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php @@ -40,10 +40,10 @@ public function testHydrator(): void self::assertEquals('jon', $user->name); self::assertInstanceOf(DateTime::class, $user->birthdate); self::assertInstanceOf(HydrationClosureReferenceOne::class, $user->referenceOne); - self::assertIsLazyObject($user->referenceOne); + self::assertTrue(self::isLazyObject($user->referenceOne)); self::assertInstanceOf(PersistentCollection::class, $user->referenceMany); - self::assertIsLazyObject($user->referenceMany[0]); - self::assertIsLazyObject($user->referenceMany[1]); + self::assertTrue(self::isLazyObject($user->referenceMany[0])); + self::assertTrue(self::isLazyObject($user->referenceMany[1])); self::assertInstanceOf(HydrationClosureEmbedOne::class, $user->embedOne); self::assertInstanceOf(PersistentCollection::class, $user->embedMany); self::assertEquals('jon', $user->embedOne->name); @@ -53,7 +53,7 @@ public function testHydrator(): void public function testHydrateProxyWithMissingAssociations(): void { $user = $this->dm->getReference(HydrationClosureUser::class, 1); - self::assertIsLazyObject($user); + self::assertTrue(self::isLazyObject($user)); $this->dm->getHydratorFactory()->hydrate($user, [ '_id' => 1, diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php index 08d54136f4..cb69ce98cb 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php @@ -495,7 +495,7 @@ public function testGetFieldValueInitializesProxy(): void $metadata = $this->dm->getClassMetadata(Album::class); self::assertEquals($document->getName(), $metadata->getFieldValue($proxy, 'name')); - self::assertIsLazyObject($proxy); + self::assertTrue(self::isLazyObject($proxy)); self::assertFalse($this->uow->isUninitializedObject($proxy)); } @@ -510,7 +510,7 @@ public function testGetFieldValueOfIdentifierDoesNotInitializeProxy(): void $metadata = $this->dm->getClassMetadata(Album::class); self::assertEquals($document->getId(), $metadata->getFieldValue($proxy, 'id')); - self::assertIsLazyObject($proxy); + self::assertTrue(self::isLazyObject($proxy)); self::assertTrue($this->uow->isUninitializedObject($proxy)); } @@ -532,7 +532,7 @@ public function testSetFieldValueWithProxy(): void $this->dm->clear(); $proxy = $this->dm->getReference(Album::class, $document->getId()); - self::assertIsLazyObject($proxy); + self::assertTrue(self::isLazyObject($proxy)); $metadata = $this->dm->getClassMetadata(Album::class); $metadata->setFieldValue($proxy, 'name', 'nevermind'); @@ -541,7 +541,7 @@ public function testSetFieldValueWithProxy(): void $this->dm->clear(); $proxy = $this->dm->getReference(Album::class, $document->getId()); - self::assertIsLazyObject($proxy); + self::assertTrue(self::isLazyObject($proxy)); self::assertInstanceOf(Album::class, $proxy); self::assertEquals('nevermind', $proxy->getName()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php index eeb852935a..269ebe2791 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php @@ -44,7 +44,7 @@ public function testProxyInitializeWithException(): void $uow = $this->dm->getUnitOfWork(); $proxy = $this->dm->getReference(Cart::class, '123'); - self::assertIsLazyObject($proxy); + self::assertTrue(self::isLazyObject($proxy)); $closure = static function (DocumentNotFoundEventArgs $eventArgs) { self::fail('DocumentNotFoundListener should not be called'); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php index 45d3b2adc0..f4a5f928ca 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php @@ -488,7 +488,7 @@ public function testRecomputeChangesetForUninitializedProxyDoesNotCreateChangese $user = $this->dm->find(ForumUser::class, $id); self::assertInstanceOf(ForumUser::class, $user); - self::assertIsLazyObject($user->getAvatar()); + self::assertTrue(self::isLazyObject($user->getAvatar())); $classMetadata = $this->dm->getClassMetadata(ForumAvatar::class); From 1888aabb49aa26804c27d5688a56518225dd4331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 31 Dec 2024 11:29:16 +0100 Subject: [PATCH 6/9] Fix deprecation version and refactor autoregenerate condition --- lib/Doctrine/ODM/MongoDB/Configuration.php | 2 +- .../Proxy/Factory/LazyGhostProxyFactory.php | 23 ++++++++----------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/lib/Doctrine/ODM/MongoDB/Configuration.php b/lib/Doctrine/ODM/MongoDB/Configuration.php index 768d32b9b4..07534abcfe 100644 --- a/lib/Doctrine/ODM/MongoDB/Configuration.php +++ b/lib/Doctrine/ODM/MongoDB/Configuration.php @@ -631,7 +631,7 @@ public function setUseLazyGhostObject(bool $flag): void trigger_deprecation( 'doctrine/mongodb-odm', - '2.6', + '2.10', 'Using "friendsofphp/proxy-manager-lts" is deprecated. Use "symfony/var-exporter" LazyGhostObjects instead.', ); } diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php index d22534c608..d667680563 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php @@ -267,20 +267,15 @@ private function loadProxyClass(ClassMetadata $class): string $fileName = $this->getProxyFileName($class->getName(), $this->proxyDir); - switch ($this->autoGenerate) { - case Configuration::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED: - if (file_exists($fileName) && filemtime($fileName) >= filemtime($class->getReflectionClass()->getFileName())) { - break; - } - // no break - case Configuration::AUTOGENERATE_FILE_NOT_EXISTS: - if (file_exists($fileName)) { - break; - } - // no break - case Configuration::AUTOGENERATE_ALWAYS: - $this->generateProxyClass($class, $fileName, $proxyClassName); - break; + if ( + match ($this->autoGenerate) { + Configuration::AUTOGENERATE_FILE_NOT_EXISTS_OR_CHANGED => ! file_exists($fileName) || filemtime($fileName) < filemtime($class->getReflectionClass()->getFileName()), + Configuration::AUTOGENERATE_FILE_NOT_EXISTS => ! file_exists($fileName), + Configuration::AUTOGENERATE_ALWAYS => true, + Configuration::AUTOGENERATE_NEVER => false, + } + ) { + $this->generateProxyClass($class, $fileName, $proxyClassName); } require $fileName; From 564f32d4292cdce8df2f739cef5c58f6b71244b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 31 Dec 2024 12:15:56 +0100 Subject: [PATCH 7/9] Fix testCreateProxyForDocumentWithUnmappedProperties --- .../MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php index 269ebe2791..16373cfe42 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/ProxyFactoryTest.php @@ -9,6 +9,7 @@ use Doctrine\ODM\MongoDB\Event\DocumentNotFoundEventArgs; use Doctrine\ODM\MongoDB\Events; use Doctrine\ODM\MongoDB\LockException; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\Cart; use Documents\DocumentWithUnmappedProperties; @@ -16,6 +17,7 @@ use MongoDB\Collection; use MongoDB\Database; use PHPUnit\Framework\MockObject\MockObject; +use ProxyManager\Proxy\GhostObjectInterface; class ProxyFactoryTest extends BaseTestCase { @@ -80,10 +82,14 @@ private function createMockedDocumentManager(): DocumentManager public function testCreateProxyForDocumentWithUnmappedProperties(): void { $proxy = $this->dm->getReference(DocumentWithUnmappedProperties::class, '123'); - self::assertInstanceOf(GhostObjectInterface::class, $proxy); + self::assertTrue(self::isLazyObject($proxy)); - // Disable initialiser so we can access properties without initialising the object - $proxy->setProxyInitializer(null); + // Disable initializer so we can access properties without initialising the object + if ($proxy instanceof InternalProxy) { + $proxy->__setInitialized(true); + } elseif ($proxy instanceof GhostObjectInterface) { + $proxy->setProxyInitializer(null); + } self::assertSame('bar', $proxy->foo); } From 1ce91acb8fea863f99a939b8a1370f9d5570ca19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 21 Jan 2025 13:53:04 +0100 Subject: [PATCH 8/9] Keep a single ProxyManagerConfiguration instance --- lib/Doctrine/ODM/MongoDB/Configuration.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/Doctrine/ODM/MongoDB/Configuration.php b/lib/Doctrine/ODM/MongoDB/Configuration.php index 07534abcfe..1d1398a93d 100644 --- a/lib/Doctrine/ODM/MongoDB/Configuration.php +++ b/lib/Doctrine/ODM/MongoDB/Configuration.php @@ -128,10 +128,14 @@ class Configuration private ?CacheItemPoolInterface $metadataCache = null; + /** @deprecated */ + private ProxyManagerConfiguration $proxyManagerConfiguration; + private bool $useTransactionalFlush = false; private bool $useLazyGhostObject = true; + /** * Adds a namespace under a certain alias. */ @@ -256,6 +260,7 @@ public function setMetadataCache(CacheItemPoolInterface $cache): void public function setProxyDir(string $dir): void { $this->attributes['proxyDir'] = $dir; + unset($this->proxyManagerConfiguration); } /** @@ -288,6 +293,7 @@ public function getAutoGenerateProxyClasses(): int public function setAutoGenerateProxyClasses(int $mode): void { $this->attributes['autoGenerateProxyClasses'] = $mode; + unset($this->proxyManagerConfiguration); } public function getProxyNamespace(): ?string @@ -298,6 +304,7 @@ public function getProxyNamespace(): ?string public function setProxyNamespace(string $ns): void { $this->attributes['proxyNamespace'] = $ns; + unset($this->proxyManagerConfiguration); } public function setHydratorDir(string $dir): void @@ -586,6 +593,10 @@ public function buildGhostObjectFactory(): LazyLoadingGhostFactory /** @deprecated */ public function getProxyManagerConfiguration(): ProxyManagerConfiguration { + if (isset($this->proxyManagerConfiguration)) { + return $this->proxyManagerConfiguration; + } + $proxyManagerConfiguration = new ProxyManagerConfiguration(); $proxyManagerConfiguration->setProxiesTargetDir($this->getProxyDir()); $proxyManagerConfiguration->setProxiesNamespace($this->getProxyNamespace()); @@ -605,7 +616,7 @@ public function getProxyManagerConfiguration(): ProxyManagerConfiguration throw new InvalidArgumentException('Invalid proxy generation strategy given - only AUTOGENERATE_FILE_NOT_EXISTS and AUTOGENERATE_EVAL are supported.'); } - return $proxyManagerConfiguration; + return $this->proxyManagerConfiguration = $proxyManagerConfiguration; } public function setUseTransactionalFlush(bool $useTransactionalFlush): void From 4d4c8647320641ea4c343ac14dfa7efdcb4cefb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Tue, 21 Jan 2025 14:33:58 +0100 Subject: [PATCH 9/9] Type fixes --- lib/Doctrine/ODM/MongoDB/Configuration.php | 3 +-- .../ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/Doctrine/ODM/MongoDB/Configuration.php b/lib/Doctrine/ODM/MongoDB/Configuration.php index 1d1398a93d..ca61fcbfc6 100644 --- a/lib/Doctrine/ODM/MongoDB/Configuration.php +++ b/lib/Doctrine/ODM/MongoDB/Configuration.php @@ -135,7 +135,6 @@ class Configuration private bool $useLazyGhostObject = true; - /** * Adds a namespace under a certain alias. */ @@ -652,7 +651,7 @@ public function setUseLazyGhostObject(bool $flag): void public function isLazyGhostObjectEnabled(): bool { - return $this->useLazyGhostObject ?? true; + return $this->useLazyGhostObject; } } diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php index d667680563..d19f885fa9 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/LazyGhostProxyFactory.php @@ -123,7 +123,7 @@ public function __construct( $this->lifecycleEventManager = new LifecycleEventManager($dm, $this->uow, $dm->getEventManager()); } - /** @param array $identifier */ + /** @param mixed $identifier */ public function getProxy(ClassMetadata $metadata, $identifier): InternalProxy { $className = $metadata->getName();