diff --git a/src/Symfony/Component/ObjectMapper/ObjectMapper.php b/src/Symfony/Component/ObjectMapper/ObjectMapper.php index 28eda13513119..87f0a821a71d9 100644 --- a/src/Symfony/Component/ObjectMapper/ObjectMapper.php +++ b/src/Symfony/Component/ObjectMapper/ObjectMapper.php @@ -20,6 +20,7 @@ use Symfony\Component\ObjectMapper\Metadata\ReflectionObjectMapperMetadataFactory; use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException as PropertyAccessorNoSuchPropertyException; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Component\VarExporter\LazyObjectInterface; /** * Object to object mapper. @@ -315,6 +316,12 @@ private function getSourceReflectionClass(object $source, \ReflectionClass $targ throw new MappingException($e->getMessage(), $e->getCode(), $e); } + if ($source instanceof LazyObjectInterface) { + $source->initializeLazyObject(); + } elseif (\PHP_VERSION_ID >= 80400 && $refl->isUninitializedLazyObject($source)) { + $refl->initializeLazyObject($source); + } + if ($metadata) { return $refl; } diff --git a/src/Symfony/Component/ObjectMapper/Tests/Fixtures/LazyFoo.php b/src/Symfony/Component/ObjectMapper/Tests/Fixtures/LazyFoo.php new file mode 100644 index 0000000000000..e8bb45e26d2ca --- /dev/null +++ b/src/Symfony/Component/ObjectMapper/Tests/Fixtures/LazyFoo.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ObjectMapper\Tests\Fixtures; + +use Symfony\Component\VarExporter\LazyGhostTrait; +use Symfony\Component\VarExporter\LazyObjectInterface; + +class LazyFoo extends \stdClass implements LazyObjectInterface +{ + use LazyGhostTrait; + + public string $name = 'foo'; +} diff --git a/src/Symfony/Component/ObjectMapper/Tests/Fixtures/MyProxy.php b/src/Symfony/Component/ObjectMapper/Tests/Fixtures/MyProxy.php new file mode 100644 index 0000000000000..50d64a80ff4bc --- /dev/null +++ b/src/Symfony/Component/ObjectMapper/Tests/Fixtures/MyProxy.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ObjectMapper\Tests\Fixtures; + +class MyProxy +{ + public string $name; +} diff --git a/src/Symfony/Component/ObjectMapper/Tests/ObjectMapperTest.php b/src/Symfony/Component/ObjectMapper/Tests/ObjectMapperTest.php index 81c1d56b17f6b..8cb3b82c2baa8 100644 --- a/src/Symfony/Component/ObjectMapper/Tests/ObjectMapperTest.php +++ b/src/Symfony/Component/ObjectMapper/Tests/ObjectMapperTest.php @@ -41,6 +41,7 @@ use Symfony\Component\ObjectMapper\Tests\Fixtures\InstanceCallback\B as InstanceCallbackB; use Symfony\Component\ObjectMapper\Tests\Fixtures\InstanceCallbackWithArguments\A as InstanceCallbackWithArgumentsA; use Symfony\Component\ObjectMapper\Tests\Fixtures\InstanceCallbackWithArguments\B as InstanceCallbackWithArgumentsB; +use Symfony\Component\ObjectMapper\Tests\Fixtures\LazyFoo; use Symfony\Component\ObjectMapper\Tests\Fixtures\MapStruct\AToBMapper; use Symfony\Component\ObjectMapper\Tests\Fixtures\MapStruct\MapStructMapperMetadataFactory; use Symfony\Component\ObjectMapper\Tests\Fixtures\MapStruct\Source; @@ -52,6 +53,7 @@ use Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargetProperty\C as MultipleTargetPropertyC; use Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargets\A as MultipleTargetsA; use Symfony\Component\ObjectMapper\Tests\Fixtures\MultipleTargets\C as MultipleTargetsC; +use Symfony\Component\ObjectMapper\Tests\Fixtures\MyProxy; use Symfony\Component\ObjectMapper\Tests\Fixtures\PromotedConstructor\Source as PromotedConstructorSource; use Symfony\Component\ObjectMapper\Tests\Fixtures\PromotedConstructor\Target as PromotedConstructorTarget; use Symfony\Component\ObjectMapper\Tests\Fixtures\Recursion\AB; @@ -368,4 +370,36 @@ public static function objectMapperProvider(): iterable yield [new ObjectMapper()]; yield [new ObjectMapper(new ReflectionObjectMapperMetadataFactory(), PropertyAccess::createPropertyAccessor())]; } + + public function testMapInitializesLazyObject() + { + $lazy = new LazyFoo(); + $mapper = new ObjectMapper(); + $mapper->map($lazy, \stdClass::class); + $this->assertTrue($lazy->isLazyObjectInitialized()); + } + + /** + * @requires PHP 8.4 + */ + public function testMapInitializesNativePhp84LazyObject() + { + $initialized = false; + $initializer = function () use (&$initialized) { + $initialized = true; + + $p = new MyProxy(); + $p->name = 'test'; + + return $p; + }; + + $r = new \ReflectionClass(MyProxy::class); + $lazyObj = $r->newLazyProxy($initializer); + $this->assertFalse($initialized); + $mapper = new ObjectMapper(); + $d = $mapper->map($lazyObj, MyProxy::class); + $this->assertSame('test', $d->name); + $this->assertTrue($initialized); + } } diff --git a/src/Symfony/Component/ObjectMapper/composer.json b/src/Symfony/Component/ObjectMapper/composer.json index 6d1b445d92781..0c7fc098992c6 100644 --- a/src/Symfony/Component/ObjectMapper/composer.json +++ b/src/Symfony/Component/ObjectMapper/composer.json @@ -20,7 +20,8 @@ "psr/container": "^2.0" }, "require-dev": { - "symfony/property-access": "^7.2" + "symfony/property-access": "^7.2", + "symfony/var-exporter": "^7.2" }, "autoload": { "psr-4": {