|
35 | 35 | use Symfony\Component\Serializer\Encoder\CsvEncoder; |
36 | 36 | use Symfony\Component\Serializer\Encoder\XmlEncoder; |
37 | 37 | use Symfony\Component\Serializer\Exception\LogicException; |
| 38 | +use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException; |
38 | 39 | use Symfony\Component\Serializer\Exception\NotNormalizableValueException; |
| 40 | +use Symfony\Component\Serializer\Exception\RuntimeException; |
39 | 41 | use Symfony\Component\Serializer\Exception\UnexpectedValueException; |
40 | 42 | use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; |
41 | 43 | use Symfony\Component\Serializer\NameConverter\NameConverterInterface; |
@@ -287,6 +289,91 @@ public function denormalize(mixed $data, string $class, ?string $format = null, |
287 | 289 | return $object; |
288 | 290 | } |
289 | 291 |
|
| 292 | + /** |
| 293 | + * Method copy-pasted from symfony/serializer. |
| 294 | + * Remove it after symfony/serializer version update @see https://github.com/symfony/symfony/pull/28263. |
| 295 | + * |
| 296 | + * {@inheritdoc} |
| 297 | + * |
| 298 | + * @internal |
| 299 | + */ |
| 300 | + protected function instantiateObject(array &$data, string $class, array &$context, \ReflectionClass $reflectionClass, array|bool $allowedAttributes, ?string $format = null): object |
| 301 | + { |
| 302 | + if (null !== $object = $this->extractObjectToPopulate($class, $context, static::OBJECT_TO_POPULATE)) { |
| 303 | + unset($context[static::OBJECT_TO_POPULATE]); |
| 304 | + |
| 305 | + return $object; |
| 306 | + } |
| 307 | + |
| 308 | + $class = $this->getClassDiscriminatorResolvedClass($data, $class, $context); |
| 309 | + $reflectionClass = new \ReflectionClass($class); |
| 310 | + |
| 311 | + $constructor = $this->getConstructor($data, $class, $context, $reflectionClass, $allowedAttributes); |
| 312 | + if ($constructor) { |
| 313 | + $constructorParameters = $constructor->getParameters(); |
| 314 | + |
| 315 | + $params = []; |
| 316 | + $missingConstructorArguments = []; |
| 317 | + foreach ($constructorParameters as $constructorParameter) { |
| 318 | + $paramName = $constructorParameter->name; |
| 319 | + $key = $this->nameConverter ? $this->nameConverter->normalize($paramName, $class, $format, $context) : $paramName; |
| 320 | + |
| 321 | + $allowed = false === $allowedAttributes || (\is_array($allowedAttributes) && \in_array($paramName, $allowedAttributes, true)); |
| 322 | + $ignored = !$this->isAllowedAttribute($class, $paramName, $format, $context); |
| 323 | + if ($constructorParameter->isVariadic()) { |
| 324 | + if ($allowed && !$ignored && (isset($data[$key]) || \array_key_exists($key, $data))) { |
| 325 | + if (!\is_array($data[$paramName])) { |
| 326 | + throw new RuntimeException(\sprintf('Cannot create an instance of %s from serialized data because the variadic parameter %s can only accept an array.', $class, $constructorParameter->name)); |
| 327 | + } |
| 328 | + |
| 329 | + $params[] = $data[$paramName]; |
| 330 | + } |
| 331 | + } elseif ($allowed && !$ignored && (isset($data[$key]) || \array_key_exists($key, $data))) { |
| 332 | + $constructorContext = $context; |
| 333 | + $constructorContext['deserialization_path'] = $context['deserialization_path'] ?? $key; |
| 334 | + try { |
| 335 | + $params[] = $this->createConstructorArgument($data[$key], $key, $constructorParameter, $constructorContext, $format); |
| 336 | + } catch (NotNormalizableValueException $exception) { |
| 337 | + if (!isset($context['not_normalizable_value_exceptions'])) { |
| 338 | + throw $exception; |
| 339 | + } |
| 340 | + $context['not_normalizable_value_exceptions'][] = $exception; |
| 341 | + } |
| 342 | + |
| 343 | + // Don't run set for a parameter passed to the constructor |
| 344 | + unset($data[$key]); |
| 345 | + } elseif (isset($context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key])) { |
| 346 | + $params[] = $context[static::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key]; |
| 347 | + } elseif ($constructorParameter->isDefaultValueAvailable()) { |
| 348 | + $params[] = $constructorParameter->getDefaultValue(); |
| 349 | + } else { |
| 350 | + if (!isset($context['not_normalizable_value_exceptions'])) { |
| 351 | + $missingConstructorArguments[] = $constructorParameter->name; |
| 352 | + } |
| 353 | + |
| 354 | + $exception = NotNormalizableValueException::createForUnexpectedDataType(\sprintf('Failed to create object because the class misses the "%s" property.', $constructorParameter->name), $data, ['unknown'], $context['deserialization_path'] ?? null, true); |
| 355 | + $context['not_normalizable_value_exceptions'][] = $exception; |
| 356 | + } |
| 357 | + } |
| 358 | + |
| 359 | + if ($missingConstructorArguments) { |
| 360 | + throw new MissingConstructorArgumentsException(\sprintf('Cannot create an instance of "%s" from serialized data because its constructor requires the following parameters to be present : "$%s".', $class, implode('", "$', $missingConstructorArguments)), 0, null, $missingConstructorArguments, $class); |
| 361 | + } |
| 362 | + |
| 363 | + if (\count($context['not_normalizable_value_exceptions'] ?? []) > 0) { |
| 364 | + return $reflectionClass->newInstanceWithoutConstructor(); |
| 365 | + } |
| 366 | + |
| 367 | + if ($constructor->isConstructor()) { |
| 368 | + return $reflectionClass->newInstanceArgs($params); |
| 369 | + } |
| 370 | + |
| 371 | + return $constructor->invokeArgs(null, $params); |
| 372 | + } |
| 373 | + |
| 374 | + return new $class(); |
| 375 | + } |
| 376 | + |
290 | 377 | protected function getClassDiscriminatorResolvedClass(array $data, string $class, array $context = []): string |
291 | 378 | { |
292 | 379 | if (null === $this->classDiscriminatorResolver || (null === $mapping = $this->classDiscriminatorResolver->getMappingForClass($class))) { |
|
0 commit comments