1616use Patchlevel \Hydrator \Metadata \ClassNotFound ;
1717use Patchlevel \Hydrator \Metadata \MetadataFactory ;
1818use Patchlevel \Hydrator \Normalizer \HydratorAwareNormalizer ;
19+ use ReflectionClass ;
1920use ReflectionParameter ;
2021use Symfony \Component \EventDispatcher \EventDispatcher ;
2122use Symfony \Component \EventDispatcher \EventDispatcherInterface ;
2728use function is_object ;
2829use function spl_object_id ;
2930
31+ use const PHP_VERSION_ID ;
32+
3033final class MetadataHydrator implements Hydrator
3134{
3235 /** @var array<int, class-string> */
@@ -36,6 +39,7 @@ public function __construct(
3639 private readonly MetadataFactory $ metadataFactory = new AttributeMetadataFactory (),
3740 PayloadCryptographer |null $ cryptographer = null ,
3841 private EventDispatcherInterface |null $ eventDispatcher = null ,
42+ private readonly bool $ defaultLazy = false ,
3943 ) {
4044 if (!$ cryptographer ) {
4145 return ;
@@ -66,6 +70,33 @@ public function hydrate(string $class, array $data): object
6670 throw new ClassNotSupported ($ class , $ e );
6771 }
6872
73+ if (PHP_VERSION_ID < 80400 ) {
74+ return $ this ->doHydrate ($ metadata , $ data );
75+ }
76+
77+ $ lazy = $ metadata ->lazy () ?? $ this ->defaultLazy ;
78+
79+ if (!$ lazy ) {
80+ return $ this ->doHydrate ($ metadata , $ data );
81+ }
82+
83+ return (new ReflectionClass ($ class ))->newLazyProxy (
84+ function () use ($ metadata , $ data ): object {
85+ return $ this ->doHydrate ($ metadata , $ data );
86+ },
87+ );
88+ }
89+
90+ /**
91+ * @param ClassMetadata<T> $metadata
92+ * @param array<string, mixed> $data
93+ *
94+ * @return T
95+ *
96+ * @template T of object
97+ */
98+ private function doHydrate (ClassMetadata $ metadata , array $ data ): object
99+ {
69100 if ($ this ->eventDispatcher ) {
70101 $ data = $ this ->eventDispatcher ->dispatch (new PreHydrate ($ data , $ metadata ))->data ;
71102 }
@@ -110,7 +141,7 @@ public function hydrate(string $class, array $data): object
110141 $ value = $ normalizer ->denormalize ($ value );
111142 } catch (Throwable $ e ) {
112143 throw new DenormalizationFailure (
113- $ class ,
144+ $ metadata -> className () ,
114145 $ propertyMetadata ->propertyName (),
115146 $ normalizer ::class,
116147 $ e ,
@@ -122,7 +153,7 @@ public function hydrate(string $class, array $data): object
122153 $ propertyMetadata ->setValue ($ object , $ value );
123154 } catch (TypeError $ e ) {
124155 throw new TypeMismatch (
125- $ class ,
156+ $ metadata -> className () ,
126157 $ propertyMetadata ->propertyName (),
127158 $ e ,
128159 );
@@ -234,6 +265,7 @@ private function promotedConstructorParametersWithDefaultValue(ClassMetadata $me
234265 public static function create (
235266 iterable $ guessers = [],
236267 EventDispatcherInterface |null $ eventDispatcher = null ,
268+ bool $ defaultLazy = false ,
237269 ): self {
238270 $ guesser = new BuiltInGuesser ();
239271
@@ -250,6 +282,7 @@ public static function create(
250282 ),
251283 null ,
252284 $ eventDispatcher ,
285+ $ defaultLazy ,
253286 );
254287 }
255288}
0 commit comments