Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch proxies to LazyGhostTrait #2700

Merged
merged 10 commits into from
Jan 22, 2025
Prev Previous commit
Next Next commit
Run the full test suite with proxy manager
  • Loading branch information
GromNaN committed Jan 21, 2025
commit 4e343864cf681a7af2cc42d8646e3207ffcced56
21 changes: 21 additions & 0 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ jobs:
- "highest"
symfony-version:
- "stable"
proxy:
- "lazy-ghost"
include:
# Test against lowest dependencies
- dependencies: "lowest"
Expand All @@ -42,20 +44,30 @@ jobs:
driver-version: "1.17.0"
topology: "server"
symfony-version: "stable"
proxy: "lazy-ghost"
# Test with highest dependencies
- topology: "server"
php-version: "8.2"
mongodb-version: "7.0"
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"
mongodb-version: "5.0"
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"
Expand All @@ -64,6 +76,7 @@ jobs:
# driver-version: "stable"
# dependencies: "highest"
# symfony-version: "stable"
# proxy: "lazy-ghost"

steps:
- name: "Checkout"
Expand Down Expand Up @@ -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:
Expand All @@ -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' }}"
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
30 changes: 30 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -128,6 +130,8 @@ class Configuration

private bool $useTransactionalFlush = false;

private bool $useLazyGhostObject = true;

/**
* Adds a namespace under a certain alias.
*/
Expand Down Expand Up @@ -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;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use the same method names as the ORM: isLazyGhostObjectEnabled and setLazyGhostObjectEnabled?

}

interface_exists(MappingDriver::class);
10 changes: 8 additions & 2 deletions lib/Doctrine/ODM/MongoDB/DocumentManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public function __serialize(): array
private readonly UnitOfWork $uow;

/** @var Configuration::AUTOGENERATE_* */
private $autoGenerate;
private int $autoGenerate;

/** @var array<class-string, Closure> */
private array $proxyFactories = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@
<ini name="error_reporting" value="-1"/>
<const name="DOCTRINE_MONGODB_SERVER" value="mongodb://localhost:27017" />
<const name="DOCTRINE_MONGODB_DATABASE" value="doctrine_odm_tests" />
<env name="USE_LAZY_GHOST_OBJECTS" value="1"/>
</php>
</phpunit>
14 changes: 10 additions & 4 deletions tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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());
Expand Down
20 changes: 10 additions & 10 deletions tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencePrimerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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());
Expand All @@ -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));

Expand All @@ -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());
Expand Down Expand Up @@ -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));
}
}
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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));
}

Expand All @@ -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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand All @@ -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');
Expand Down
Loading