Compare commits

...

8 Commits
3.4.3 ... 3.4.4

Author SHA1 Message Date
Grégoire Paris
e67fa5388b Merge pull request #12043 from beberlei/Bugfix-DisableNativeLazyLogicException
Only throw PHP 8.4 requirement exception when enabling native lazy objects.
2025-06-30 23:43:15 +02:00
Grégoire Paris
dddcc507ef Merge pull request #12039 from xabbuh/pr-12036
do not register the legacy proxy class name resolver with enabled native lazy ghost
2025-06-30 20:38:34 +02:00
Christian Flothmann
b41d9da88d do not register the legacy proxy class name resolver with enabled native lazy ghost 2025-06-30 19:14:11 +02:00
Benjamin Eberlei
c04bfb78b7 Only throw PHP 8.4 requirement exception when enabling native lazy objects. 2025-06-30 19:01:49 +02:00
Grégoire Paris
ee919d6231 Merge pull request #12030 from greg0ire/test-w-lazy-o
Rework tests and benchmarks to work with lazy objects
2025-06-27 18:13:24 +02:00
Grégoire Paris
04c390693a Merge pull request #12033 from greg0ire/remove-assert
Remove wrong assertion
2025-06-27 15:55:40 +02:00
Gregoire PARIS
8d9e2e7d4e Remove wrong assertion
When using native lazy objects, it is plain wrong.
2025-06-27 14:42:37 +02:00
Gregoire PARIS
ed543a205c Rework tests and benchmarks to work with lazy objects
These tests and benchmarks are still relevant with lazy objects.
I am not setting up an extra job to test phpbench without native lazy
objects. Instead, I'm bumping the PHP version to 8.4 so that native lazy
objects are in use.
2025-06-27 14:12:58 +02:00
7 changed files with 33 additions and 41 deletions

View File

@@ -3450,12 +3450,6 @@ parameters:
count: 1
path: src/UnitOfWork.php
-
message: '#^PHPDoc tag @phpstan\-assert\-if\-true for \$obj contains generic interface Doctrine\\ORM\\Proxy\\InternalProxy but does not specify its types\: T$#'
identifier: missingType.generics
count: 1
path: src/UnitOfWork.php
-
message: '#^Parameter \#2 \$assoc of method Doctrine\\ORM\\PersistentCollection\<\(int\|string\),mixed\>\:\:setOwner\(\) expects Doctrine\\ORM\\Mapping\\AssociationMapping&Doctrine\\ORM\\Mapping\\ToManyAssociationMapping, Doctrine\\ORM\\Mapping\\ManyToManyInverseSideMapping\|Doctrine\\ORM\\Mapping\\ManyToManyOwningSideMapping\|Doctrine\\ORM\\Mapping\\ManyToOneAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToManyAssociationMapping\|Doctrine\\ORM\\Mapping\\OneToOneInverseSideMapping\|Doctrine\\ORM\\Mapping\\OneToOneOwningSideMapping given\.$#'
identifier: argument.type
@@ -3498,6 +3492,12 @@ parameters:
count: 1
path: src/UnitOfWork.php
-
message: '#^Unable to resolve the template type T in call to method static method Symfony\\Component\\VarExporter\\Hydrator\:\:hydrate\(\)$#'
identifier: argument.templateType
count: 1
path: src/UnitOfWork.php
-
message: '#^Access to an undefined property Doctrine\\Persistence\\Mapping\\ClassMetadata\:\:\$name\.$#'
identifier: property.notFound

View File

