comparison vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents c2387f117808
children af1871eacc83
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
10 */ 10 */
11 11
12 namespace Symfony\Component\Serializer\Normalizer; 12 namespace Symfony\Component\Serializer\Normalizer;
13 13
14 use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; 14 use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
15 use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
16 use Symfony\Component\PropertyInfo\Type;
15 use Symfony\Component\Serializer\Encoder\JsonEncoder; 17 use Symfony\Component\Serializer\Encoder\JsonEncoder;
16 use Symfony\Component\Serializer\Exception\ExtraAttributesException; 18 use Symfony\Component\Serializer\Exception\ExtraAttributesException;
17 use Symfony\Component\Serializer\Exception\LogicException; 19 use Symfony\Component\Serializer\Exception\LogicException;
18 use Symfony\Component\Serializer\Exception\NotNormalizableValueException; 20 use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
19 use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
20 use Symfony\Component\PropertyInfo\Type;
21 use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; 21 use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
22 use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; 22 use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
23 use Symfony\Component\Serializer\NameConverter\NameConverterInterface; 23 use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
24 24
25 /** 25 /**
32 const ENABLE_MAX_DEPTH = 'enable_max_depth'; 32 const ENABLE_MAX_DEPTH = 'enable_max_depth';
33 const DEPTH_KEY_PATTERN = 'depth_%s::%s'; 33 const DEPTH_KEY_PATTERN = 'depth_%s::%s';
34 const DISABLE_TYPE_ENFORCEMENT = 'disable_type_enforcement'; 34 const DISABLE_TYPE_ENFORCEMENT = 'disable_type_enforcement';
35 35
36 private $propertyTypeExtractor; 36 private $propertyTypeExtractor;
37 private $attributesCache = array(); 37 private $attributesCache = [];
38 private $cache = array(); 38 private $cache = [];
39 39
40 public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null) 40 public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null)
41 { 41 {
42 parent::__construct($classMetadataFactory, $nameConverter); 42 parent::__construct($classMetadataFactory, $nameConverter);
43 43
53 } 53 }
54 54
55 /** 55 /**
56 * {@inheritdoc} 56 * {@inheritdoc}
57 */ 57 */
58 public function normalize($object, $format = null, array $context = array()) 58 public function normalize($object, $format = null, array $context = [])
59 { 59 {
60 if (!isset($context['cache_key'])) { 60 if (!isset($context['cache_key'])) {
61 $context['cache_key'] = $this->getCacheKey($format, $context); 61 $context['cache_key'] = $this->getCacheKey($format, $context);
62 } 62 }
63 63
64 if ($this->isCircularReference($object, $context)) { 64 if ($this->isCircularReference($object, $context)) {
65 return $this->handleCircularReference($object); 65 return $this->handleCircularReference($object);
66 } 66 }
67 67
68 $data = array(); 68 $data = [];
69 $stack = array(); 69 $stack = [];
70 $attributes = $this->getAttributes($object, $format, $context); 70 $attributes = $this->getAttributes($object, $format, $context);
71 $class = get_class($object); 71 $class = \get_class($object);
72 $attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null; 72 $attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null;
73 73
74 foreach ($attributes as $attribute) { 74 foreach ($attributes as $attribute) {
75 if (null !== $attributesMetadata && $this->isMaxDepthReached($attributesMetadata, $class, $attribute, $context)) { 75 if (null !== $attributesMetadata && $this->isMaxDepthReached($attributesMetadata, $class, $attribute, $context)) {
76 continue; 76 continue;
77 } 77 }
78 78
79 $attributeValue = $this->getAttributeValue($object, $attribute, $format, $context); 79 $attributeValue = $this->getAttributeValue($object, $attribute, $format, $context);
80 80
81 if (isset($this->callbacks[$attribute])) { 81 if (isset($this->callbacks[$attribute])) {
82 $attributeValue = call_user_func($this->callbacks[$attribute], $attributeValue); 82 $attributeValue = \call_user_func($this->callbacks[$attribute], $attributeValue);
83 } 83 }
84 84
85 if (null !== $attributeValue && !is_scalar($attributeValue)) { 85 if (null !== $attributeValue && !is_scalar($attributeValue)) {
86 $stack[$attribute] = $attributeValue; 86 $stack[$attribute] = $attributeValue;
87 } 87 }
109 * 109 *
110 * @return string[] 110 * @return string[]
111 */ 111 */
112 protected function getAttributes($object, $format = null, array $context) 112 protected function getAttributes($object, $format = null, array $context)
113 { 113 {
114 $class = get_class($object); 114 $class = \get_class($object);
115 $key = $class.'-'.$context['cache_key']; 115 $key = $class.'-'.$context['cache_key'];
116 116
117 if (isset($this->attributesCache[$key])) { 117 if (isset($this->attributesCache[$key])) {
118 return $this->attributesCache[$key]; 118 return $this->attributesCache[$key];
119 } 119 }
146 * @param string|null $format 146 * @param string|null $format
147 * @param array $context 147 * @param array $context
148 * 148 *
149 * @return string[] 149 * @return string[]
150 */ 150 */
151 abstract protected function extractAttributes($object, $format = null, array $context = array()); 151 abstract protected function extractAttributes($object, $format = null, array $context = []);
152 152
153 /** 153 /**
154 * Gets the attribute value. 154 * Gets the attribute value.
155 * 155 *
156 * @param object $object 156 * @param object $object
158 * @param string|null $format 158 * @param string|null $format
159 * @param array $context 159 * @param array $context
160 * 160 *
161 * @return mixed 161 * @return mixed
162 */ 162 */
163 abstract protected function getAttributeValue($object, $attribute, $format = null, array $context = array()); 163 abstract protected function getAttributeValue($object, $attribute, $format = null, array $context = []);
164 164
165 /** 165 /**
166 * {@inheritdoc} 166 * {@inheritdoc}
167 */ 167 */
168 public function supportsDenormalization($data, $type, $format = null) 168 public function supportsDenormalization($data, $type, $format = null)
171 } 171 }
172 172
173 /** 173 /**
174 * {@inheritdoc} 174 * {@inheritdoc}
175 */ 175 */
176 public function denormalize($data, $class, $format = null, array $context = array()) 176 public function denormalize($data, $class, $format = null, array $context = [])
177 { 177 {
178 if (!isset($context['cache_key'])) { 178 if (!isset($context['cache_key'])) {
179 $context['cache_key'] = $this->getCacheKey($format, $context); 179 $context['cache_key'] = $this->getCacheKey($format, $context);
180 } 180 }
181 181
182 $allowedAttributes = $this->getAllowedAttributes($class, $context, true); 182 $allowedAttributes = $this->getAllowedAttributes($class, $context, true);
183 $normalizedData = $this->prepareForDenormalization($data); 183 $normalizedData = $this->prepareForDenormalization($data);
184 $extraAttributes = array(); 184 $extraAttributes = [];
185 185
186 $reflectionClass = new \ReflectionClass($class); 186 $reflectionClass = new \ReflectionClass($class);
187 $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format); 187 $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format);
188 188
189 foreach ($normalizedData as $attribute => $value) { 189 foreach ($normalizedData as $attribute => $value) {
190 if ($this->nameConverter) { 190 if ($this->nameConverter) {
191 $attribute = $this->nameConverter->denormalize($attribute); 191 $attribute = $this->nameConverter->denormalize($attribute);
192 } 192 }
193 193
194 if ((false !== $allowedAttributes && !in_array($attribute, $allowedAttributes)) || !$this->isAllowedAttribute($class, $attribute, $format, $context)) { 194 if ((false !== $allowedAttributes && !\in_array($attribute, $allowedAttributes)) || !$this->isAllowedAttribute($class, $attribute, $format, $context)) {
195 if (isset($context[self::ALLOW_EXTRA_ATTRIBUTES]) && !$context[self::ALLOW_EXTRA_ATTRIBUTES]) { 195 if (isset($context[self::ALLOW_EXTRA_ATTRIBUTES]) && !$context[self::ALLOW_EXTRA_ATTRIBUTES]) {
196 $extraAttributes[] = $attribute; 196 $extraAttributes[] = $attribute;
197 } 197 }
198 198
199 continue; 199 continue;
221 * @param string $attribute 221 * @param string $attribute
222 * @param mixed $value 222 * @param mixed $value
223 * @param string|null $format 223 * @param string|null $format
224 * @param array $context 224 * @param array $context
225 */ 225 */
226 abstract protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array()); 226 abstract protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = []);
227 227
228 /** 228 /**
229 * Validates the submitted data and denormalizes it. 229 * Validates the submitted data and denormalizes it.
230 * 230 *
231 * @param string $currentClass 231 * @param string $currentClass
243 { 243 {
244 if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($currentClass, $attribute)) { 244 if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($currentClass, $attribute)) {
245 return $data; 245 return $data;
246 } 246 }
247 247
248 $expectedTypes = array(); 248 $expectedTypes = [];
249 foreach ($types as $type) { 249 foreach ($types as $type) {
250 if (null === $data && $type->isNullable()) { 250 if (null === $data && $type->isNullable()) {
251 return; 251 return;
252 } 252 }
253 253
255 $builtinType = Type::BUILTIN_TYPE_OBJECT; 255 $builtinType = Type::BUILTIN_TYPE_OBJECT;
256 $class = $collectionValueType->getClassName().'[]'; 256 $class = $collectionValueType->getClassName().'[]';
257 257
258 // Fix a collection that contains the only one element 258 // Fix a collection that contains the only one element
259 // This is special to xml format only 259 // This is special to xml format only
260 if ('xml' === $format && !is_int(key($data))) { 260 if ('xml' === $format && !\is_int(key($data))) {
261 $data = array($data); 261 $data = [$data];
262 } 262 }
263 263
264 if (null !== $collectionKeyType = $type->getCollectionKeyType()) { 264 if (null !== $collectionKeyType = $type->getCollectionKeyType()) {
265 $context['key_type'] = $collectionKeyType; 265 $context['key_type'] = $collectionKeyType;
266 } 266 }
286 // PHP's json_encode, JavaScript's JSON.stringify, Go's json.Marshal as well as most other JSON encoders convert 286 // PHP's json_encode, JavaScript's JSON.stringify, Go's json.Marshal as well as most other JSON encoders convert
287 // floating-point numbers like 12.0 to 12 (the decimal part is dropped when possible). 287 // floating-point numbers like 12.0 to 12 (the decimal part is dropped when possible).
288 // PHP's json_decode automatically converts Numbers without a decimal part to integers. 288 // PHP's json_decode automatically converts Numbers without a decimal part to integers.
289 // To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when 289 // To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when
290 // a float is expected. 290 // a float is expected.
291 if (Type::BUILTIN_TYPE_FLOAT === $builtinType && is_int($data) && false !== strpos($format, JsonEncoder::FORMAT)) { 291 if (Type::BUILTIN_TYPE_FLOAT === $builtinType && \is_int($data) && false !== strpos($format, JsonEncoder::FORMAT)) {
292 return (float) $data; 292 return (float) $data;
293 } 293 }
294 294
295 if (call_user_func('is_'.$builtinType, $data)) { 295 if (\call_user_func('is_'.$builtinType, $data)) {
296 return $data; 296 return $data;
297 } 297 }
298 } 298 }
299 299
300 if (!empty($context[self::DISABLE_TYPE_ENFORCEMENT])) { 300 if (!empty($context[self::DISABLE_TYPE_ENFORCEMENT])) {
301 return $data; 301 return $data;
302 } 302 }
303 303
304 throw new NotNormalizableValueException(sprintf('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, implode('", "', array_keys($expectedTypes)), gettype($data))); 304 throw new NotNormalizableValueException(sprintf('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, implode('", "', array_keys($expectedTypes)), \gettype($data)));
305 }
306
307 /**
308 * @internal
309 */
310 protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
311 {
312 if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($class->getName(), $parameterName)) {
313 return parent::denormalizeParameter($class, $parameter, $parameterName, $parameterData, $context, $format);
314 }
315
316 return $this->validateAndDenormalize($class->getName(), $parameterName, $parameterData, $format, $context);
305 } 317 }
306 318
307 /** 319 /**
308 * Sets an attribute and apply the name converter if necessary. 320 * Sets an attribute and apply the name converter if necessary.
309 * 321 *