1818use SergiX44 \Hydrator \Annotation \Alias ;
1919use SergiX44 \Hydrator \Annotation \ArrayType ;
2020use SergiX44 \Hydrator \Annotation \ConcreteResolver ;
21+ use SergiX44 \Hydrator \Annotation \SkipConstructor ;
2122use SergiX44 \Hydrator \Annotation \UnionResolver ;
2223use SergiX44 \Hydrator \Exception \InvalidObjectException ;
2324
@@ -57,8 +58,6 @@ public function __construct(?ContainerInterface $container = null)
5758 * @param class-string<T>|T $object
5859 * @param array|object $data
5960 *
60- * @throws Exception\UntypedPropertyException
61- * If one of the object properties isn't typed.
6261 * @throws Exception\UnsupportedPropertyTypeException
6362 * If one of the object properties contains an unsupported type.
6463 * @throws Exception\MissingRequiredValueException
@@ -69,6 +68,8 @@ public function __construct(?ContainerInterface $container = null)
6968 * If the object cannot be hydrated.
7069 * @throws InvalidArgumentException
7170 * If the data isn't valid.
71+ * @throws Exception\UntypedPropertyException
72+ * If one of the object properties isn't typed.
7273 *
7374 * @return T
7475 *
@@ -109,7 +110,11 @@ public function hydrate(string|object $object, array|object $data): object
109110 }
110111
111112 if ($ propertyType instanceof ReflectionUnionType) {
112- $ resolver = $ this ->getAttributeInstance ($ property , UnionResolver::class, ReflectionAttribute::IS_INSTANCEOF );
113+ $ resolver = $ this ->getAttributeInstance (
114+ $ property ,
115+ UnionResolver::class,
116+ ReflectionAttribute::IS_INSTANCEOF
117+ );
113118 if (isset ($ resolver )) {
114119 $ propertyType = $ resolver ->resolve ($ propertyType , $ data [$ key ]);
115120 } else {
@@ -146,10 +151,10 @@ public function hydrate(string|object $object, array|object $data): object
146151 * @param string $json
147152 * @param ?int $flags
148153 *
149- * @throws Exception\HydrationException
150- * If the object cannot be hydrated.
151154 * @throws InvalidArgumentException
152155 * If the JSON cannot be decoded.
156+ * @throws Exception\HydrationException
157+ * If the object cannot be hydrated.
153158 *
154159 * @return T
155160 *
@@ -182,17 +187,21 @@ public function hydrateWithJson(string|object $object, string $json, ?int $flags
182187 */
183188 public function getConcreteResolverFor (string |object $ object ): ?ConcreteResolver
184189 {
185- return $ this ->getAttributeInstance (new ReflectionClass ($ object ), ConcreteResolver::class, ReflectionAttribute::IS_INSTANCEOF );
190+ return $ this ->getAttributeInstance (
191+ new ReflectionClass ($ object ),
192+ ConcreteResolver::class,
193+ ReflectionAttribute::IS_INSTANCEOF
194+ );
186195 }
187196
188197 /**
189198 * Initializes the given object.
190199 *
191200 * @param class-string<T>|T $object
192201 *
193- * @throws InvalidArgumentException
194202 * @throws ContainerExceptionInterface
195203 * If the object cannot be initialized.
204+ * @throws InvalidArgumentException
196205 *
197206 * @return T
198207 *
@@ -214,7 +223,11 @@ private function initializeObject(string|object $object, array|object $data): ob
214223 $ reflectionClass = new ReflectionClass ($ object );
215224
216225 if ($ reflectionClass ->isAbstract ()) {
217- $ attribute = $ this ->getAttributeInstance ($ reflectionClass , ConcreteResolver::class, ReflectionAttribute::IS_INSTANCEOF );
226+ $ attribute = $ this ->getAttributeInstance (
227+ $ reflectionClass ,
228+ ConcreteResolver::class,
229+ ReflectionAttribute::IS_INSTANCEOF
230+ );
218231
219232 if ($ attribute === null ) {
220233 throw new InvalidObjectException (sprintf (
@@ -227,6 +240,11 @@ private function initializeObject(string|object $object, array|object $data): ob
227240 }
228241
229242 // if we have a container, get the instance through it
243+ $ skipConstructor = $ this ->getAttributeInstance ($ reflectionClass , SkipConstructor::class);
244+ if ($ skipConstructor !== null ) {
245+ return $ reflectionClass ->newInstanceWithoutConstructor ();
246+ }
247+
230248 if ($ this ->container !== null ) {
231249 return $ this ->container ->get ($ object );
232250 }
@@ -254,8 +272,11 @@ private function initializeObject(string|object $object, array|object $data): ob
254272 *
255273 * @return T|null
256274 */
257- private function getAttributeInstance (ReflectionProperty |ReflectionClass $ target , string $ class , int $ criteria = 0 ): mixed
258- {
275+ private function getAttributeInstance (
276+ ReflectionProperty |ReflectionClass $ target ,
277+ string $ class ,
278+ int $ criteria = 0
279+ ): mixed {
259280 $ attributes = $ target ->getAttributes ($ class , $ criteria );
260281 if (isset ($ attributes [0 ])) {
261282 return $ attributes [0 ]->newInstance ();
@@ -273,10 +294,10 @@ private function getAttributeInstance(ReflectionProperty|ReflectionClass $target
273294 * @param ReflectionNamedType $type
274295 * @param mixed $value
275296 *
276- * @throws Exception\InvalidValueException
277- * If the given value isn't valid.
278297 * @throws Exception\UnsupportedPropertyTypeException
279298 * If the given property contains an unsupported type.
299+ * @throws Exception\InvalidValueException
300+ * If the given value isn't valid.
280301 *
281302 * @return void
282303 */
@@ -291,7 +312,12 @@ private function hydrateProperty(
291312
292313 match (true ) {
293314 // an empty string for a non-string type is always processes as null
294- '' === $ value && 'string ' !== $ propertyType , null === $ value => $ this ->propertyNull ($ object , $ class , $ property , $ type ),
315+ '' === $ value && 'string ' !== $ propertyType , null === $ value => $ this ->propertyNull (
316+ $ object ,
317+ $ class ,
318+ $ property ,
319+ $ type
320+ ),
295321
296322 'bool ' === $ propertyType => $ this ->propertyBool ($ object , $ class , $ property , $ type , $ value ),
297323
@@ -305,11 +331,26 @@ private function hydrateProperty(
305331
306332 'object ' === $ propertyType => $ this ->propertyObject ($ object , $ class , $ property , $ type , $ value ),
307333
308- DateTime::class === $ propertyType , DateTimeImmutable::class === $ propertyType => $ this ->propertyDateTime ($ object , $ class , $ property , $ type , $ value ),
309-
310- DateInterval::class === $ propertyType => $ this ->propertyDateInterval ($ object , $ class , $ property , $ type , $ value ),
311-
312- PHP_VERSION_ID >= 80100 && is_subclass_of ($ propertyType , BackedEnum::class) => $ this ->propertyBackedEnum ($ object , $ class , $ property , $ type , $ value ),
334+ DateTime::class === $ propertyType , DateTimeImmutable::class === $ propertyType => $ this ->propertyDateTime (
335+ $ object ,
336+ $ class ,
337+ $ property ,
338+ $ type ,
339+ $ value
340+ ),
341+
342+ DateInterval::class === $ propertyType => $ this ->propertyDateInterval (
343+ $ object ,
344+ $ class ,
345+ $ property ,
346+ $ type ,
347+ $ value
348+ ),
349+
350+ PHP_VERSION_ID >= 80100 && is_subclass_of (
351+ $ propertyType ,
352+ BackedEnum::class
353+ ) => $ this ->propertyBackedEnum ($ object , $ class , $ property , $ type , $ value ),
313354
314355 class_exists ($ propertyType ) => $ this ->propertyFromInstance ($ object , $ class , $ property , $ type , $ value ),
315356
@@ -562,7 +603,11 @@ private function hydrateObjectsInArray(array $array, ArrayType $arrayType, int $
562603 }
563604
564605 return array_map (function ($ object ) use ($ arrayType ) {
565- $ newInstance = $ this ->container ?->get($ arrayType ->class ) ?? $ arrayType ->getInstance ();
606+ if (is_subclass_of ($ arrayType ->class , BackedEnum::class)) {
607+ return $ arrayType ->class ::tryFrom ($ object );
608+ }
609+
610+ $ newInstance = $ this ->initializeObject ($ arrayType ->class , []);
566611
567612 return $ this ->hydrate ($ newInstance , $ object );
568613 }, $ array );
0 commit comments