@@ -602,7 +602,7 @@ class Configuration extends \Doctrine\DBAL\Configuration
public function enableNativeLazyObjects(bool $nativeLazyObjects): void
{
if (PHP_VERSION_ID < 80400) {
if (PHP_VERSION_ID < 80400 && $nativeLazyObjects) {
throw new LogicException('Lazy loading proxies require PHP 8.4 or higher.');
}

View File

@@ -66,7 +66,9 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
public function setEntityManager(EntityManagerInterface $em): void
{
parent::setProxyClassNameResolver(new DefaultProxyClassNameResolver());
if (! $em->getConfiguration()->isNativeLazyObjectsEnabled()) {
parent::setProxyClassNameResolver(new DefaultProxyClassNameResolver());
}
$this->em = $em;
}

View File

@@ -3050,11 +3050,7 @@ class UnitOfWork implements PropertyChangedListener
}
}
/**
* Tests if a value is an uninitialized entity.
*
* @phpstan-assert-if-true InternalProxy $obj
*/
/** Tests if a value is an uninitialized entity. */
public function isUninitializedObject(mixed $obj): bool
{
if ($this->em->getConfiguration()->isNativeLazyObjectsEnabled() && ! ($obj instanceof Collection) && is_object($obj)) {

View File

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Doctrine\Performance\LazyLoading;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Proxy\InternalProxy as Proxy;
use Doctrine\Performance\EntityManagerFactory;
use Doctrine\Performance\Mock\NonProxyLoadingEntityManager;
@@ -25,9 +26,11 @@ final class ProxyInitializationTimeBench
/** @var Proxy[] */
private array|null $initializedEmployees = null;
private EntityManager $em;
public function init(): void
{
$proxyFactory = (new NonProxyLoadingEntityManager(EntityManagerFactory::getEntityManager([])))
$proxyFactory = (new NonProxyLoadingEntityManager($this->em = EntityManagerFactory::getEntityManager([])))
->getProxyFactory();
for ($i = 0; $i < 10000; ++$i) {
@@ -36,36 +39,36 @@ final class ProxyInitializationTimeBench
$this->initializedUsers[$i] = $proxyFactory->getProxy(CmsUser::class, ['id' => $i]);
$this->initializedEmployees[$i] = $proxyFactory->getProxy(CmsEmployee::class, ['id' => $i]);
$this->initializedUsers[$i]->__load();
$this->initializedEmployees[$i]->__load();
$this->em->getUnitOfWork()->initializeObject($this->initializedUsers[$i]);
$this->em->getUnitOfWork()->initializeObject($this->initializedEmployees[$i]);
}
}
public function benchCmsUserInitialization(): void
{
foreach ($this->cmsUsers as $proxy) {
$proxy->__load();
$this->em->getUnitOfWork()->initializeObject($proxy);
}
}
public function benchCmsEmployeeInitialization(): void
{
foreach ($this->cmsEmployees as $proxy) {
$proxy->__load();
$this->em->getUnitOfWork()->initializeObject($proxy);
}
}
public function benchInitializationOfAlreadyInitializedCmsUsers(): void
{
foreach ($this->initializedUsers as $proxy) {
$proxy->__load();
$this->em->getUnitOfWork()->initializeObject($proxy);
}
}
public function benchInitializationOfAlreadyInitializedCmsEmployees(): void
{
foreach ($this->initializedEmployees as $proxy) {
$proxy->__load();
$this->em->getUnitOfWork()->initializeObject($proxy);
}
}
}

View File

@@ -14,8 +14,6 @@ use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\OrmFunctionalTestCase;
use Doctrine\Tests\Proxies\__CG__\Doctrine\Tests\Models\CMS\CmsUser as CmsUserProxy;
use function assert;
/**
* Test that Doctrine ORM correctly works with proxy instances exactly like with ordinary Entities
*
@@ -34,10 +32,6 @@ class ProxiesLikeEntitiesTest extends OrmFunctionalTestCase
{
parent::setUp();
if ($this->_em->getConfiguration()->isNativeLazyObjectsEnabled()) {
self::markTestSkipped('This test is not applicable when lazy proxy is enabled.');
}
$this->createSchemaForModels(
CmsUser::class,
CmsTag::class,
@@ -83,8 +77,7 @@ class ProxiesLikeEntitiesTest extends OrmFunctionalTestCase
{
$userId = $this->user->getId();
$uninitializedProxy = $this->_em->getReference(CmsUser::class, $userId);
assert($uninitializedProxy instanceof CmsUserProxy);
self::assertInstanceOf(CmsUserProxy::class, $uninitializedProxy);
$this->assertTrue($this->isUninitializedObject($uninitializedProxy));
$this->_em->persist($uninitializedProxy);
$this->_em->flush();
@@ -116,6 +109,10 @@ class ProxiesLikeEntitiesTest extends OrmFunctionalTestCase
*/
public function testFindWithProxyName(): void
{
if ($this->_em->getConfiguration()->isNativeLazyObjectsEnabled()) {
self::markTestSkipped('There is no such thing as a proxy class name when native lazy objects are enabled.');
}
$result = $this->_em->find(CmsUserProxy::class, $this->user->getId());
self::assertSame($this->user->getId(), $result->getId());
$this->_em->clear();

View File

@@ -14,8 +14,7 @@ use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\UnitOfWork;
use Doctrine\Tests\OrmFunctionalTestCase;
use PHPUnit\Framework\Attributes\Group;
use function get_class;
use ReflectionClass;
#[Group('GH10808')]
class GH10808Test extends OrmFunctionalTestCase
@@ -32,10 +31,6 @@ class GH10808Test extends OrmFunctionalTestCase
public function testDQLDeferredEagerLoad(): void
{
if ($this->_em->getConfiguration()->isNativeLazyObjectsEnabled()) {
self::markTestSkipped('Test requires lazy loading to be disabled');
}
$appointment = new GH10808Appointment();
$this->_em->persist($appointment);
@@ -55,14 +50,13 @@ class GH10808Test extends OrmFunctionalTestCase
$eagerLoadResult = $query->setHint(UnitOfWork::HINT_DEFEREAGERLOAD, false)->getSingleResult();
self::assertNotEquals(
GH10808AppointmentChild::class,
get_class($deferredLoadResult->child),
'$deferredLoadResult->child should be a proxy',
$reflector = new ReflectionClass(GH10808AppointmentChild::class);
self::assertFalse(
$this->isUninitializedObject($deferredLoadResult->child),
'$deferredLoadResult->child should be a native lazy object',
);
self::assertEquals(
GH10808AppointmentChild::class,
get_class($eagerLoadResult->child),
self::assertFalse(
$this->isUninitializedObject($deferredLoadResult->child),
'$eagerLoadResult->child should not be a proxy',
);
}