Merge pull request #12335 from greg0ire/gh-12166

Avoid lazy object initialization when initializing read-only property
This commit is contained in:
Grégoire Paris
2026-01-09 06:28:15 +01:00
committed by GitHub
3 changed files with 83 additions and 1 deletions

View File

@@ -10,6 +10,8 @@ use ReflectionProperty;
use function sprintf;
use const PHP_VERSION_ID;
/** @internal */
class ReadonlyAccessor implements PropertyAccessor
{
@@ -26,7 +28,12 @@ class ReadonlyAccessor implements PropertyAccessor
public function setValue(object $object, mixed $value): void
{
if (! $this->reflectionProperty->isInitialized($object)) {
/* For lazy properties, skip the isInitialized() check
because it would trigger the initialization of the whole object. */
if (
PHP_VERSION_ID >= 80400 && $this->reflectionProperty->isLazy($object)
|| ! $this->reflectionProperty->isInitialized($object)
) {
$this->parent->setValue($object, $value);
return;

View File

@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional\Ticket\GH12166;
use Doctrine\Tests\OrmFunctionalTestCase;
class GH12166Test extends OrmFunctionalTestCase
{
public function testProxyWithReadonlyIdIsNotInitializedImmediately(): void
{
$this->createSchemaForModels(LazyEntityWithReadonlyId::class);
$this->_em->persist(new LazyEntityWithReadonlyId(123, 'Test Name'));
$this->_em->flush();
$this->_em->clear();
$proxy = $this->_em->getReference(LazyEntityWithReadonlyId::class, 123);
$reflClass = $this->_em->getClassMetadata(LazyEntityWithReadonlyId::class)->reflClass;
self::assertTrue(
$this->isUninitializedObject($proxy),
'Proxy should remain uninitialized after creation',
);
$id = $proxy->getId();
self::assertSame(123, $id);
self::assertTrue(
$this->isUninitializedObject($proxy),
'Proxy should remain uninitialized after accessing ID',
);
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional\Ticket\GH12166;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\Table;
#[Entity]
#[Table(name: 'gh12166_lazy_entity')]
class LazyEntityWithReadonlyId
{
#[Column(type: 'integer')]
#[Id]
private readonly int $id;
#[Column(type: 'string')]
private string $name;
public function __construct(int $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
public function getId(): int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
}