From 4c1b320aa19506d98b3dd98715d1cf9d3232db56 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 30 Aug 2024 10:18:34 +0200 Subject: [PATCH] [VarExporter] Allow reinitializing lazy objects with a new initializer --- .../Component/VarExporter/CHANGELOG.md | 5 ++++ .../VarExporter/Internal/LazyObjectState.php | 4 +-- .../Component/VarExporter/LazyGhostTrait.php | 11 ++++++++ .../Component/VarExporter/LazyProxyTrait.php | 7 +++++ .../Tests/Fixtures/LazyGhost/RegularClass.php | 20 ++++++++++++++ .../VarExporter/Tests/LazyGhostTraitTest.php | 11 ++++++++ .../VarExporter/Tests/LazyProxyTraitTest.php | 26 +++++++++++++++++++ 7 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/RegularClass.php diff --git a/src/Symfony/Component/VarExporter/CHANGELOG.md b/src/Symfony/Component/VarExporter/CHANGELOG.md index fdca002cb05df..74333ea7bbb02 100644 --- a/src/Symfony/Component/VarExporter/CHANGELOG.md +++ b/src/Symfony/Component/VarExporter/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.2 +--- + + * Allow reinitializing lazy objects with a new initializer + 6.4 --- diff --git a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php index 5fc398e058950..30fbff18028ca 100644 --- a/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php +++ b/src/Symfony/Component/VarExporter/Internal/LazyObjectState.php @@ -38,8 +38,8 @@ class LazyObjectState * @param array $skippedProperties */ public function __construct( - public readonly \Closure $initializer, - public readonly array $skippedProperties = [], + public \Closure $initializer, + public array $skippedProperties = [], ) { } diff --git a/src/Symfony/Component/VarExporter/LazyGhostTrait.php b/src/Symfony/Component/VarExporter/LazyGhostTrait.php index fa8279b52bde8..f32f1b6d0ce48 100644 --- a/src/Symfony/Component/VarExporter/LazyGhostTrait.php +++ b/src/Symfony/Component/VarExporter/LazyGhostTrait.php @@ -51,6 +51,17 @@ public static function createLazyGhost(\Closure $initializer, ?array $skippedPro $instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor(); } + if (isset($instance->lazyObjectState)) { + $instance->lazyObjectState->initializer = $initializer; + $instance->lazyObjectState->skippedProperties = $skippedProperties ??= []; + + if (LazyObjectState::STATUS_UNINITIALIZED_FULL !== $instance->lazyObjectState->status) { + $instance->lazyObjectState->reset($instance); + } + + return $instance; + } + $instance->lazyObjectState = new LazyObjectState($initializer, $skippedProperties ??= []); foreach (Registry::$classResetters[$class] as $reset) { diff --git a/src/Symfony/Component/VarExporter/LazyProxyTrait.php b/src/Symfony/Component/VarExporter/LazyProxyTrait.php index f3ed52b6966e5..9fc719ffbaa2a 100644 --- a/src/Symfony/Component/VarExporter/LazyProxyTrait.php +++ b/src/Symfony/Component/VarExporter/LazyProxyTrait.php @@ -47,6 +47,13 @@ public static function createLazyProxy(\Closure $initializer, ?object $instance $instance ??= Registry::$classReflectors[$class]->newInstanceWithoutConstructor(); } + if (isset($instance->lazyObjectState)) { + $instance->lazyObjectState->initializer = $initializer; + unset($instance->lazyObjectState->realInstance); + + return $instance; + } + $instance->lazyObjectState = new LazyObjectState($initializer); foreach (Registry::$classResetters[$class] as $reset) { diff --git a/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/RegularClass.php b/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/RegularClass.php new file mode 100644 index 0000000000000..2ebb2f5848f77 --- /dev/null +++ b/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/RegularClass.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost; + +class RegularClass +{ + public function __construct( + public int $foo + ) { + } +} diff --git a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php index 0c0bccffaafa9..3c892cd7d98bb 100644 --- a/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php +++ b/src/Symfony/Component/VarExporter/Tests/LazyGhostTraitTest.php @@ -280,6 +280,17 @@ public function testNormalization() $this->assertSame(['property' => 'property', 'method' => 'method'], $output); } + public function testReinitLazyGhost() + { + $object = TestClass::createLazyGhost(function ($p) { $p->public = 2; }); + + $this->assertSame(2, $object->public); + + TestClass::createLazyGhost(function ($p) { $p->public = 3; }, null, $object); + + $this->assertSame(3, $object->public); + } + /** * @template T * diff --git a/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php b/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php index 26cfa77f4a9b8..5e719e0343f5d 100644 --- a/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php +++ b/src/Symfony/Component/VarExporter/Tests/LazyProxyTraitTest.php @@ -18,6 +18,7 @@ use Symfony\Component\VarExporter\Exception\LogicException; use Symfony\Component\VarExporter\LazyProxyTrait; use Symfony\Component\VarExporter\ProxyHelper; +use Symfony\Component\VarExporter\Tests\Fixtures\LazyGhost\RegularClass; use Symfony\Component\VarExporter\Tests\Fixtures\LazyProxy\FinalPublicClass; use Symfony\Component\VarExporter\Tests\Fixtures\LazyProxy\ReadOnlyClass; use Symfony\Component\VarExporter\Tests\Fixtures\LazyProxy\StringMagicGetClass; @@ -295,6 +296,31 @@ public function testNormalization() $this->assertSame(['property' => 'property', 'method' => 'method'], $output); } + public function testReinitRegularLazyProxy() + { + $object = $this->createLazyProxy(RegularClass::class, fn () => new RegularClass(123)); + + $this->assertSame(123, $object->foo); + + $object::createLazyProxy(fn () => new RegularClass(234), $object); + + $this->assertSame(234, $object->foo); + } + + /** + * @requires PHP 8.3 + */ + public function testReinitReadonlyLazyProxy() + { + $object = $this->createLazyProxy(ReadOnlyClass::class, fn () => new ReadOnlyClass(123)); + + $this->assertSame(123, $object->foo); + + $object::createLazyProxy(fn () => new ReadOnlyClass(234), $object); + + $this->assertSame(234, $object->foo); + } + /** * @template T *