15
15
use Symfony \Component \PropertyAccess \Exception \NoSuchPropertyException ;
16
16
use Symfony \Component \PropertyInfo \PropertyTypeExtractorInterface ;
17
17
use Symfony \Component \PropertyInfo \Type ;
18
+ use Symfony \Component \Serializer \Context \ObjectChildContextTrait ;
18
19
use Symfony \Component \Serializer \Encoder \CsvEncoder ;
19
20
use Symfony \Component \Serializer \Encoder \JsonEncoder ;
20
21
use Symfony \Component \Serializer \Encoder \XmlEncoder ;
21
22
use Symfony \Component \Serializer \Exception \ExtraAttributesException ;
22
23
use Symfony \Component \Serializer \Exception \LogicException ;
24
+ use Symfony \Component \Serializer \Exception \MissingConstructorArgumentsException ;
23
25
use Symfony \Component \Serializer \Exception \NotNormalizableValueException ;
24
- use Symfony \Component \Serializer \Exception \RuntimeException ;
26
+ use Symfony \Component \Serializer \Instantiator \Instantiator ;
27
+ use Symfony \Component \Serializer \Instantiator \InstantiatorInterface ;
25
28
use Symfony \Component \Serializer \Mapping \AttributeMetadataInterface ;
26
29
use Symfony \Component \Serializer \Mapping \ClassDiscriminatorFromClassMetadata ;
27
30
use Symfony \Component \Serializer \Mapping \ClassDiscriminatorResolverInterface ;
33
36
*
34
37
* @author Kévin Dunglas <[email protected] >
35
38
*/
36
- abstract class AbstractObjectNormalizer extends AbstractNormalizer
39
+ abstract class AbstractObjectNormalizer extends AbstractNormalizer implements DenormalizerAwareInterface
37
40
{
41
+ use DenormalizerAwareTrait;
42
+ use ObjectChildContextTrait;
43
+
38
44
/**
39
45
* Set to true to respect the max depth metadata on fields.
40
46
*/
@@ -93,6 +99,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
93
99
public const PRESERVE_EMPTY_OBJECTS = 'preserve_empty_objects ' ;
94
100
95
101
private $ propertyTypeExtractor ;
102
+ private $ instantiator ;
96
103
private $ typesCache = [];
97
104
private $ attributesCache = [];
98
105
@@ -103,7 +110,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
103
110
*/
104
111
protected $ classDiscriminatorResolver ;
105
112
106
- public function __construct (ClassMetadataFactoryInterface $ classMetadataFactory = null , NameConverterInterface $ nameConverter = null , PropertyTypeExtractorInterface $ propertyTypeExtractor = null , ClassDiscriminatorResolverInterface $ classDiscriminatorResolver = null , callable $ objectClassResolver = null , array $ defaultContext = [])
113
+ public function __construct (ClassMetadataFactoryInterface $ classMetadataFactory = null , NameConverterInterface $ nameConverter = null , PropertyTypeExtractorInterface $ propertyTypeExtractor = null , ClassDiscriminatorResolverInterface $ classDiscriminatorResolver = null , callable $ objectClassResolver = null , array $ defaultContext = [], InstantiatorInterface $ instantiator = null )
107
114
{
108
115
parent ::__construct ($ classMetadataFactory , $ nameConverter , $ defaultContext );
109
116
@@ -120,6 +127,24 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory
120
127
}
121
128
$ this ->classDiscriminatorResolver = $ classDiscriminatorResolver ;
122
129
$ this ->objectClassResolver = $ objectClassResolver ;
130
+
131
+ if (null === $ instantiator ) {
132
+ $ instantiator = new Instantiator ($ classMetadataFactory , $ this ->classDiscriminatorResolver , $ propertyTypeExtractor , null , $ nameConverter , null );
133
+
134
+ if ($ this ->denormalizer instanceof DenormalizerInterface) {
135
+ $ instantiator ->setDenormalizer ($ this ->denormalizer );
136
+ }
137
+ }
138
+ $ this ->instantiator = $ instantiator ;
139
+ }
140
+
141
+ public function setDenormalizer (DenormalizerInterface $ denormalizer )
142
+ {
143
+ $ this ->denormalizer = $ denormalizer ;
144
+
145
+ // Because we need a denormalizer in the Instantiator and we create it in the construct method, it won't get it.
146
+ // So we are obliged to overwrite this method in order to give the denormalizer to the Instantiator.
147
+ $ this ->instantiator ->setDenormalizer ($ denormalizer );
123
148
}
124
149
125
150
/**
@@ -210,29 +235,6 @@ public function normalize($object, string $format = null, array $context = [])
210
235
return $ data ;
211
236
}
212
237
213
- /**
214
- * {@inheritdoc}
215
- */
216
- protected function instantiateObject (array &$ data , string $ class , array &$ context , \ReflectionClass $ reflectionClass , $ allowedAttributes , string $ format = null )
217
- {
218
- if ($ this ->classDiscriminatorResolver && $ mapping = $ this ->classDiscriminatorResolver ->getMappingForClass ($ class )) {
219
- if (!isset ($ data [$ mapping ->getTypeProperty ()])) {
220
- throw new RuntimeException (sprintf ('Type property "%s" not found for the abstract object "%s". ' , $ mapping ->getTypeProperty (), $ class ));
221
- }
222
-
223
- $ type = $ data [$ mapping ->getTypeProperty ()];
224
- if (null === ($ mappedClass = $ mapping ->getClassForType ($ type ))) {
225
- throw new RuntimeException (sprintf ('The type "%s" has no mapped class for the abstract object "%s". ' , $ type , $ class ));
226
- }
227
-
228
- if ($ mappedClass !== $ class ) {
229
- return $ this ->instantiateObject ($ data , $ mappedClass , $ context , new \ReflectionClass ($ mappedClass ), $ allowedAttributes , $ format );
230
- }
231
- }
232
-
233
- return parent ::instantiateObject ($ data , $ class , $ context , $ reflectionClass , $ allowedAttributes , $ format );
234
- }
235
-
236
238
/**
237
239
* Gets and caches attributes for the given object, format and context.
238
240
*
@@ -307,8 +309,14 @@ public function denormalize($data, string $type, string $format = null, array $c
307
309
$ normalizedData = $ this ->prepareForDenormalization ($ data );
308
310
$ extraAttributes = [];
309
311
310
- $ reflectionClass = new \ReflectionClass ($ type );
311
- $ object = $ this ->instantiateObject ($ normalizedData , $ type , $ context , $ reflectionClass , $ allowedAttributes , $ format );
312
+ $ instantiatorResult = $ this ->instantiator ->instantiate ($ type , $ normalizedData , $ context , $ format );
313
+ if ($ instantiatorResult ->hasFailed ()) {
314
+ throw new MissingConstructorArgumentsException ($ instantiatorResult ->getError ());
315
+ }
316
+ $ object = $ instantiatorResult ->getObject ();
317
+ $ normalizedData = $ instantiatorResult ->getUnusedData ();
318
+ $ context = $ instantiatorResult ->getUnusedContext ();
319
+
312
320
$ resolvedClass = $ this ->objectClassResolver ? ($ this ->objectClassResolver )($ object ) : \get_class ($ object );
313
321
314
322
foreach ($ normalizedData as $ attribute => $ value ) {
@@ -595,23 +603,6 @@ private function isMaxDepthReached(array $attributesMetadata, string $class, str
595
603
return false ;
596
604
}
597
605
598
- /**
599
- * Overwritten to update the cache key for the child.
600
- *
601
- * We must not mix up the attribute cache between parent and children.
602
- *
603
- * {@inheritdoc}
604
- *
605
- * @internal
606
- */
607
- protected function createChildContext (array $ parentContext , string $ attribute , ?string $ format ): array
608
- {
609
- $ context = parent ::createChildContext ($ parentContext , $ attribute , $ format );
610
- $ context ['cache_key ' ] = $ this ->getCacheKey ($ format , $ context );
611
-
612
- return $ context ;
613
- }
614
-
615
606
/**
616
607
* Builds the cache key for the attributes cache.
617
608
*
0 commit comments