diff --git a/UPGRADE-3.0.md b/UPGRADE-3.0.md index 2b8f298592..3e2df83b12 100644 --- a/UPGRADE-3.0.md +++ b/UPGRADE-3.0.md @@ -31,6 +31,16 @@ no longer implements the `PropertyChangedListener` interface. `AnnotationDriver` class defined in `doctrine/persistence` (or in ODM's compatibility layer) +## Proxy objects + +The proxy implementation changed back to Doctrine proxies. +If you are checking for proxies, the following changed: +* Proxies no longer implement `ProxyManager\Proxy\GhostObjectInterface`. + To check whether a returned object is a proxy, check for the + `Doctrine\Persistence\Proxy` interface. +* The `initializeProxy` method has been replaced by `__load`. +* The `isProxyInitialized` method has been replaced by `__isInitialized`. + ## Proxy Class Name Resolution The `Doctrine\ODM\MongoDB\Proxy\Resolver\ClassNameResolver` interface has been diff --git a/composer.json b/composer.json index fdcf7c9a2d..935b96fe18 100644 --- a/composer.json +++ b/composer.json @@ -28,13 +28,13 @@ "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-dumper": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^6.3.9 || ^7.0" }, "require-dev": { "ext-bcmath": "*", @@ -48,6 +48,7 @@ "phpunit/phpunit": "^10.4", "squizlabs/php_codesniffer": "^3.5", "symfony/cache": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^6.2 || ^7.0", "vimeo/psalm": "~5.24.0" }, "conflict": { diff --git a/lib/Doctrine/ODM/MongoDB/Configuration.php b/lib/Doctrine/ODM/MongoDB/Configuration.php index aef7fa05b7..8d390434b7 100644 --- a/lib/Doctrine/ODM/MongoDB/Configuration.php +++ b/lib/Doctrine/ODM/MongoDB/Configuration.php @@ -15,7 +15,6 @@ use Doctrine\ODM\MongoDB\PersistentCollection\DefaultPersistentCollectionGenerator; use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionFactory; use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionGenerator; -use Doctrine\ODM\MongoDB\Proxy\FileLocator; use Doctrine\ODM\MongoDB\Repository\DefaultGridFSRepository; use Doctrine\ODM\MongoDB\Repository\DefaultRepositoryFactory; use Doctrine\ODM\MongoDB\Repository\DocumentRepository; @@ -23,12 +22,7 @@ use Doctrine\ODM\MongoDB\Repository\RepositoryFactory; use Doctrine\Persistence\Mapping\Driver\MappingDriver; use Doctrine\Persistence\ObjectRepository; -use InvalidArgumentException; use MongoDB\Driver\WriteConcern; -use ProxyManager\Configuration as ProxyManagerConfiguration; -use ProxyManager\Factory\LazyLoadingGhostFactory; -use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy; -use ProxyManager\GeneratorStrategy\FileWriterGeneratorStrategy; use Psr\Cache\CacheItemPoolInterface; use ReflectionClass; @@ -52,9 +46,8 @@ class Configuration { /** - * Never autogenerate a proxy/hydrator/persistent collection and rely that - * it was generated by some process before deployment. Copied from - * \Doctrine\Common\Proxy\AbstractProxyFactory. + * Never autogenerate proxy/hydrator/persistent collection and rely that it + * was generated by some process before deployment. */ public const AUTOGENERATE_NEVER = 0; @@ -62,15 +55,15 @@ class Configuration * Always generates a new proxy/hydrator/persistent collection in every request. * * This is only sane during development. - * Copied from \Doctrine\Common\Proxy\AbstractProxyFactory. */ public const AUTOGENERATE_ALWAYS = 1; /** - * Autogenerate the proxy/hydrator/persistent collection class when the file does not exist. + * Autogenerate the proxy/hydrator/persistent collection class when the + * proxy file does not exist. * - * This strategy causes a file exists call whenever any proxy/hydrator is used the - * first time in a request. Copied from \Doctrine\Common\Proxy\AbstractProxyFactory. + * This strategy causes a file_exists() call whenever any proxy/hydrator is + * used the first time in a request. */ public const AUTOGENERATE_FILE_NOT_EXISTS = 2; @@ -78,16 +71,26 @@ class Configuration * Generate the proxy/hydrator/persistent collection classes using eval(). * * This strategy is only sane for development. - * Copied from \Doctrine\Common\Proxy\AbstractProxyFactory. */ 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. * * @psalm-var array{ * autoGenerateHydratorClasses?: self::AUTOGENERATE_*, * autoGeneratePersistentCollectionClasses?: self::AUTOGENERATE_*, + * autoGenerateProxyClasses?: self::AUTOGENERATE_*, * classMetadataFactoryName?: class-string, * defaultCommitOptions?: CommitOptions, * defaultDocumentRepositoryClassName?: class-string>, @@ -106,6 +109,8 @@ class Configuration * persistentCollectionGenerator?: PersistentCollectionGenerator, * persistentCollectionDir?: string, * persistentCollectionNamespace?: string, + * proxyDir?: string, + * proxyNamespace?: string, * repositoryFactory?: RepositoryFactory * } */ @@ -113,15 +118,12 @@ 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); } @@ -248,14 +250,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,12 +258,14 @@ 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 { @@ -279,37 +276,27 @@ public function getAutoGenerateProxyClasses(): int * Sets an int flag that indicates whether proxy classes should always be regenerated * during each script execution. * - * @throws InvalidArgumentException If an invalid mode was given. + * @param bool|self::AUTOGENERATE_* $autoGenerate True is converted to AUTOGENERATE_ALWAYS, false to AUTOGENERATE_NEVER. */ - public function setAutoGenerateProxyClasses(int $mode): void + public function setAutoGenerateProxyClasses(bool|int $autoGenerate): 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'] = (int) $autoGenerate; } - public function getProxyNamespace(): ?string + /** + * Gets the namespace where proxy classes reside. + */ + public function getProxyNamespace(): string|null { - return $this->getProxyManagerConfiguration()->getProxiesNamespace(); + return $this->attributes['proxyNamespace'] ?? null; } + /** + * Sets the namespace where proxy classes reside. + */ public function setProxyNamespace(string $ns): void { - $this->getProxyManagerConfiguration()->setProxiesNamespace($ns); + $this->attributes['proxyNamespace'] = $ns; } public function setHydratorDir(string $dir): void @@ -589,16 +576,6 @@ public function getPersistentCollectionGenerator(): PersistentCollectionGenerato return $this->attributes['persistentCollectionGenerator']; } - public function buildGhostObjectFactory(): LazyLoadingGhostFactory - { - return new LazyLoadingGhostFactory(clone $this->getProxyManagerConfiguration()); - } - - public function getProxyManagerConfiguration(): ProxyManagerConfiguration - { - return $this->proxyManagerConfiguration; - } - public function setUseTransactionalFlush(bool $useTransactionalFlush): void { $this->useTransactionalFlush = $useTransactionalFlush; diff --git a/lib/Doctrine/ODM/MongoDB/DocumentManager.php b/lib/Doctrine/ODM/MongoDB/DocumentManager.php index 0a281a6c6f..06c28caf6b 100644 --- a/lib/Doctrine/ODM/MongoDB/DocumentManager.php +++ b/lib/Doctrine/ODM/MongoDB/DocumentManager.php @@ -9,11 +9,9 @@ use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactoryInterface; use Doctrine\ODM\MongoDB\Mapping\MappingException; -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; +use Doctrine\ODM\MongoDB\Proxy\DefaultProxyClassNameResolver; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; +use Doctrine\ODM\MongoDB\Proxy\ProxyFactory; use Doctrine\ODM\MongoDB\Query\FilterCollection; use Doctrine\ODM\MongoDB\Repository\DocumentRepository; use Doctrine\ODM\MongoDB\Repository\GridFSRepository; @@ -29,7 +27,6 @@ use MongoDB\Database; use MongoDB\Driver\ReadPreference; use MongoDB\GridFS\Bucket; -use ProxyManager\Proxy\GhostObjectInterface; use RuntimeException; use Throwable; @@ -133,7 +130,6 @@ class DocumentManager implements ObjectManager */ private ?FilterCollection $filterCollection = null; - /** @var ProxyClassNameResolver&ClassNameResolver */ private ProxyClassNameResolver $classNameResolver; private static ?string $version = null; @@ -157,7 +153,7 @@ protected function __construct(?Client $client = null, ?Configuration $config = ], ); - $this->classNameResolver = new CachingClassNameResolver(new ProxyManagerClassNameResolver($this->config)); + $this->classNameResolver = new DefaultProxyClassNameResolver(); $metadataFactoryClassName = $this->config->getClassMetadataFactoryName(); $this->metadataFactory = new $metadataFactoryClassName(); @@ -182,7 +178,12 @@ 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 ProxyFactory( + $this, + $config->getProxyDir(), + $config->getProxyNamespace(), + $config->getAutoGenerateProxyClasses(), + ); $this->repositoryFactory = $this->config->getRepositoryFactory(); } @@ -279,7 +280,7 @@ public function getSchemaManager(): SchemaManager * * @deprecated Fetch metadata for any class string (e.g. proxy object class) and read the class name from the metadata object */ - public function getClassNameResolver(): ClassNameResolver + public function getClassNameResolver(): ProxyClassNameResolver { return $this->classNameResolver; } @@ -595,7 +596,7 @@ public function flush(array $options = []): void * @param mixed $identifier * @psalm-param class-string $documentName * - * @psalm-return T|(T&GhostObjectInterface) + * @return T|(T&InternalProxy) * * @template T of object */ @@ -612,7 +613,7 @@ public function getReference(string $documentName, $identifier): object return $document; } - /** @psalm-var T&GhostObjectInterface $document */ + /** @psalm-var T&InternalProxy $document */ $document = $this->proxyFactory->getProxy($class, $identifier); $this->unitOfWork->registerManaged($document, $identifier, []); diff --git a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php index 52c1f3943e..3603ae4497 100644 --- a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php @@ -11,9 +11,9 @@ 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; use function array_key_exists; use function chmod; @@ -448,19 +448,9 @@ public function hydrate(object $document, array $data, array $hints = []): array } } - if ($document instanceof GhostObjectInterface && $document->getProxyInitializer() !== null) { - // Inject an empty initialiser to not load any object data - $document->setProxyInitializer(static function ( - GhostObjectInterface $ghostObject, - string $method, // we don't care - array $parameters, // we don't care - &$initializer, - array $properties, // we currently do not use this - ): bool { - $initializer = null; - - return true; - }); + if ($document instanceof InternalProxy) { + // Skip initialization to not load any object data + $document->__setInitialized(true); } $data = $this->getHydratorFor($metadata->name)->hydrate($document, $data, $hints); diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php index cd20698af2..a581c54b74 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php @@ -13,6 +13,7 @@ use Doctrine\Instantiator\InstantiatorInterface; use Doctrine\ODM\MongoDB\Id\IdGenerator; use Doctrine\ODM\MongoDB\LockException; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Types\Incrementable; use Doctrine\ODM\MongoDB\Types\Type; use Doctrine\ODM\MongoDB\Types\Versionable; @@ -23,7 +24,6 @@ use Doctrine\Persistence\Reflection\EnumReflectionProperty; use InvalidArgumentException; use LogicException; -use ProxyManager\Proxy\GhostObjectInterface; use ReflectionClass; use ReflectionEnum; use ReflectionNamedType; @@ -1898,10 +1898,10 @@ 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->initializeProxy(); + $document->__load(); } $this->reflFields[$field]->setValue($document, $value); @@ -1914,8 +1914,8 @@ 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()) { - $document->initializeProxy(); + if ($document instanceof InternalProxy && $field !== $this->identifier && ! $document->__isInitialized()) { + $document->__load(); } return $this->reflFields[$field]->getValue($document); 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 @@ +resolveClassName($object::class); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/ProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/ProxyFactory.php deleted file mode 100644 index 35b292281e..0000000000 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/ProxyFactory.php +++ /dev/null @@ -1,27 +0,0 @@ -[] $classes */ - public function generateProxyClasses(array $classes): int; - - /** - * Gets a reference proxy instance for the entity of the given type and identified by - * the given identifier. - * - * @param mixed $identifier - * @psalm-param ClassMetadata $metadata - * - * @psalm-return T&GhostObjectInterface - * - * @template T of object - */ - public function getProxy(ClassMetadata $metadata, $identifier): GhostObjectInterface; -} diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php deleted file mode 100644 index 737795fe48..0000000000 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php +++ /dev/null @@ -1,169 +0,0 @@ -uow = $documentManager->getUnitOfWork(); - $this->lifecycleEventManager = new LifecycleEventManager($documentManager, $this->uow, $documentManager->getEventManager()); - $this->proxyFactory = $documentManager->getConfiguration()->buildGhostObjectFactory(); - } - - /** - * @param mixed $identifier - * @psalm-param ClassMetadata $metadata - * - * @psalm-return T&GhostObjectInterface - * - * @template T of object - */ - public function getProxy(ClassMetadata $metadata, $identifier): GhostObjectInterface - { - $documentPersister = $this->uow->getDocumentPersister($metadata->getName()); - - $ghostObject = $this - ->proxyFactory - ->createProxy( - $metadata->getName(), - $this->createInitializer($metadata, $documentPersister), - [ - 'skippedProperties' => $this->skippedFieldsFqns($metadata), - ], - ); - - $metadata->setIdentifierValue($ghostObject, $identifier); - - return $ghostObject; - } - - public function generateProxyClasses(array $classes): int - { - $concreteClasses = array_filter($classes, static fn (ClassMetadata $metadata): bool => ! ($metadata->isMappedSuperclass || $metadata->isQueryResultDocument || $metadata->getReflectionClass()->isAbstract())); - - foreach ($concreteClasses as $metadata) { - $this - ->proxyFactory - ->createProxy( - $metadata->getName(), - static fn (): bool => true, // empty closure, serves its purpose, for now - [ - 'skippedProperties' => $this->skippedFieldsFqns($metadata), - ], - ); - } - - return count($concreteClasses); - } - - /** - * @param ClassMetadata $metadata - * @param DocumentPersister $documentPersister - * - * @psalm-return Closure( - * TDocument&GhostObjectInterface=, - * string=, - * array=, - * ?Closure=, - * array= - * ) : bool - * - * @template TDocument of object - */ - private function createInitializer( - ClassMetadata $metadata, - DocumentPersister $documentPersister, - ): Closure { - return function ( - GhostObjectInterface $ghostObject, - string $method, // we don't care - array $parameters, // we don't care - &$initializer, - array $properties, // we currently do not use this - ) use ( - $metadata, - $documentPersister, - ): bool { - $originalInitializer = $initializer; - $initializer = null; - $identifier = $metadata->getIdentifierValue($ghostObject); - - try { - $document = $documentPersister->load(['_id' => $identifier], $ghostObject); - } catch (Throwable $exception) { - $initializer = $originalInitializer; - - throw $exception; - } - - if (! $document) { - $initializer = $originalInitializer; - - if (! $this->lifecycleEventManager->documentNotFound($ghostObject, $identifier)) { - throw DocumentNotFoundException::documentNotFound($metadata->getName(), $identifier); - } - } - - if ($ghostObject instanceof NotifyPropertyChanged) { - $ghostObject->addPropertyChangedListener($this->uow); - } - - return true; - }; - } - - /** - * @param ClassMetadata $metadata - * - * @return array - */ - private function skippedFieldsFqns(ClassMetadata $metadata): array - { - $idFieldFqcns = []; - - foreach ($metadata->getIdentifierFieldNames() as $idField) { - $idFieldFqcns[] = $this->propertyFqcn($metadata->getReflectionProperty($idField)); - } - - return $idFieldFqcns; - } - - private function propertyFqcn(ReflectionProperty $property): string - { - if ($property->isPrivate()) { - return "\0" . $property->getDeclaringClass()->getName() . "\0" . $property->getName(); - } - - if ($property->isProtected()) { - return "\0*\0" . $property->getName(); - } - - return $property->getName(); - } -} diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/FileLocator.php b/lib/Doctrine/ODM/MongoDB/Proxy/FileLocator.php deleted file mode 100644 index 516b4ef6e9..0000000000 --- a/lib/Doctrine/ODM/MongoDB/Proxy/FileLocator.php +++ /dev/null @@ -1,25 +0,0 @@ - + */ +interface InternalProxy extends Proxy +{ + public function __setInitialized(bool $initialized): void; +} diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php new file mode 100644 index 0000000000..145edaf539 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php @@ -0,0 +1,385 @@ +; + +/** + * 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'; + } + + 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 { + $proxy = self::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/Resolver/CachingClassNameResolver.php b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/CachingClassNameResolver.php deleted file mode 100644 index 8c2879ed34..0000000000 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/CachingClassNameResolver.php +++ /dev/null @@ -1,35 +0,0 @@ - */ - private array $resolvedNames = []; - - public function __construct(private ProxyClassNameResolver $resolver) - { - } - - /** - * Gets the real class name of a class name that could be a proxy. - */ - public function getRealClass(string $class): string - { - return $this->resolveClassName($class); - } - - public function resolveClassName(string $className): string - { - if (! isset($this->resolvedNames[$className])) { - $this->resolvedNames[$className] = $this->resolver->resolveClassName($className); - } - - return $this->resolvedNames[$className]; - } -} diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ClassNameResolver.php b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ClassNameResolver.php deleted file mode 100644 index 36ffaafc59..0000000000 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ClassNameResolver.php +++ /dev/null @@ -1,16 +0,0 @@ -resolveClassName($class); - } - - /** - * @psalm-param class-string|class-string> $className - * - * @psalm-return class-string - * - * @psalm-template RealClassName of object - */ - public function resolveClassName(string $className): string - { - return $this->getClassNameInflector()->getUserClassName($className); - } - - private function getClassNameInflector(): ClassNameInflectorInterface - { - return $this->configuration->getProxyManagerConfiguration()->getClassNameInflector(); - } -} diff --git a/lib/Doctrine/ODM/MongoDB/UnitOfWork.php b/lib/Doctrine/ODM/MongoDB/UnitOfWork.php index 774f55f6a6..7b0909f950 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; @@ -23,11 +24,11 @@ use Doctrine\Persistence\Mapping\RuntimeReflectionService; use Doctrine\Persistence\NotifyPropertyChanged; use Doctrine\Persistence\PropertyChangedListener; +use Doctrine\Persistence\Proxy; use InvalidArgumentException; use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Session; use MongoDB\Driver\WriteConcern; -use ProxyManager\Proxy\GhostObjectInterface; use ReflectionProperty; use Throwable; use UnexpectedValueException; @@ -971,7 +972,7 @@ private function computeAssociationChanges(object $parentDocument, array $assoc, $class = $this->dm->getClassMetadata($parentDocument::class); $topOrExistingDocument = ( ! $isNewParentDocument || ! $class->isEmbeddedDocument); - if ($value instanceof GhostObjectInterface && ! $value->isProxyInitialized()) { + if ($value instanceof Proxy && ! $value->__isInitialized()) { return; } @@ -2781,7 +2782,8 @@ 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); + assert($document instanceof InternalProxy); + $document->__setInitialized(true); $overrideLocalValues = true; if ($document instanceof NotifyPropertyChanged) { $document->addPropertyChangedListener($this); @@ -3061,8 +3063,8 @@ public function getScheduledCollectionUpdates(): array */ public function initializeObject(object $obj): void { - if ($obj instanceof GhostObjectInterface && $obj->isProxyInitialized() === false) { - $obj->initializeProxy(); + if ($obj instanceof Proxy && $obj->__isInitialized() === false) { + $obj->__load(); } elseif ($obj instanceof PersistentCollectionInterface) { $obj->initialize(); } @@ -3076,8 +3078,8 @@ public function initializeObject(object $obj): void public function isUninitializedObject(object $obj): bool { return match (true) { - $obj instanceof GhostObjectInterface => $obj->isProxyInitialized() === false, $obj instanceof PersistentCollectionInterface => $obj->isInitialized() === false, + $obj instanceof Proxy => $obj->__isInitialized() === false, default => false }; } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ae58dfd951..5fbaf29107 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -65,16 +65,6 @@ parameters: count: 1 path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php - - - message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Merge\\:\\:whenMatched\\(\\) has parameter \\$whenMatched with no value type specified in iterable type array\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php - - - - message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Merge\\:\\:\\$whenMatched type has no value type specified in iterable type array\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php - - message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" count: 1 @@ -425,6 +415,11 @@ parameters: count: 1 path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Configuration\\:\\:getAutoGenerateProxyClasses\\(\\) should return 0\\|1\\|2\\|3\\|4 but returns int\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Configuration.php + - message: "#^Return type \\(Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadataFactoryInterface\\) of method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getMetadataFactory\\(\\) should be compatible with return type \\(Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\\\>\\) of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getMetadataFactory\\(\\)$#" count: 1 @@ -440,11 +435,6 @@ parameters: count: 1 path: lib/Doctrine/ODM/MongoDB/Event/OnClearEventArgs.php - - - message: "#^Parameter \\#1 \\$initializer of method ProxyManager\\\\Proxy\\\\GhostObjectInterface\\\\:\\:setProxyInitializer\\(\\) expects \\(Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface\\\\=, string\\=, array\\\\=, Closure\\|null\\=, array\\\\=\\)\\: bool\\)\\|null, Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface, string, array, mixed, array\\)\\: true given\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php - - message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\Annotations\\\\SearchIndex\\:\\:__construct\\(\\) has parameter \\$analyzers with no value type specified in iterable type array\\.$#" count: 1 @@ -530,11 +520,6 @@ parameters: count: 1 path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - - message: "#^Right side of && is always true\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" count: 1 @@ -546,9 +531,19 @@ parameters: path: lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.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\\.$#" + message: "#^Call to an undefined static method Doctrine\\\\ODM\\\\MongoDB\\\\Proxy\\\\ProxyFactory\\:\\:createLazyGhost\\(\\)\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Proxy\\\\ProxyFactory\\:\\:createLazyInitializer\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php + + - + message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" count: 1 - path: lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php + path: lib/Doctrine/ODM/MongoDB/Proxy/ProxyFactory.php - message: "#^Unsafe call to private method Doctrine\\\\ODM\\\\MongoDB\\\\Query\\\\Expr\\:\\:convertExpression\\(\\) through static\\:\\:\\.$#" diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 5a9cfda35a..0363b8b0b9 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -160,10 +160,40 @@ - - - - + + + + + + identifier => $identifier]]]> + + + + + + + + + proxyFactories]]> + + + proxyFactories[$className] = $proxyFactory]]> + + + + + + + 4]]> + 4)]]> + 4)]]> + + + + + diff --git a/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php b/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php index 832037e53c..5ce2a53c1c 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php @@ -13,7 +13,7 @@ use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory; use Doctrine\ODM\MongoDB\Mapping\MappingException; use Doctrine\ODM\MongoDB\MongoDBException; -use Doctrine\ODM\MongoDB\Proxy\Factory\ProxyFactory; +use Doctrine\ODM\MongoDB\Proxy\ProxyFactory; use Doctrine\ODM\MongoDB\Query\Builder as QueryBuilder; use Doctrine\ODM\MongoDB\Query\FilterCollection; use Doctrine\ODM\MongoDB\SchemaManager; diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php index 66cec01810..cc0c0594c8 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php @@ -5,9 +5,9 @@ namespace Doctrine\ODM\MongoDB\Tests\Functional; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; +use Doctrine\Persistence\Proxy; use Documents\Event; use Documents\User; -use ProxyManager\Proxy\LazyLoadingInterface; use function assert; use function get_class; @@ -30,7 +30,7 @@ public function testGetIdentifierValue(): void $userTest = $test->getUser(); self::assertEquals($user->getId(), $userTest->getId()); - self::assertInstanceOf(LazyLoadingInterface::class, $userTest); + self::assertInstanceOf(Proxy::class, $userTest); self::assertTrue($this->uow->isUninitializedObject($userTest)); $this->dm->clear(); @@ -42,7 +42,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(Proxy::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 44c2c5c40e..503d458729 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php @@ -10,6 +10,7 @@ use Doctrine\ODM\MongoDB\Query\Query; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Doctrine\ODM\MongoDB\Tests\ClassMetadataTestUtil; +use Doctrine\Persistence\Proxy; use Documents\Account; use Documents\Agent; use Documents\BlogPost; @@ -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(Proxy::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(Proxy::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(Proxy::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(Proxy::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(Proxy::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(Proxy::class, $embeddedDoc); self::assertInstanceOf(EmbeddedWhichReferences::class, $embeddedDoc); - self::assertInstanceOf(GhostObjectInterface::class, $embeddedDoc->referencedDoc); + self::assertInstanceOf(Proxy::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(Proxy::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(Proxy::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(Proxy::class, $user); self::assertInstanceOf(User::class, $user); } $parentUser = $referenceUser->getParentUser(); - self::assertInstanceOf(GhostObjectInterface::class, $parentUser); + self::assertInstanceOf(Proxy::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(Proxy::class, $user); self::assertInstanceOf(User::class, $user); } $otherUser = $referenceUser->getOtherUser(); self::assertInstanceOf(User::class, $otherUser); - self::assertInstanceOf(GhostObjectInterface::class, $otherUser); + self::assertInstanceOf(Proxy::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(Proxy::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(Proxy::class, $favorites[0]); self::assertInstanceOf(Group::class, $favorites[0]); - self::assertNotInstanceOf(GhostObjectInterface::class, $favorites[1]); + self::assertNotInstanceOf(Proxy::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(Proxy::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(Proxy::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(Proxy::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(Proxy::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(Proxy::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(Proxy::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(Proxy::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..b3804acfb5 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php @@ -13,6 +13,7 @@ use Doctrine\ODM\MongoDB\PersistentCollection; use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionInterface; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; +use Doctrine\Persistence\Proxy; use Documents\Account; use Documents\Address; use Documents\Group; @@ -22,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; @@ -82,7 +81,7 @@ public function testLazyLoadReference(): void assert($profile instanceof Profile); self::assertInstanceOf(Profile::class, $profile); - self::assertInstanceOf(GhostObjectInterface::class, $profile); + self::assertInstanceOf(Proxy::class, $profile); $profile->getFirstName(); @@ -104,7 +103,7 @@ public function testLazyLoadedWithNotifyPropertyChanged(): void $user = $this->dm->find($user::class, $user->getId()); $profile = $user->getProfileNotify(); - self::assertInstanceOf(GhostObjectInterface::class, $profile); + self::assertInstanceOf(Proxy::class, $profile); self::assertTrue($this->uow->isUninitializedObject($profile)); $user->getProfileNotify()->setLastName('Malarz'); @@ -396,13 +395,13 @@ public function testDocumentNotFoundExceptionWithArrayId(): void ); $test = $this->dm->find($test::class, $test->id); - self::assertInstanceOf(LazyLoadingInterface::class, $test->referenceOne); + self::assertInstanceOf(Proxy::class, $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(); + $test->referenceOne->__load(); } public function testDocumentNotFoundExceptionWithObjectId(): void @@ -429,12 +428,12 @@ public function testDocumentNotFoundExceptionWithObjectId(): void $user = $this->dm->find($user::class, $user->getId()); $profile = $user->getProfile(); - self::assertInstanceOf(LazyLoadingInterface::class, $profile); + self::assertInstanceOf(Proxy::class, $profile); $this->expectException(DocumentNotFoundException::class); $this->expectExceptionMessage( 'The "Documents\Profile" document with identifier "abcdefabcdefabcdefabcdef" could not be found.', ); - $profile->initializeProxy(); + $profile->__load(); } public function testDocumentNotFoundExceptionWithMongoBinDataId(): void @@ -460,13 +459,13 @@ public function testDocumentNotFoundExceptionWithMongoBinDataId(): void ); $test = $this->dm->find($test::class, $test->id); - self::assertInstanceOf(LazyLoadingInterface::class, $test->referenceOne); + self::assertInstanceOf(Proxy::class, $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(); + $test->referenceOne->__load(); } public function testDocumentNotFoundEvent(): void @@ -502,8 +501,13 @@ public function testDocumentNotFoundEvent(): void $this->dm->getEventManager()->addEventListener(Events::documentNotFound, new DocumentNotFoundListener($closure)); - self::assertInstanceOf(LazyLoadingInterface::class, $profile); - $profile->initializeProxy(); + self::assertInstanceOf(Proxy::class, $profile); + $this->expectException(DocumentNotFoundException::class); + $this->expectExceptionMessage( + 'The "Documents\Profile" document with identifier ' . + '"abcdefabcdefabcdefabcdef" could not be found.', + ); + $profile->__load(); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php index 4d69bff53f..b50c54e5cb 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/SimpleReferencesTest.php @@ -4,11 +4,12 @@ namespace Doctrine\ODM\MongoDB\Tests\Functional; +use Doctrine\ODM\MongoDB\Proxy\InternalProxy; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; +use Doctrine\Persistence\Proxy; use Documents\SimpleReferenceUser; use Documents\User; use MongoDB\BSON\ObjectId; -use ProxyManager\Proxy\GhostObjectInterface; use stdClass; use function assert; @@ -84,10 +85,10 @@ public function testProxy(): void self::assertNotNull($test); $user = $test->getUser(); - assert($user instanceof User && $user instanceof GhostObjectInterface); + assert($user instanceof User && $user instanceof InternalProxy); self::assertNotNull($user); self::assertInstanceOf(User::class, $user); - self::assertInstanceOf(GhostObjectInterface::class, $user); + self::assertInstanceOf(Proxy::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..6b83e26082 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH520Test.php @@ -8,7 +8,7 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use ProxyManager\Proxy\GhostObjectInterface; +use Doctrine\Persistence\Proxy; class GH520Test extends BaseTestCase { @@ -29,7 +29,7 @@ public function testPrimeWithGetSingleResult(): void $document = $query->getSingleResult(); self::assertInstanceOf(GH520Document::class, $document); - self::assertInstanceOf(GhostObjectInterface::class, $document->ref); + self::assertInstanceOf(Proxy::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..8b24404a36 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH593Test.php @@ -9,7 +9,7 @@ use Doctrine\ODM\MongoDB\DocumentNotFoundException; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use ProxyManager\Proxy\GhostObjectInterface; +use Doctrine\Persistence\Proxy; use function iterator_to_array; @@ -58,16 +58,16 @@ public function testReferenceManyOwningSidePreparesFilterCriteria(): void */ self::assertCount(2, $user1following); - self::assertInstanceOf(GhostObjectInterface::class, $user1following[0]); + self::assertInstanceOf(Proxy::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(Proxy::class, $user1following[1]); self::assertTrue($this->uow->isUninitializedObject($user1following[1])); self::assertEquals($user3->getId(), $user1following[1]->getId()); $this->expectException(DocumentNotFoundException::class); - $user1following[1]->initializeProxy(); + $user1following[1]->__load(); } 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 ce38f44edb..299f84237a 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH602Test.php @@ -9,7 +9,7 @@ use Doctrine\ODM\MongoDB\DocumentNotFoundException; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use ProxyManager\Proxy\GhostObjectInterface; +use Doctrine\Persistence\Proxy; use function iterator_to_array; @@ -49,16 +49,16 @@ public function testReferenceManyOwningSidePreparesFilterCriteriaForDifferentCla */ self::assertCount(2, $user1likes); - self::assertInstanceOf(GhostObjectInterface::class, $user1likes[0]); + self::assertInstanceOf(Proxy::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(Proxy::class, $user1likes[1]); self::assertTrue($this->uow->isUninitializedObject($user1likes[1])); self::assertEquals($thing2->getId(), $user1likes[1]->getId()); $this->expectException(DocumentNotFoundException::class); - $user1likes[1]->initializeProxy(); + $user1likes[1]->__load(); } 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 0b4e912e82..eeb81be210 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php @@ -11,9 +11,9 @@ use Doctrine\ODM\MongoDB\Iterator\Iterator; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; +use Doctrine\Persistence\Proxy; use MongoDB\BSON\Binary; use PHPUnit\Framework\Attributes\DataProvider; -use ProxyManager\Proxy\GhostObjectInterface; class GH852Test extends BaseTestCase { @@ -49,7 +49,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(Proxy::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 +61,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(Proxy::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(Proxy::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..200992faee 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH936Test.php @@ -8,7 +8,7 @@ use Doctrine\ODM\MongoDB\Events; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use ProxyManager\Proxy\GhostObjectInterface; +use Doctrine\Persistence\Proxy; class GH936Test extends BaseTestCase { @@ -27,7 +27,7 @@ public function testRemoveCascadesThroughProxyDocuments(): void $foo = $this->dm->find(GH936Document::class, $foo->id); - self::assertInstanceOf(GhostObjectInterface::class, $foo->ref); + self::assertInstanceOf(Proxy::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..e7e0050d63 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ViewTest.php @@ -7,10 +7,10 @@ use Doctrine\ODM\MongoDB\Repository\ViewRepository; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Doctrine\ODM\MongoDB\UnitOfWork; +use Doctrine\Persistence\Proxy; use Documents\CmsUser; use Documents\UserName; use Documents\ViewReference; -use ProxyManager\Proxy\GhostObjectInterface; use function assert; @@ -112,7 +112,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(Proxy::class, $viewReference->getReferenceOneView()); self::assertSame($malarzm->getId(), $viewReference->getReferenceOneView()->getId()); // No proxies for inverse referenceOne @@ -120,7 +120,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(Proxy::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..cfbd946506 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/HydratorTest.php @@ -10,8 +10,9 @@ 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; +use Doctrine\Persistence\Proxy; class HydratorTest extends BaseTestCase { @@ -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(Proxy::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(Proxy::class, $user->referenceMany[0]); + self::assertInstanceOf(Proxy::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 adf80fb6a6..2a0d72d5ca 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php @@ -13,6 +13,7 @@ use Doctrine\ODM\MongoDB\Tests\ClassMetadataTestUtil; use Doctrine\ODM\MongoDB\Types\Type; use Doctrine\ODM\MongoDB\Utility\CollectionHelper; +use Doctrine\Persistence\Proxy; use Doctrine\Persistence\Reflection\EnumReflectionProperty; use DoctrineGlobal_Article; use DoctrineGlobal_User; @@ -36,7 +37,6 @@ use Generator; use InvalidArgumentException; use PHPUnit\Framework\Attributes\DataProvider; -use ProxyManager\Proxy\GhostObjectInterface; use ReflectionClass; use ReflectionException; use stdClass; @@ -493,7 +493,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(Proxy::class, $proxy); self::assertFalse($this->uow->isUninitializedObject($proxy)); } @@ -508,7 +508,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(Proxy::class, $proxy); self::assertTrue($this->uow->isUninitializedObject($proxy)); } @@ -530,7 +530,7 @@ public function testSetFieldValueWithProxy(): void $this->dm->clear(); $proxy = $this->dm->getReference(Album::class, $document->getId()); - self::assertInstanceOf(GhostObjectInterface::class, $proxy); + self::assertInstanceOf(Proxy::class, $proxy); $metadata = $this->dm->getClassMetadata(Album::class); $metadata->setFieldValue($proxy, 'name', 'nevermind'); @@ -539,7 +539,8 @@ public function testSetFieldValueWithProxy(): void $this->dm->clear(); $proxy = $this->dm->getReference(Album::class, $document->getId()); - self::assertInstanceOf(GhostObjectInterface::class, $proxy); + self::assertInstanceOf(Proxy::class, $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/ProxyFactoryTest.php similarity index 88% rename from tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/StaticProxyFactoryTest.php rename to tests/Doctrine/ODM/MongoDB/Tests/Proxy/ProxyFactoryTest.php index 2be5e3fc1a..62562def78 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Proxy/Factory/StaticProxyFactoryTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Proxy/ProxyFactoryTest.php @@ -9,17 +9,17 @@ 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 MongoDB\Client; use MongoDB\Collection; use MongoDB\Database; use PHPUnit\Framework\MockObject\MockObject; -use ProxyManager\Proxy\GhostObjectInterface; -class StaticProxyFactoryTest extends BaseTestCase +class ProxyFactoryTest extends BaseTestCase { - /** @var Client|MockObject */ + /** @var Client&MockObject */ private Client $client; public function setUp(): void @@ -49,7 +49,7 @@ public function testProxyInitializeWithException(): void $uow = $this->dm->getUnitOfWork(); $proxy = $this->dm->getReference(Cart::class, '123'); - self::assertInstanceOf(GhostObjectInterface::class, $proxy); + self::assertInstanceOf(InternalProxy::class, $proxy); $closure = static function (DocumentNotFoundEventArgs $eventArgs) { self::fail('DocumentNotFoundListener should not be called'); @@ -57,7 +57,7 @@ public function testProxyInitializeWithException(): void $this->dm->getEventManager()->addEventListener(Events::documentNotFound, new DocumentNotFoundListener($closure)); try { - $proxy->initializeProxy(); + $proxy->__load(); self::fail('An exception should have been thrown'); } catch (LockException $exception) { self::assertInstanceOf(LockException::class, $exception); @@ -65,7 +65,7 @@ public function testProxyInitializeWithException(): void $uow->computeChangeSets(); - self::assertFalse($proxy->isProxyInitialized(), 'Proxy should not be initialized'); + self::assertFalse($proxy->__isInitialized(), '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 55a9257f8d..d1c0ab7b4f 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php @@ -16,6 +16,7 @@ use Doctrine\ODM\MongoDB\UnitOfWork; use Doctrine\Persistence\NotifyPropertyChanged; use Doctrine\Persistence\PropertyChangedListener; +use Doctrine\Persistence\Proxy; use Documents\Address; use Documents\File; use Documents\FileWithoutMetadata; @@ -28,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; @@ -489,7 +489,7 @@ public function testRecomputeChangesetForUninitializedProxyDoesNotCreateChangese $user = $this->dm->find(ForumUser::class, $id); self::assertInstanceOf(ForumUser::class, $user); - self::assertInstanceOf(GhostObjectInterface::class, $user->getAvatar()); + self::assertInstanceOf(Proxy::class, $user->getAvatar()); $classMetadata = $this->dm->getClassMetadata(ForumAvatar::class);