From d6db18d33b88b8b7c3b2437e314245cca6f096b3 Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Sat, 12 Sep 2020 16:30:30 +0200 Subject: [PATCH 1/5] [Serializer] Add ability to collect denormalization errors --- src/Symfony/Component/Serializer/CHANGELOG.md | 1 + .../Serializer/DenormalizationResult.php | 92 +++++++++ .../Serializer/InvariantViolation.php | 41 ++++ .../Normalizer/AbstractObjectNormalizer.php | 85 +++++++-- .../Normalizer/ArrayDenormalizer.php | 39 +++- .../Normalizer/DataUriNormalizer.php | 23 +++ .../Normalizer/DateIntervalNormalizer.php | 23 +++ .../Normalizer/DateTimeNormalizer.php | 23 +++ .../Normalizer/DenormalizerInterface.php | 6 + .../Normalizer/MimeMessageNormalizer.php | 29 ++- .../Serializer/Normalizer/UidNormalizer.php | 20 +- .../Normalizer/UnwrappingDenormalizer.php | 11 +- .../Component/Serializer/Serializer.php | 14 +- .../Serializer/SerializerInterface.php | 4 + .../Tests/InvariantViolationTest.php | 23 +++ .../Normalizer/DataUriNormalizerTest.php | 30 +++ .../Normalizer/DateTimeNormalizerTest.php | 27 +++ .../Tests/Normalizer/UidNormalizerTest.php | 49 +++++ .../Serializer/Tests/SerializerTest.php | 177 ++++++++++++++++++ 19 files changed, 694 insertions(+), 23 deletions(-) create mode 100644 src/Symfony/Component/Serializer/DenormalizationResult.php create mode 100644 src/Symfony/Component/Serializer/InvariantViolation.php create mode 100644 src/Symfony/Component/Serializer/Tests/InvariantViolationTest.php diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 8ccc9f4c7db1a..a88a7de035f95 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -15,6 +15,7 @@ CHANGELOG * added `FormErrorNormalizer` * added `MimeMessageNormalizer` * serializer mapping can be configured using php attributes + * added `DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS` context option to collect denormalization errors instead of throwing immediately 5.1.0 ----- diff --git a/src/Symfony/Component/Serializer/DenormalizationResult.php b/src/Symfony/Component/Serializer/DenormalizationResult.php new file mode 100644 index 0000000000000..fc35cb3f952b6 --- /dev/null +++ b/src/Symfony/Component/Serializer/DenormalizationResult.php @@ -0,0 +1,92 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer; + +final class DenormalizationResult +{ + private $denormalizedValue; + private $invariantViolations = []; + + private function __construct() + { + } + + public static function success($denormalizedValue): self + { + $result = new self(); + $result->denormalizedValue = $denormalizedValue; + + return $result; + } + + /** + * @param array> $invariantViolations + */ + public static function failure(array $invariantViolations): self + { + $result = new self(); + $result->invariantViolations = $invariantViolations; + + return $result; + } + + public function isSucessful(): bool + { + return [] === $this->invariantViolations; + } + + public function getDenormalizedValue() + { + return $this->denormalizedValue; + } + + public function getInvariantViolations(): array + { + return $this->invariantViolations; + } + + /** + * @return array> + */ + public function getInvariantViolationsNestedIn(string $parentPath): array + { + if ('' === $parentPath) { + throw new \InvalidArgumentException('Parent path cannot be empty.'); + } + + $nestedViolations = []; + + foreach ($this->invariantViolations as $path => $violations) { + $path = '' !== $path ? "{$parentPath}.{$path}" : $parentPath; + + $nestedViolations[$path] = $violations; + } + + return $nestedViolations; + } + + /** + * @return array> + */ + public function getInvariantViolationMessages(): array + { + $messages = []; + + foreach ($this->invariantViolations as $path => $violations) { + foreach ($violations as $violation) { + $messages[$path][] = $violation->getMessage(); + } + } + + return $messages; + } +} diff --git a/src/Symfony/Component/Serializer/InvariantViolation.php b/src/Symfony/Component/Serializer/InvariantViolation.php new file mode 100644 index 0000000000000..94d3ca0b8faee --- /dev/null +++ b/src/Symfony/Component/Serializer/InvariantViolation.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer; + +final class InvariantViolation +{ + private $normalizedValue; + private $message; + private $exception; + + public function __construct($normalizedValue, string $message, ?\Throwable $exception = null) + { + $this->normalizedValue = $normalizedValue; + $this->message = $message; + $this->exception = $exception; + } + + public function getNormalizedValue() + { + return $this->normalizedValue; + } + + public function getMessage(): string + { + return $this->message; + } + + public function getException(): ?\Throwable + { + return $this->exception; + } +} diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 36099aa385ab7..2a2d468c3b006 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -15,6 +15,7 @@ use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Encoder\CsvEncoder; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Encoder\XmlEncoder; @@ -22,6 +23,7 @@ use Symfony\Component\Serializer\Exception\LogicException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\Exception\RuntimeException; +use Symfony\Component\Serializer\InvariantViolation; use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface; @@ -311,6 +313,8 @@ public function denormalize($data, string $type, string $format = null, array $c $object = $this->instantiateObject($normalizedData, $type, $context, $reflectionClass, $allowedAttributes, $format); $resolvedClass = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object); + $invariantViolations = []; + foreach ($normalizedData as $attribute => $value) { if ($this->nameConverter) { $attribute = $this->nameConverter->denormalize($attribute, $resolvedClass, $format, $context); @@ -332,6 +336,17 @@ public function denormalize($data, string $type, string $format = null, array $c } $value = $this->validateAndDenormalize($resolvedClass, $attribute, $value, $format, $context); + + if ($value instanceof DenormalizationResult) { + if (!$value->isSucessful()) { + $invariantViolations += $value->getInvariantViolationsNestedIn($attribute); + + continue; + } + + $value = $value->getDenormalizedValue(); + } + try { $this->setAttributeValue($object, $attribute, $value, $format, $context); } catch (InvalidArgumentException $e) { @@ -339,6 +354,20 @@ public function denormalize($data, string $type, string $format = null, array $c } } + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if (!empty($extraAttributes)) { + $message = (new ExtraAttributesException($extraAttributes))->getMessage(); + + $invariantViolations[''][] = new InvariantViolation($data, $message); + } + + if ([] !== $invariantViolations) { + return DenormalizationResult::failure($invariantViolations); + } + + return DenormalizationResult::success($object); + } + if (!empty($extraAttributes)) { throw new ExtraAttributesException($extraAttributes); } @@ -364,13 +393,13 @@ abstract protected function setAttributeValue(object $object, string $attribute, private function validateAndDenormalize(string $currentClass, string $attribute, $data, ?string $format, array $context) { if (null === $types = $this->getTypes($currentClass, $attribute)) { - return $data; + return $this->denormalizationSuccess($data, $context); } $expectedTypes = []; foreach ($types as $type) { if (null === $data && $type->isNullable()) { - return null; + return $this->denormalizationSuccess(null, $context); } $collectionValueType = $type->isCollection() ? $type->getCollectionValueTypes()[0] ?? null : null; @@ -386,7 +415,7 @@ private function validateAndDenormalize(string $currentClass, string $attribute, // That's why we have to transform the values, if one of these non-string basic datatypes is expected. if (\is_string($data) && (XmlEncoder::FORMAT === $format || CsvEncoder::FORMAT === $format)) { if ('' === $data && $type->isNullable() && \in_array($type->getBuiltinType(), [Type::BUILTIN_TYPE_BOOL, Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_FLOAT], true)) { - return null; + return $this->denormalizationSuccess(null, $context); } switch ($type->getBuiltinType()) { @@ -397,30 +426,36 @@ private function validateAndDenormalize(string $currentClass, string $attribute, } elseif ('true' === $data || '1' === $data) { $data = true; } else { - throw new NotNormalizableValueException(sprintf('The type of the "%s" attribute for class "%s" must be bool ("%s" given).', $attribute, $currentClass, $data)); + $message = sprintf('The type of the "%s" attribute for class "%s" must be bool ("%s" given).', $attribute, $currentClass, $data); + + return $this->denormalizationFailure($data, $message, $context); } break; case Type::BUILTIN_TYPE_INT: if (ctype_digit($data) || '-' === $data[0] && ctype_digit(substr($data, 1))) { $data = (int) $data; } else { - throw new NotNormalizableValueException(sprintf('The type of the "%s" attribute for class "%s" must be int ("%s" given).', $attribute, $currentClass, $data)); + $message = sprintf('The type of the "%s" attribute for class "%s" must be int ("%s" given).', $attribute, $currentClass, $data); + + return $this->denormalizationFailure($data, $message, $context); } break; case Type::BUILTIN_TYPE_FLOAT: if (is_numeric($data)) { - return (float) $data; + return $this->denormalizationSuccess((float) $data, $context); } switch ($data) { case 'NaN': - return \NAN; + return $this->denormalizationSuccess(\NAN, $context); case 'INF': - return \INF; + return $this->denormalizationSuccess(\INF, $context); case '-INF': - return -\INF; + return $this->denormalizationSuccess(-\INF, $context); default: - throw new NotNormalizableValueException(sprintf('The type of the "%s" attribute for class "%s" must be float ("%s" given).', $attribute, $currentClass, $data)); + $message = sprintf('The type of the "%s" attribute for class "%s" must be float ("%s" given).', $attribute, $currentClass, $data); + + return $this->denormalizationFailure($data, $message, $context); } break; @@ -479,19 +514,41 @@ private function validateAndDenormalize(string $currentClass, string $attribute, // To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when // a float is expected. if (Type::BUILTIN_TYPE_FLOAT === $builtinType && \is_int($data) && false !== strpos($format, JsonEncoder::FORMAT)) { - return (float) $data; + return $this->denormalizationSuccess((float) $data, $context); } if (('is_'.$builtinType)($data)) { - return $data; + return $this->denormalizationSuccess($data, $context); } } if ($context[self::DISABLE_TYPE_ENFORCEMENT] ?? $this->defaultContext[self::DISABLE_TYPE_ENFORCEMENT] ?? false) { - return $data; + return $this->denormalizationSuccess($data, $context); + } + + $message = sprintf('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, implode('", "', array_keys($expectedTypes)), get_debug_type($data)); + + return $this->denormalizationFailure($data, $message, $context); + } + + private function denormalizationSuccess($denormalizedValue, array $context) + { + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + return DenormalizationResult::success($denormalizedValue); + } + + return $denormalizedValue; + } + + private function denormalizationFailure($normalizedValue, string $message, array $context) + { + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + $violation = new InvariantViolation($normalizedValue, $message); + + return DenormalizationResult::failure(['' => [$violation]]); } - 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)), get_debug_type($data))); + throw new NotNormalizableValueException($message); } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php index 77c746c752282..496dd28544d9d 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php @@ -11,9 +11,11 @@ namespace Symfony\Component\Serializer\Normalizer; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Exception\BadMethodCallException; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; +use Symfony\Component\Serializer\InvariantViolation; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Serializer\SerializerInterface; @@ -34,7 +36,7 @@ class ArrayDenormalizer implements ContextAwareDenormalizerInterface, Denormaliz * * @throws NotNormalizableValueException */ - public function denormalize($data, string $type, string $format = null, array $context = []): array + public function denormalize($data, string $type, string $format = null, array $context = []) { if (null === $this->denormalizer) { throw new BadMethodCallException('Please set a denormalizer before calling denormalize()!'); @@ -48,13 +50,44 @@ public function denormalize($data, string $type, string $format = null, array $c $type = substr($type, 0, -2); + $invariantViolations = []; + $collectInvariantViolations = $context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false; + $builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null; foreach ($data as $key => $value) { if (null !== $builtinType && !('is_'.$builtinType)($key)) { - throw new NotNormalizableValueException(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, get_debug_type($key))); + $message = sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, get_debug_type($key)); + + if ($collectInvariantViolations) { + $invariantViolations[$key][] = new InvariantViolation($value, $message); + + continue; + } + + throw new NotNormalizableValueException($message); } - $data[$key] = $this->denormalizer->denormalize($value, $type, $format, $context); + $denormalizedValue = $this->denormalizer->denormalize($value, $type, $format, $context); + + if ($denormalizedValue instanceof DenormalizationResult) { + if (!$denormalizedValue->isSucessful()) { + $invariantViolations += $denormalizedValue->getInvariantViolationsNestedIn($key); + + continue; + } + + $denormalizedValue = $denormalizedValue->getDenormalizedValue(); + } + + $data[$key] = $denormalizedValue; + } + + if ([] !== $invariantViolations) { + return DenormalizationResult::failure($invariantViolations); + } + + if ($collectInvariantViolations) { + return DenormalizationResult::success($data); } return $data; diff --git a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php index bb866ec9bcc36..93182085af0b7 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php @@ -14,8 +14,10 @@ use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\Mime\MimeTypeGuesserInterface; use Symfony\Component\Mime\MimeTypes; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; +use Symfony\Component\Serializer\InvariantViolation; /** * Normalizes an {@see \SplFileInfo} object to a data URI. @@ -94,6 +96,27 @@ public function supportsNormalization($data, string $format = null) * @return \SplFileInfo */ public function denormalize($data, string $type, string $format = null, array $context = []) + { + try { + $result = $this->doDenormalize($data, $type); + } catch (NotNormalizableValueException $exception) { + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + $violation = new InvariantViolation($data, $exception->getMessage(), $exception); + + return DenormalizationResult::failure(['' => [$violation]]); + } + + throw $exception; + } + + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + return DenormalizationResult::success($result); + } + + return $result; + } + + private function doDenormalize($data, string $type) { if (!preg_match('/^data:([a-z0-9][a-z0-9\!\#\$\&\-\^\_\+\.]{0,126}\/[a-z0-9][a-z0-9\!\#\$\&\-\^\_\+\.]{0,126}(;[a-z0-9\-]+\=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9\!\$\&\\\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$/i', $data)) { throw new NotNormalizableValueException('The provided "data:" URI is not valid.'); diff --git a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php index a47fd76b45116..93274b938bc2c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php @@ -11,8 +11,10 @@ namespace Symfony\Component\Serializer\Normalizer; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\UnexpectedValueException; +use Symfony\Component\Serializer\InvariantViolation; /** * Normalizes an instance of {@see \DateInterval} to an interval string. @@ -74,6 +76,27 @@ public function hasCacheableSupportsMethod(): bool * @return \DateInterval */ public function denormalize($data, string $type, string $format = null, array $context = []) + { + try { + $result = $this->doDenormalize($data, $context); + } catch (InvalidArgumentException | UnexpectedValueException $exception) { + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + $violation = new InvariantViolation($data, $exception->getMessage(), $exception); + + return DenormalizationResult::failure(['' => [$violation]]); + } + + throw $exception; + } + + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + return DenormalizationResult::success($result); + } + + return $result; + } + + private function doDenormalize($data, array $context = []) { if (!\is_string($data)) { throw new InvalidArgumentException(sprintf('Data expected to be a string, "%s" given.', get_debug_type($data))); diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index 9de76008a56a7..85b54800d1658 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -11,8 +11,10 @@ namespace Symfony\Component\Serializer\Normalizer; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; +use Symfony\Component\Serializer\InvariantViolation; /** * Normalizes an object implementing the {@see \DateTimeInterface} to a date string. @@ -81,6 +83,27 @@ public function supportsNormalization($data, string $format = null) * @return \DateTimeInterface */ public function denormalize($data, string $type, string $format = null, array $context = []) + { + try { + $result = $this->doDenormalize($data, $type, $context); + } catch (NotNormalizableValueException $exception) { + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + $violation = new InvariantViolation($data, $exception->getMessage(), $exception); + + return DenormalizationResult::failure(['' => [$violation]]); + } + + throw $exception; + } + + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + return DenormalizationResult::success($result); + } + + return $result; + } + + private function doDenormalize($data, string $type, array $context = []) { $dateTimeFormat = $context[self::FORMAT_KEY] ?? null; $timezone = $this->getTimezone($context); diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php index d903b3912d019..a783d6cd0bb28 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php @@ -24,9 +24,15 @@ */ interface DenormalizerInterface { + const COLLECT_INVARIANT_VIOLATIONS = 'collect_invariant_violations'; + /** * Denormalizes data back into an object of the given class. * + * When context option `collect_invariant_violations` is enabled, the + * denormalizer must always return an instance of + * {@see \Symfony\Component\Serializer\DenormalizationResult}. + * * @param mixed $data Data to restore * @param string $type The expected class to instantiate * @param string $format Format the given data was extracted from diff --git a/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php index a1c4f169bbf5e..54289ac2c3fe4 100644 --- a/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php @@ -17,6 +17,7 @@ use Symfony\Component\Mime\Header\UnstructuredHeader; use Symfony\Component\Mime\Message; use Symfony\Component\Mime\Part\AbstractPart; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Serializer\SerializerInterface; @@ -80,13 +81,37 @@ public function denormalize($data, string $type, ?string $format = null, array $ { if (Headers::class === $type) { $ret = []; + $invariantViolations = []; foreach ($data as $headers) { foreach ($headers as $header) { - $ret[] = $this->serializer->denormalize($header, $this->headerClassMap[strtolower($header['name'])] ?? UnstructuredHeader::class, $format, $context); + $name = $header['name']; + $result = $this->serializer->denormalize($header, $this->headerClassMap[strtolower($name)] ?? UnstructuredHeader::class, $format, $context); + + if ($result instanceof DenormalizationResult) { + if (!$result->isSucessful()) { + $invariantViolations += $result->getInvariantViolations(); + + continue; + } + + $result = $result->getDenormalizedValue(); + } + + $ret[] = $result; } } - return new Headers(...$ret); + if ([] !== $invariantViolations) { + return DenormalizationResult::failure($invariantViolations); + } + + $headers = new Headers(...$ret); + + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + return DenormalizationResult::success($headers); + } + + return $headers; } if (AbstractPart::class === $type) { diff --git a/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php index a7152613cbd93..d3efbd0765bb1 100644 --- a/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php @@ -11,8 +11,10 @@ namespace Symfony\Component\Serializer\Normalizer; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Exception\LogicException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; +use Symfony\Component\Serializer\InvariantViolation; use Symfony\Component\Uid\AbstractUid; use Symfony\Component\Uid\Ulid; use Symfony\Component\Uid\Uuid; @@ -70,10 +72,24 @@ public function supportsNormalization($data, string $format = null) public function denormalize($data, string $type, string $format = null, array $context = []) { try { - return Ulid::class === $type ? Ulid::fromString($data) : Uuid::fromString($data); + $result = Ulid::class === $type ? Ulid::fromString($data) : Uuid::fromString($data); } catch (\InvalidArgumentException $exception) { - throw new NotNormalizableValueException(sprintf('The data is not a valid "%s" string representation.', $type)); + $message = sprintf('The data is not a valid "%s" string representation.', $type); + + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + $violation = new InvariantViolation($data, $message, $exception); + + return DenormalizationResult::failure(['' => [$violation]]); + } + + throw new NotNormalizableValueException($message); } + + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + return DenormalizationResult::success($result); + } + + return $result; } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php index 6bc8df80bb3cd..c47cf41a5d49e 100644 --- a/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php @@ -13,6 +13,7 @@ use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Serializer\SerializerAwareTrait; @@ -48,7 +49,15 @@ public function denormalize($data, $class, string $format = null, array $context $data = $this->propertyAccessor->getValue($data, $propertyPath); } - return $this->serializer->denormalize($data, $class, $format, $context); + $result = $this->serializer->denormalize($data, $class, $format, $context); + + if ($result instanceof DenormalizationResult && !$result->isSucessful()) { + $propertyPath = str_replace('][', '.', substr($propertyPath, 1, -1)); + + return DenormalizationResult::failure($result->getInvariantViolationsNestedIn($propertyPath)); + } + + return $result; } /** diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index 6414caf900472..d588cd48a1f50 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -194,7 +194,19 @@ public function denormalize($data, string $type, string $format = null, array $c // Check for a denormalizer first, e.g. the data is wrapped if (!$normalizer && isset(self::SCALAR_TYPES[$type])) { if (!('is_'.$type)($data)) { - throw new NotNormalizableValueException(sprintf('Data expected to be of type "%s" ("%s" given).', $type, get_debug_type($data))); + $message = sprintf('Data expected to be of type "%s" ("%s" given).', $type, get_debug_type($data)); + + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + $violation = new InvariantViolation($data, $message); + + return DenormalizationResult::failure(['' => [$violation]]); + } + + throw new NotNormalizableValueException($message); + } + + if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + return DenormalizationResult::success($data); } return $data; diff --git a/src/Symfony/Component/Serializer/SerializerInterface.php b/src/Symfony/Component/Serializer/SerializerInterface.php index 96e144e90e992..c968a04add921 100644 --- a/src/Symfony/Component/Serializer/SerializerInterface.php +++ b/src/Symfony/Component/Serializer/SerializerInterface.php @@ -30,6 +30,10 @@ public function serialize($data, string $format, array $context = []); /** * Deserializes data into the given type. * + * When context option `collect_invariant_violations` is enabled, the + * serializer must always return an instance of + * {@see \Symfony\Component\Serializer\DenormalizationResult}. + * * @param mixed $data * * @return mixed diff --git a/src/Symfony/Component/Serializer/Tests/InvariantViolationTest.php b/src/Symfony/Component/Serializer/Tests/InvariantViolationTest.php new file mode 100644 index 0000000000000..020a474550341 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/InvariantViolationTest.php @@ -0,0 +1,23 @@ +getNormalizedValue()); + self::assertSame('"foo" is not an integer.', $violation->getMessage()); + self::assertSame($exception, $violation->getException()); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php index 1e54f5b37bfa0..a6d5c961ea544 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php @@ -13,7 +13,9 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\File\File; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Normalizer\DataUriNormalizer; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; /** * @author Kévin Dunglas @@ -169,6 +171,34 @@ public function validUriProvider() ]; } + /** + * @dataProvider validUriProvider + */ + public function testItDenormalizesAndReturnsSuccessResult($uri): void + { + $result = $this->normalizer->denormalize($uri, \SplFileObject::class, null, [ + DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + ]); + + self::assertInstanceOf(DenormalizationResult::class, $result); + self::assertTrue($result->isSucessful()); + self::assertInstanceOf(\SplFileObject::class, $result->getDenormalizedValue()); + } + + public function testItDenormalizesAndReturnsFailureResult(): void + { + $result = $this->normalizer->denormalize('not-a-uri', \SplFileObject::class, null, [ + DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + ]); + + self::assertInstanceOf(DenormalizationResult::class, $result); + self::assertFalse($result->isSucessful()); + self::assertSame( + ['' => ['The provided "data:" URI is not valid.']], + $result->getInvariantViolationMessages() + ); + } + private function getContent(\SplFileObject $file) { $buffer = ''; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php index a041074b84fee..e43b38d47c68f 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php @@ -12,7 +12,9 @@ namespace Symfony\Component\Serializer\Tests\Normalizer; use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; /** * @author Kévin Dunglas @@ -259,4 +261,29 @@ public function testDenormalizeFormatMismatchThrowsException() $this->expectException(\Symfony\Component\Serializer\Exception\UnexpectedValueException::class); $this->normalizer->denormalize('2016-01-01T00:00:00+00:00', \DateTimeInterface::class, null, [DateTimeNormalizer::FORMAT_KEY => 'Y-m-d|']); } + + public function testItDenormalizesAndReturnsSuccessResult(): void + { + $result = $this->normalizer->denormalize('2020-01-01', \DateTimeInterface::class, null, [ + DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + ]); + + self::assertInstanceOf(DenormalizationResult::class, $result); + self::assertTrue($result->isSucessful()); + self::assertEquals(new \DateTime('2020-01-01'), $result->getDenormalizedValue()); + } + + public function testItDenormalizesAndReturnsFailureResult(): void + { + $result = $this->normalizer->denormalize('not-a-date', \DateTimeInterface::class, null, [ + DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + ]); + + self::assertInstanceOf(DenormalizationResult::class, $result); + self::assertFalse($result->isSucessful()); + self::assertSame( + ['' => ['DateTimeImmutable::__construct(): Failed to parse time string (not-a-date) at position 0 (n): The timezone could not be found in the database']], + $result->getInvariantViolationMessages() + ); + } } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php index f1b766ea4c87c..773de4ff5f02e 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php @@ -3,7 +3,9 @@ namespace Symfony\Component\Serializer\Tests\Normalizer; use PHPUnit\Framework\TestCase; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Exception\LogicException; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\UidNormalizer; use Symfony\Component\Uid\AbstractUid; use Symfony\Component\Uid\Ulid; @@ -168,4 +170,51 @@ public function testNormalizeWithNormalizationFormatNotValid() 'uid_normalization_format' => 'ccc', ]); } + + /** + * @dataProvider dataProvider + */ + public function testItDenormalizesAndReturnsSuccessResult(string $uuidString, string $class): void + { + $result = $this->normalizer->denormalize($uuidString, $class, null, [ + DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + ]); + + self::assertInstanceOf(DenormalizationResult::class, $result); + self::assertTrue($result->isSucessful()); + self::assertEquals( + Ulid::class === $class ? new Ulid($uuidString) : Uuid::fromString($uuidString), + $result->getDenormalizedValue() + ); + } + + /** + * @dataProvider failureDataProvider + */ + public function testItDenormalizesAndReturnsFailureResult(string $class): void + { + $result = $this->normalizer->denormalize('not-an-uuid', $class, null, [ + DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + ]); + + self::assertInstanceOf(DenormalizationResult::class, $result); + self::assertFalse($result->isSucessful()); + self::assertSame( + ['' => [sprintf('The data is not a valid "%s" string representation.', $class)]], + $result->getInvariantViolationMessages() + ); + } + + public function failureDataProvider(): iterable + { + return [ + [UuidV1::class], + [UuidV3::class], + [UuidV4::class], + [UuidV5::class], + [UuidV6::class], + [AbstractUid::class], + [Ulid::class], + ]; + } } diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 7806be7cbb496..767a61c3f1a8a 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -11,11 +11,13 @@ namespace Symfony\Component\Serializer\Tests; +use DateTimeImmutable; use Doctrine\Common\Annotations\AnnotationReader; use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; +use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\LogicException; @@ -30,6 +32,7 @@ use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; use Symfony\Component\Serializer\Normalizer\CustomNormalizer; +use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; @@ -640,6 +643,154 @@ public function testDeserializeAndUnwrap() $serializer->deserialize($jsonData, __NAMESPACE__.'\Model', 'json', [UnwrappingDenormalizer::UNWRAP_PATH => '[baz][inner]']) ); } + + /** + * @dataProvider provideDenormalizationSuccessResultCases + */ + public function testDenormalizationSuccessResult(string $type, $normalizedData, $expectedValue) + { + $serializer = new Serializer( + [ + new DateTimeNormalizer(), + new ObjectNormalizer(null, null, null, new PhpDocExtractor()), + new ArrayDenormalizer(), + ], + [ + 'json' => new JsonEncoder(), + ] + ); + + $json = json_encode($normalizedData); + + $result = $serializer->deserialize($json, $type, 'json', [ + Serializer::COLLECT_INVARIANT_VIOLATIONS => true, + ]); + + self::assertInstanceOf(DenormalizationResult::class, $result); + self::assertTrue($result->isSucessful()); + self::assertEquals($expectedValue, $result->getDenormalizedValue()); + } + + public function provideDenormalizationSuccessResultCases() + { + $dto = new Dto(); + $dto->int = 1; + $dto->date = new DateTimeImmutable('2020-01-01'); + $dto->nested = new NestedDto(); + $dto->nested->string = 'string'; + + yield [ + Dto::class, + [ + 'int' => 1, + 'date' => '2020-01-01', + 'nested' => [ + 'string' => 'string', + ], + ], + $dto, + ]; + + yield [ + 'bool', + true, + true, + ]; + } + + /** + * @dataProvider provideDenormalizationFailureResultCases + */ + public function testDenormalizationFailureResult(string $type, $normalizedData, array $expectedErrors) + { + $serializer = new Serializer( + [ + new DateTimeNormalizer(), + new ObjectNormalizer(null, null, null, new PhpDocExtractor()), + new ArrayDenormalizer(), + ], + [ + 'json' => new JsonEncoder(), + ] + ); + + $json = json_encode($normalizedData); + + $result = $serializer->deserialize($json, $type, 'json', [ + Serializer::COLLECT_INVARIANT_VIOLATIONS => true, + ]); + + self::assertInstanceOf(DenormalizationResult::class, $result); + self::assertFalse($result->isSucessful()); + self::assertSame($expectedErrors, $result->getInvariantViolationMessages()); + } + + public function provideDenormalizationFailureResultCases() + { + yield [ + Dto::class, + [ + 'int' => 'not-an-integer', + 'date' => 'not-a-date', + 'nested' => [ + 'string' => [], + ], + ], + [ + 'int' => ['The type of the "int" attribute for class "Symfony\Component\Serializer\Tests\Dto" must be one of "int" ("string" given).'], + 'date' => ['DateTimeImmutable::__construct(): Failed to parse time string (not-a-date) at position 0 (n): The timezone could not be found in the database'], + 'nested.string' => ['The type of the "string" attribute for class "Symfony\Component\Serializer\Tests\NestedDto" must be one of "string" ("array" given).'], + ], + ]; + + yield [ + 'bool', + 'not-a-boolean', + [ + '' => ['Data expected to be of type "bool" ("string" given).'], + ], + ]; + } + + public function testDenormalizationFailureResultWithUnwrapping() + { + $serializer = new Serializer( + [ + new UnwrappingDenormalizer(new PropertyAccessor()), + new DateTimeNormalizer(), + new ObjectNormalizer(null, null, null, new PhpDocExtractor()), + new ArrayDenormalizer(), + ], + [ + 'json' => new JsonEncoder(), + ] + ); + + $json = json_encode([ + 'wrapped' => [ + 'data' => [ + 'int' => 'not-an-integer', + 'date' => 'not-a-date', + 'nested' => [ + 'string' => [], + ], + ], + ], + ]); + + $result = $serializer->deserialize($json, Dto::class, 'json', [ + Serializer::COLLECT_INVARIANT_VIOLATIONS => true, + UnwrappingDenormalizer::UNWRAP_PATH => '[wrapped][data]', + ]); + + self::assertInstanceOf(DenormalizationResult::class, $result); + self::assertFalse($result->isSucessful()); + self::assertSame([ + 'wrapped.data.int' => ['The type of the "int" attribute for class "Symfony\Component\Serializer\Tests\Dto" must be one of "int" ("string" given).'], + 'wrapped.data.date' => ['DateTimeImmutable::__construct(): Failed to parse time string (not-a-date) at position 0 (n): The timezone could not be found in the database'], + 'wrapped.data.nested.string' => ['The type of the "string" attribute for class "Symfony\Component\Serializer\Tests\NestedDto" must be one of "string" ("array" given).'], + ], $result->getInvariantViolationMessages()); + } } class Model @@ -713,3 +864,29 @@ interface NormalizerAwareNormalizer extends NormalizerInterface, NormalizerAware interface DenormalizerAwareDenormalizer extends DenormalizerInterface, DenormalizerAwareInterface { } + +class Dto +{ + /** + * @var int + */ + public $int; + + /** + * @var \DateTimeImmutable + */ + public $date; + + /** + * @var NestedDto + */ + public $nested; +} + +class NestedDto +{ + /** + * @var string + */ + public $string; +} From 5dc34909d62869a0ec19d5d8a96d4fb4edc60427 Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Sun, 3 Jan 2021 10:33:21 +0100 Subject: [PATCH 2/5] Exception -> Throwable --- .../Component/Serializer/InvariantViolation.php | 10 +++++----- .../Serializer/Tests/InvariantViolationTest.php | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Serializer/InvariantViolation.php b/src/Symfony/Component/Serializer/InvariantViolation.php index 94d3ca0b8faee..50f000649f850 100644 --- a/src/Symfony/Component/Serializer/InvariantViolation.php +++ b/src/Symfony/Component/Serializer/InvariantViolation.php @@ -15,13 +15,13 @@ final class InvariantViolation { private $normalizedValue; private $message; - private $exception; + private $throwable; - public function __construct($normalizedValue, string $message, ?\Throwable $exception = null) + public function __construct($normalizedValue, string $message, ?\Throwable $throwable = null) { $this->normalizedValue = $normalizedValue; $this->message = $message; - $this->exception = $exception; + $this->throwable = $throwable; } public function getNormalizedValue() @@ -34,8 +34,8 @@ public function getMessage(): string return $this->message; } - public function getException(): ?\Throwable + public function getThrowable(): ?\Throwable { - return $this->exception; + return $this->throwable; } } diff --git a/src/Symfony/Component/Serializer/Tests/InvariantViolationTest.php b/src/Symfony/Component/Serializer/Tests/InvariantViolationTest.php index 020a474550341..7b81f816fe92c 100644 --- a/src/Symfony/Component/Serializer/Tests/InvariantViolationTest.php +++ b/src/Symfony/Component/Serializer/Tests/InvariantViolationTest.php @@ -18,6 +18,6 @@ public function testItRepresentsAnInvariantViolation(): void self::assertSame('foo', $violation->getNormalizedValue()); self::assertSame('"foo" is not an integer.', $violation->getMessage()); - self::assertSame($exception, $violation->getException()); + self::assertSame($exception, $violation->getThrowable()); } } From 30b4ef47c70115103d7b8aaa61fb12ef4436540d Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Sun, 3 Jan 2021 10:45:22 +0100 Subject: [PATCH 3/5] Allow passing partially denormalized value in failure result --- src/Symfony/Component/Serializer/DenormalizationResult.php | 3 ++- .../Serializer/Normalizer/AbstractObjectNormalizer.php | 2 +- .../Component/Serializer/Normalizer/ArrayDenormalizer.php | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Serializer/DenormalizationResult.php b/src/Symfony/Component/Serializer/DenormalizationResult.php index fc35cb3f952b6..36290aa59ff29 100644 --- a/src/Symfony/Component/Serializer/DenormalizationResult.php +++ b/src/Symfony/Component/Serializer/DenormalizationResult.php @@ -31,10 +31,11 @@ public static function success($denormalizedValue): self /** * @param array> $invariantViolations */ - public static function failure(array $invariantViolations): self + public static function failure(array $invariantViolations, $partiallyDenormalizedValue = null): self { $result = new self(); $result->invariantViolations = $invariantViolations; + $result->denormalizedValue = $partiallyDenormalizedValue; return $result; } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 2a2d468c3b006..f9236eee90290 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -362,7 +362,7 @@ public function denormalize($data, string $type, string $format = null, array $c } if ([] !== $invariantViolations) { - return DenormalizationResult::failure($invariantViolations); + return DenormalizationResult::failure($invariantViolations, $object); } return DenormalizationResult::success($object); diff --git a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php index 496dd28544d9d..3772077a0c504 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php @@ -83,7 +83,7 @@ public function denormalize($data, string $type, string $format = null, array $c } if ([] !== $invariantViolations) { - return DenormalizationResult::failure($invariantViolations); + return DenormalizationResult::failure($invariantViolations, $data); } if ($collectInvariantViolations) { From 1abf68a346239c6869b2acca3f6236f68899caf6 Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Fri, 8 Jan 2021 08:12:10 +0100 Subject: [PATCH 4/5] Move DenormalizationResult to Result namespace --- .../Serializer/Normalizer/AbstractObjectNormalizer.php | 2 +- .../Component/Serializer/Normalizer/DataUriNormalizer.php | 2 +- .../Serializer/Normalizer/DateIntervalNormalizer.php | 2 +- .../Component/Serializer/Normalizer/DateTimeNormalizer.php | 2 +- .../Component/Serializer/Normalizer/DenormalizerInterface.php | 2 +- .../Component/Serializer/Normalizer/MimeMessageNormalizer.php | 2 +- src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php | 2 +- .../Serializer/Normalizer/UnwrappingDenormalizer.php | 2 +- .../Serializer/{ => Result}/DenormalizationResult.php | 4 +++- src/Symfony/Component/Serializer/Serializer.php | 1 + src/Symfony/Component/Serializer/SerializerInterface.php | 2 +- .../Serializer/Tests/Normalizer/DataUriNormalizerTest.php | 2 +- .../Serializer/Tests/Normalizer/DateTimeNormalizerTest.php | 2 +- .../Serializer/Tests/Normalizer/UidNormalizerTest.php | 2 +- src/Symfony/Component/Serializer/Tests/SerializerTest.php | 2 +- 15 files changed, 17 insertions(+), 14 deletions(-) rename src/Symfony/Component/Serializer/{ => Result}/DenormalizationResult.php (95%) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index f9236eee90290..be04ddea770b1 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -15,7 +15,6 @@ use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type; -use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Encoder\CsvEncoder; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Encoder\XmlEncoder; @@ -29,6 +28,7 @@ use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; +use Symfony\Component\Serializer\Result\DenormalizationResult; /** * Base class for a normalizer dealing with objects. diff --git a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php index 93182085af0b7..a9d4aae3971b0 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php @@ -14,10 +14,10 @@ use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\Mime\MimeTypeGuesserInterface; use Symfony\Component\Mime\MimeTypes; -use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\InvariantViolation; +use Symfony\Component\Serializer\Result\DenormalizationResult; /** * Normalizes an {@see \SplFileInfo} object to a data URI. diff --git a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php index 93274b938bc2c..d3c4b98f39192 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php @@ -11,10 +11,10 @@ namespace Symfony\Component\Serializer\Normalizer; -use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\InvariantViolation; +use Symfony\Component\Serializer\Result\DenormalizationResult; /** * Normalizes an instance of {@see \DateInterval} to an interval string. diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index 85b54800d1658..040cb8a216a86 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -11,10 +11,10 @@ namespace Symfony\Component\Serializer\Normalizer; -use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\InvariantViolation; +use Symfony\Component\Serializer\Result\DenormalizationResult; /** * Normalizes an object implementing the {@see \DateTimeInterface} to a date string. diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php index a783d6cd0bb28..df38c48b1cbb7 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php @@ -31,7 +31,7 @@ interface DenormalizerInterface * * When context option `collect_invariant_violations` is enabled, the * denormalizer must always return an instance of - * {@see \Symfony\Component\Serializer\DenormalizationResult}. + * {@see \Symfony\Component\Serializer\Result\DenormalizationResult}. * * @param mixed $data Data to restore * @param string $type The expected class to instantiate diff --git a/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php index 54289ac2c3fe4..752af1b50deaa 100644 --- a/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php @@ -17,7 +17,7 @@ use Symfony\Component\Mime\Header\UnstructuredHeader; use Symfony\Component\Mime\Message; use Symfony\Component\Mime\Part\AbstractPart; -use Symfony\Component\Serializer\DenormalizationResult; +use Symfony\Component\Serializer\Result\DenormalizationResult; use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Serializer\SerializerInterface; diff --git a/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php index d3efbd0765bb1..f0b9338e5c234 100644 --- a/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php @@ -11,10 +11,10 @@ namespace Symfony\Component\Serializer\Normalizer; -use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Exception\LogicException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\InvariantViolation; +use Symfony\Component\Serializer\Result\DenormalizationResult; use Symfony\Component\Uid\AbstractUid; use Symfony\Component\Uid\Ulid; use Symfony\Component\Uid\Uuid; diff --git a/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php index c47cf41a5d49e..781059796f336 100644 --- a/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/UnwrappingDenormalizer.php @@ -13,7 +13,7 @@ use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessorInterface; -use Symfony\Component\Serializer\DenormalizationResult; +use Symfony\Component\Serializer\Result\DenormalizationResult; use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Serializer\SerializerAwareTrait; diff --git a/src/Symfony/Component/Serializer/DenormalizationResult.php b/src/Symfony/Component/Serializer/Result/DenormalizationResult.php similarity index 95% rename from src/Symfony/Component/Serializer/DenormalizationResult.php rename to src/Symfony/Component/Serializer/Result/DenormalizationResult.php index 36290aa59ff29..34c75d9fde4d2 100644 --- a/src/Symfony/Component/Serializer/DenormalizationResult.php +++ b/src/Symfony/Component/Serializer/Result/DenormalizationResult.php @@ -9,7 +9,9 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Serializer; +namespace Symfony\Component\Serializer\Result; + +use Symfony\Component\Serializer\InvariantViolation; final class DenormalizationResult { diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index d588cd48a1f50..264c956b832ac 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -29,6 +29,7 @@ use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Result\DenormalizationResult; /** * Serializer serializes and deserializes data. diff --git a/src/Symfony/Component/Serializer/SerializerInterface.php b/src/Symfony/Component/Serializer/SerializerInterface.php index c968a04add921..041368e3b3e25 100644 --- a/src/Symfony/Component/Serializer/SerializerInterface.php +++ b/src/Symfony/Component/Serializer/SerializerInterface.php @@ -32,7 +32,7 @@ public function serialize($data, string $format, array $context = []); * * When context option `collect_invariant_violations` is enabled, the * serializer must always return an instance of - * {@see \Symfony\Component\Serializer\DenormalizationResult}. + * {@see \Symfony\Component\Serializer\Result\DenormalizationResult}. * * @param mixed $data * diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php index a6d5c961ea544..09697dca24eea 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php @@ -13,9 +13,9 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\File\File; -use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Normalizer\DataUriNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Result\DenormalizationResult; /** * @author Kévin Dunglas diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php index e43b38d47c68f..9f8c252163ea2 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php @@ -12,9 +12,9 @@ namespace Symfony\Component\Serializer\Tests\Normalizer; use PHPUnit\Framework\TestCase; -use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Result\DenormalizationResult; /** * @author Kévin Dunglas diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php index 773de4ff5f02e..e59be25bee1d4 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php @@ -3,10 +3,10 @@ namespace Symfony\Component\Serializer\Tests\Normalizer; use PHPUnit\Framework\TestCase; -use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Exception\LogicException; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\UidNormalizer; +use Symfony\Component\Serializer\Result\DenormalizationResult; use Symfony\Component\Uid\AbstractUid; use Symfony\Component\Uid\Ulid; use Symfony\Component\Uid\Uuid; diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 767a61c3f1a8a..70f1a1bcef949 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -17,7 +17,6 @@ use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; -use Symfony\Component\Serializer\DenormalizationResult; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\LogicException; @@ -41,6 +40,7 @@ use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; use Symfony\Component\Serializer\Normalizer\UnwrappingDenormalizer; +use Symfony\Component\Serializer\Result\DenormalizationResult; use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummy; use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummyFirstChild; From 096c487f329d9c12aaf72cc23cdf5f15206b9b6e Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Tue, 26 Jan 2021 19:17:05 +0100 Subject: [PATCH 5/5] NormalizationResult --- .../Normalizer/FlattenExceptionNormalizer.php | 6 ++ src/Symfony/Component/Serializer/CHANGELOG.md | 2 +- .../Normalizer/AbstractNormalizer.php | 18 +++- .../Normalizer/AbstractObjectNormalizer.php | 12 ++- .../Normalizer/ArrayDenormalizer.php | 2 +- .../ConstraintViolationListNormalizer.php | 12 ++- .../Normalizer/CustomNormalizer.php | 10 +- .../Normalizer/DataUriNormalizer.php | 16 +++- .../Normalizer/DateIntervalNormalizer.php | 14 ++- .../Normalizer/DateTimeNormalizer.php | 16 +++- .../Normalizer/DateTimeZoneNormalizer.php | 10 +- .../Normalizer/DenormalizerInterface.php | 6 +- .../Normalizer/FormErrorNormalizer.php | 8 +- .../Normalizer/JsonSerializableNormalizer.php | 10 +- .../Normalizer/MimeMessageNormalizer.php | 19 +++- .../Normalizer/NormalizerInterface.php | 6 +- .../Normalizer/ProblemNormalizer.php | 6 ++ .../Serializer/Normalizer/UidNormalizer.php | 17 +++- .../Serializer/Result/NormalizationResult.php | 93 +++++++++++++++++++ .../Component/Serializer/Serializer.php | 25 +++-- .../Serializer/SerializerInterface.php | 10 +- .../Normalizer/DataUriNormalizerTest.php | 5 +- .../Normalizer/DateTimeNormalizerTest.php | 5 +- .../Tests/Normalizer/UidNormalizerTest.php | 5 +- .../Serializer/Tests/SerializerTest.php | 7 +- 25 files changed, 288 insertions(+), 52 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Result/NormalizationResult.php diff --git a/src/Symfony/Component/Messenger/Transport/Serialization/Normalizer/FlattenExceptionNormalizer.php b/src/Symfony/Component/Messenger/Transport/Serialization/Normalizer/FlattenExceptionNormalizer.php index 344eea7cc743f..78d293d8d3350 100644 --- a/src/Symfony/Component/Messenger/Transport/Serialization/Normalizer/FlattenExceptionNormalizer.php +++ b/src/Symfony/Component/Messenger/Transport/Serialization/Normalizer/FlattenExceptionNormalizer.php @@ -17,6 +17,8 @@ use Symfony\Component\Serializer\Normalizer\ContextAwareNormalizerInterface; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait; +use Symfony\Component\Serializer\Result\NormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; /** * This normalizer is only used in Debug/Dev/Messenger contexts. @@ -50,6 +52,10 @@ public function normalize($object, $format = null, array $context = []) $normalized['status'] = $status; } + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($normalized); + } + return $normalized; } diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index a88a7de035f95..709b5dd8c09da 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -15,7 +15,7 @@ CHANGELOG * added `FormErrorNormalizer` * added `MimeMessageNormalizer` * serializer mapping can be configured using php attributes - * added `DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS` context option to collect denormalization errors instead of throwing immediately + * added `SerializerInterface::RETURN_RESULT` context option to collect (de)normalization errors instead of throwing immediately 5.1.0 ----- diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 4a03ab851a3a2..a9fd856d116aa 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -19,8 +19,10 @@ use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; +use Symfony\Component\Serializer\Result\NormalizationResult; use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Serializer\SerializerAwareTrait; +use Symfony\Component\Serializer\SerializerInterface; /** * Normalizer implementation. @@ -210,10 +212,22 @@ protected function handleCircularReference(object $object, string $format = null { $circularReferenceHandler = $context[self::CIRCULAR_REFERENCE_HANDLER] ?? $this->defaultContext[self::CIRCULAR_REFERENCE_HANDLER]; if ($circularReferenceHandler) { - return $circularReferenceHandler($object, $format, $context); + $result = $circularReferenceHandler($object, $format, $context); + + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($result); + } + + return $result; + } + + $exception = new CircularReferenceException(sprintf('A circular reference has been detected when serializing the object of class "%s" (configured limit: %d).', get_debug_type($object), $context[self::CIRCULAR_REFERENCE_LIMIT] ?? $this->defaultContext[self::CIRCULAR_REFERENCE_LIMIT])); + + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::failure(['' => $exception]); } - throw new CircularReferenceException(sprintf('A circular reference has been detected when serializing the object of class "%s" (configured limit: %d).', get_debug_type($object), $context[self::CIRCULAR_REFERENCE_LIMIT] ?? $this->defaultContext[self::CIRCULAR_REFERENCE_LIMIT])); + throw $exception; } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index be04ddea770b1..864da0b06bf31 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -29,6 +29,8 @@ use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; use Symfony\Component\Serializer\Result\DenormalizationResult; +use Symfony\Component\Serializer\Result\NormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; /** * Base class for a normalizer dealing with objects. @@ -209,6 +211,10 @@ public function normalize($object, string $format = null, array $context = []) return new \ArrayObject(); } + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($data); + } + return $data; } @@ -354,7 +360,7 @@ public function denormalize($data, string $type, string $format = null, array $c } } - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { if (!empty($extraAttributes)) { $message = (new ExtraAttributesException($extraAttributes))->getMessage(); @@ -533,7 +539,7 @@ private function validateAndDenormalize(string $currentClass, string $attribute, private function denormalizationSuccess($denormalizedValue, array $context) { - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { return DenormalizationResult::success($denormalizedValue); } @@ -542,7 +548,7 @@ private function denormalizationSuccess($denormalizedValue, array $context) private function denormalizationFailure($normalizedValue, string $message, array $context) { - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { $violation = new InvariantViolation($normalizedValue, $message); return DenormalizationResult::failure(['' => [$violation]]); diff --git a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php index 3772077a0c504..dbd5fb1107b40 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php @@ -51,7 +51,7 @@ public function denormalize($data, string $type, string $format = null, array $c $type = substr($type, 0, -2); $invariantViolations = []; - $collectInvariantViolations = $context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false; + $collectInvariantViolations = $context[SerializerInterface::RETURN_RESULT] ?? false; $builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null; foreach ($data as $key => $value) { diff --git a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php index 2546ffd0c69d5..0d6a6435839ae 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ConstraintViolationListNormalizer.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Serializer\Normalizer; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; +use Symfony\Component\Serializer\Result\NormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Validator\ConstraintViolationListInterface; /** @@ -41,8 +43,6 @@ public function __construct($defaultContext = [], NameConverterInterface $nameCo /** * {@inheritdoc} - * - * @return array */ public function normalize($object, string $format = null, array $context = []) { @@ -103,7 +103,13 @@ public function normalize($object, string $format = null, array $context = []) $result['instance'] = $instance; } - return $result + ['violations' => $violations]; + $result += ['violations' => $violations]; + + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($result); + } + + return $result; } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php index 6bf6339372d5f..56182705772c2 100644 --- a/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/CustomNormalizer.php @@ -11,8 +11,10 @@ namespace Symfony\Component\Serializer\Normalizer; +use Symfony\Component\Serializer\Result\NormalizationResult; use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Serializer\SerializerAwareTrait; +use Symfony\Component\Serializer\SerializerInterface; /** * @author Jordi Boggiano @@ -27,7 +29,13 @@ class CustomNormalizer implements NormalizerInterface, DenormalizerInterface, Se */ public function normalize($object, string $format = null, array $context = []) { - return $object->normalize($this->serializer, $format, $context); + $result = $object->normalize($this->serializer, $format, $context); + + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($result); + } + + return $result; } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php index a9d4aae3971b0..370a7528cfa60 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php @@ -18,6 +18,8 @@ use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\InvariantViolation; use Symfony\Component\Serializer\Result\DenormalizationResult; +use Symfony\Component\Serializer\Result\NormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; /** * Normalizes an {@see \SplFileInfo} object to a data URI. @@ -69,10 +71,16 @@ public function normalize($object, string $format = null, array $context = []) } if ('text' === explode('/', $mimeType, 2)[0]) { - return sprintf('data:%s,%s', $mimeType, rawurlencode($data)); + $result = sprintf('data:%s,%s', $mimeType, rawurlencode($data)); + } else { + $result = sprintf('data:%s;base64,%s', $mimeType, base64_encode($data)); } - return sprintf('data:%s;base64,%s', $mimeType, base64_encode($data)); + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($result); + } + + return $result; } /** @@ -100,7 +108,7 @@ public function denormalize($data, string $type, string $format = null, array $c try { $result = $this->doDenormalize($data, $type); } catch (NotNormalizableValueException $exception) { - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { $violation = new InvariantViolation($data, $exception->getMessage(), $exception); return DenormalizationResult::failure(['' => [$violation]]); @@ -109,7 +117,7 @@ public function denormalize($data, string $type, string $format = null, array $c throw $exception; } - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { return DenormalizationResult::success($result); } diff --git a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php index d3c4b98f39192..ea8caa58e192f 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateIntervalNormalizer.php @@ -15,6 +15,8 @@ use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\InvariantViolation; use Symfony\Component\Serializer\Result\DenormalizationResult; +use Symfony\Component\Serializer\Result\NormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; /** * Normalizes an instance of {@see \DateInterval} to an interval string. @@ -48,7 +50,13 @@ public function normalize($object, string $format = null, array $context = []) throw new InvalidArgumentException('The object must be an instance of "\DateInterval".'); } - return $object->format($context[self::FORMAT_KEY] ?? $this->defaultContext[self::FORMAT_KEY]); + $result = $object->format($context[self::FORMAT_KEY] ?? $this->defaultContext[self::FORMAT_KEY]); + + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($result); + } + + return $result; } /** @@ -80,7 +88,7 @@ public function denormalize($data, string $type, string $format = null, array $c try { $result = $this->doDenormalize($data, $context); } catch (InvalidArgumentException | UnexpectedValueException $exception) { - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { $violation = new InvariantViolation($data, $exception->getMessage(), $exception); return DenormalizationResult::failure(['' => [$violation]]); @@ -89,7 +97,7 @@ public function denormalize($data, string $type, string $format = null, array $c throw $exception; } - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { return DenormalizationResult::success($result); } diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index 040cb8a216a86..5fe5c7ef3929a 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -15,6 +15,8 @@ use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\InvariantViolation; use Symfony\Component\Serializer\Result\DenormalizationResult; +use Symfony\Component\Serializer\Result\NormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; /** * Normalizes an object implementing the {@see \DateTimeInterface} to a date string. @@ -47,8 +49,6 @@ public function __construct(array $defaultContext = []) * {@inheritdoc} * * @throws InvalidArgumentException - * - * @return string */ public function normalize($object, string $format = null, array $context = []) { @@ -64,7 +64,13 @@ public function normalize($object, string $format = null, array $context = []) $object = $object->setTimezone($timezone); } - return $object->format($dateTimeFormat); + $result = $object->format($dateTimeFormat); + + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($result); + } + + return $result; } /** @@ -87,7 +93,7 @@ public function denormalize($data, string $type, string $format = null, array $c try { $result = $this->doDenormalize($data, $type, $context); } catch (NotNormalizableValueException $exception) { - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { $violation = new InvariantViolation($data, $exception->getMessage(), $exception); return DenormalizationResult::failure(['' => [$violation]]); @@ -96,7 +102,7 @@ public function denormalize($data, string $type, string $format = null, array $c throw $exception; } - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { return DenormalizationResult::success($result); } diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php index af262ebaad70e..ea20948845fdc 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeZoneNormalizer.php @@ -13,6 +13,8 @@ use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; +use Symfony\Component\Serializer\Result\NormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; /** * Normalizes a {@see \DateTimeZone} object to a timezone string. @@ -34,7 +36,13 @@ public function normalize($object, string $format = null, array $context = []) throw new InvalidArgumentException('The object must be an instance of "\DateTimeZone".'); } - return $object->getName(); + $result = $object->getName(); + + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($result); + } + + return $result; } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php index df38c48b1cbb7..05845d2e69f34 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php @@ -24,13 +24,11 @@ */ interface DenormalizerInterface { - const COLLECT_INVARIANT_VIOLATIONS = 'collect_invariant_violations'; - /** * Denormalizes data back into an object of the given class. * - * When context option `collect_invariant_violations` is enabled, the - * denormalizer must always return an instance of + * When context option `return_result` is enabled, the denormalizer must + * always return an instance of * {@see \Symfony\Component\Serializer\Result\DenormalizationResult}. * * @param mixed $data Data to restore diff --git a/src/Symfony/Component/Serializer/Normalizer/FormErrorNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/FormErrorNormalizer.php index 48399f4e6c068..3492def115842 100644 --- a/src/Symfony/Component/Serializer/Normalizer/FormErrorNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/FormErrorNormalizer.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Serializer\Normalizer; use Symfony\Component\Form\FormInterface; +use Symfony\Component\Serializer\Result\NormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; /** * Normalizes invalid Form instances. @@ -25,7 +27,7 @@ final class FormErrorNormalizer implements NormalizerInterface, CacheableSupport /** * {@inheritdoc} */ - public function normalize($object, $format = null, array $context = []): array + public function normalize($object, $format = null, array $context = []) { $data = [ 'title' => $context[self::TITLE] ?? 'Validation Failed', @@ -38,6 +40,10 @@ public function normalize($object, $format = null, array $context = []): array $data['children'] = $this->convertFormChildrenToArray($object); } + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($data); + } + return $data; } diff --git a/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php index f38956c3e3cab..7c776f2722270 100644 --- a/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/JsonSerializableNormalizer.php @@ -13,6 +13,8 @@ use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\LogicException; +use Symfony\Component\Serializer\Result\NormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; /** * A normalizer that uses an objects own JsonSerializable implementation. @@ -38,7 +40,13 @@ public function normalize($object, string $format = null, array $context = []) throw new LogicException('Cannot normalize object because injected serializer is not a normalizer.'); } - return $this->serializer->normalize($object->jsonSerialize(), $format, $context); + $result = $this->serializer->normalize($object->jsonSerialize(), $format, $context); + + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($result); + } + + return $result; } /** diff --git a/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php index 752af1b50deaa..b41d1e60f0367 100644 --- a/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/MimeMessageNormalizer.php @@ -18,6 +18,7 @@ use Symfony\Component\Mime\Message; use Symfony\Component\Mime\Part\AbstractPart; use Symfony\Component\Serializer\Result\DenormalizationResult; +use Symfony\Component\Serializer\Result\NormalizationResult; use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Serializer\SerializerInterface; @@ -61,6 +62,10 @@ public function normalize($object, ?string $format = null, array $context = []) $ret[$name] = $this->serializer->normalize($header, $format, $context); } + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($ret); + } + return $ret; } @@ -68,10 +73,20 @@ public function normalize($object, ?string $format = null, array $context = []) $ret = $this->normalizer->normalize($object, $format, $context); $ret['class'] = \get_class($object); + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($ret); + } + return $ret; } - return $this->normalizer->normalize($object, $format, $context); + $ret = $this->normalizer->normalize($object, $format, $context); + + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($ret); + } + + return $ret; } /** @@ -107,7 +122,7 @@ public function denormalize($data, string $type, ?string $format = null, array $ $headers = new Headers(...$ret); - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { return DenormalizationResult::success($headers); } diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php index 653f949548c41..df0ef7b656f5f 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php @@ -15,6 +15,7 @@ use Symfony\Component\Serializer\Exception\ExceptionInterface; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\LogicException; +use Symfony\Component\Serializer\Result\NormalizationResult; /** * @author Jordi Boggiano @@ -24,11 +25,14 @@ interface NormalizerInterface /** * Normalizes an object into a set of arrays/scalars. * + * When context option `return_result` is enabled, the normalizer must + * always return an instance of {@see NormalizationResult}. + * * @param mixed $object Object to normalize * @param string $format Format the normalization result will be encoded as * @param array $context Context options for the normalizer * - * @return array|string|int|float|bool|\ArrayObject|null \ArrayObject is used to make sure an empty object is encoded as an object not an array + * @return NormalizationResult|array|string|int|float|bool|\ArrayObject|null \ArrayObject is used to make sure an empty object is encoded as an object not an array * * @throws InvalidArgumentException Occurs when the object given is not a supported type for the normalizer * @throws CircularReferenceException Occurs when the normalizer detects a circular reference when no circular diff --git a/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php index 6fdd2773a3608..f2bdfd6f0359d 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ProblemNormalizer.php @@ -13,6 +13,8 @@ use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\Serializer\Exception\InvalidArgumentException; +use Symfony\Component\Serializer\Result\NormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; /** * Normalizes errors according to the API Problem spec (RFC 7807). @@ -61,6 +63,10 @@ public function normalize($object, string $format = null, array $context = []) $data['trace'] = $object->getTrace(); } + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($data); + } + return $data; } diff --git a/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php index f0b9338e5c234..3e31fae064bfc 100644 --- a/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/UidNormalizer.php @@ -15,6 +15,8 @@ use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\InvariantViolation; use Symfony\Component\Serializer\Result\DenormalizationResult; +use Symfony\Component\Serializer\Result\NormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Uid\AbstractUid; use Symfony\Component\Uid\Ulid; use Symfony\Component\Uid\Uuid; @@ -43,6 +45,17 @@ public function __construct(array $defaultContext = []) * @param AbstractUid $object */ public function normalize($object, string $format = null, array $context = []) + { + $result = $this->doNormalize($object, $context); + + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($result); + } + + return $result; + } + + public function doNormalize(AbstractUid $object, array $context): string { switch ($context[self::NORMALIZATION_FORMAT_KEY] ?? $this->defaultContext[self::NORMALIZATION_FORMAT_KEY]) { case self::NORMALIZATION_FORMAT_CANONICAL: @@ -76,7 +89,7 @@ public function denormalize($data, string $type, string $format = null, array $c } catch (\InvalidArgumentException $exception) { $message = sprintf('The data is not a valid "%s" string representation.', $type); - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { $violation = new InvariantViolation($data, $message, $exception); return DenormalizationResult::failure(['' => [$violation]]); @@ -85,7 +98,7 @@ public function denormalize($data, string $type, string $format = null, array $c throw new NotNormalizableValueException($message); } - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { return DenormalizationResult::success($result); } diff --git a/src/Symfony/Component/Serializer/Result/NormalizationResult.php b/src/Symfony/Component/Serializer/Result/NormalizationResult.php new file mode 100644 index 0000000000000..b7c44cab38071 --- /dev/null +++ b/src/Symfony/Component/Serializer/Result/NormalizationResult.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Result; + +use Throwable; + +final class NormalizationResult +{ + private $normalizedValue; + private $exceptions = []; + + private function __construct() + { + } + + public static function success($normalizedValue): self + { + $result = new self(); + $result->normalizedValue = $normalizedValue; + + return $result; + } + + /** + * @param array $exceptions + */ + public static function failure(array $exceptions, $partiallyNormalizedValue = null): self + { + $result = new self(); + $result->exceptions = $exceptions; + $result->normalizedValue = $partiallyNormalizedValue; + + return $result; + } + + public function isSucessful(): bool + { + return [] === $this->exceptions; + } + + public function getNormalizedValue() + { + return $this->normalizedValue; + } + + public function getExceptions(): array + { + return $this->exceptions; + } + + /** + * @return array + */ + public function getExceptionsNestedIn(string $parentPath): array + { + if ('' === $parentPath) { + throw new \InvalidArgumentException('Parent path cannot be empty.'); + } + + $nestedExceptions = []; + + foreach ($this->exceptions as $path => $exception) { + $path = '' !== $path ? "{$parentPath}.{$path}" : $parentPath; + + $nestedExceptions[$path] = $exception; + } + + return $nestedExceptions; + } + + /** + * @return array + */ + public function getExceptionMessages(): array + { + $messages = []; + + foreach ($this->exceptions as $path => $exception) { + $messages[$path] = $exception->getMessage(); + } + + return $messages; + } +} diff --git a/src/Symfony/Component/Serializer/Serializer.php b/src/Symfony/Component/Serializer/Serializer.php index 264c956b832ac..ff04b02913b9b 100644 --- a/src/Symfony/Component/Serializer/Serializer.php +++ b/src/Symfony/Component/Serializer/Serializer.php @@ -30,6 +30,7 @@ use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Result\DenormalizationResult; +use Symfony\Component\Serializer\Result\NormalizationResult; /** * Serializer serializes and deserializes data. @@ -152,16 +153,19 @@ public function normalize($data, string $format = null, array $context = []) { // If a normalizer supports the given data, use it if ($normalizer = $this->getNormalizer($data, $format, $context)) { - return $normalizer->normalize($data, $format, $context); + return $this->normalizationSuccess( + $normalizer->normalize($data, $format, $context), + $context + ); } if (null === $data || is_scalar($data)) { - return $data; + return $this->normalizationSuccess($data, $context); } if (\is_array($data) || $data instanceof \Traversable) { if (($context[AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS] ?? false) === true && $data instanceof \Countable && 0 === $data->count()) { - return $data; + return $this->normalizationSuccess($data, $context); } $normalized = []; @@ -169,7 +173,7 @@ public function normalize($data, string $format = null, array $context = []) $normalized[$key] = $this->normalize($val, $format, $context); } - return $normalized; + return $this->normalizationSuccess($normalized, $context); } if (\is_object($data)) { @@ -197,7 +201,7 @@ public function denormalize($data, string $type, string $format = null, array $c if (!('is_'.$type)($data)) { $message = sprintf('Data expected to be of type "%s" ("%s" given).', $type, get_debug_type($data)); - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[self::RETURN_RESULT] ?? false) { $violation = new InvariantViolation($data, $message); return DenormalizationResult::failure(['' => [$violation]]); @@ -206,7 +210,7 @@ public function denormalize($data, string $type, string $format = null, array $c throw new NotNormalizableValueException($message); } - if ($context[self::COLLECT_INVARIANT_VIOLATIONS] ?? false) { + if ($context[self::RETURN_RESULT] ?? false) { return DenormalizationResult::success($data); } @@ -315,6 +319,15 @@ private function getDenormalizer($data, string $class, ?string $format, array $c return null; } + private function normalizationSuccess($result, array $context) + { + if ($context[SerializerInterface::RETURN_RESULT] ?? false) { + return NormalizationResult::success($result); + } + + return $result; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Serializer/SerializerInterface.php b/src/Symfony/Component/Serializer/SerializerInterface.php index 041368e3b3e25..39d669938bb2a 100644 --- a/src/Symfony/Component/Serializer/SerializerInterface.php +++ b/src/Symfony/Component/Serializer/SerializerInterface.php @@ -16,9 +16,15 @@ */ interface SerializerInterface { + const RETURN_RESULT = 'return_result'; + /** * Serializes data in the appropriate format. * + * When context option `return_result` is enabled, the serializer must + * always return an instance of + * {@see \Symfony\Component\Serializer\Result\NormalizationResult}. + * * @param mixed $data Any data * @param string $format Format name * @param array $context Options normalizers/encoders have access to @@ -30,8 +36,8 @@ public function serialize($data, string $format, array $context = []); /** * Deserializes data into the given type. * - * When context option `collect_invariant_violations` is enabled, the - * serializer must always return an instance of + * When context option `return_result` is enabled, the serializer must + * always return an instance of * {@see \Symfony\Component\Serializer\Result\DenormalizationResult}. * * @param mixed $data diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php index 09697dca24eea..f4de12059aeb9 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DataUriNormalizerTest.php @@ -16,6 +16,7 @@ use Symfony\Component\Serializer\Normalizer\DataUriNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Result\DenormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; /** * @author Kévin Dunglas @@ -177,7 +178,7 @@ public function validUriProvider() public function testItDenormalizesAndReturnsSuccessResult($uri): void { $result = $this->normalizer->denormalize($uri, \SplFileObject::class, null, [ - DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + SerializerInterface::RETURN_RESULT => true, ]); self::assertInstanceOf(DenormalizationResult::class, $result); @@ -188,7 +189,7 @@ public function testItDenormalizesAndReturnsSuccessResult($uri): void public function testItDenormalizesAndReturnsFailureResult(): void { $result = $this->normalizer->denormalize('not-a-uri', \SplFileObject::class, null, [ - DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + SerializerInterface::RETURN_RESULT => true, ]); self::assertInstanceOf(DenormalizationResult::class, $result); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php index 9f8c252163ea2..de40035e49131 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Result\DenormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; /** * @author Kévin Dunglas @@ -265,7 +266,7 @@ public function testDenormalizeFormatMismatchThrowsException() public function testItDenormalizesAndReturnsSuccessResult(): void { $result = $this->normalizer->denormalize('2020-01-01', \DateTimeInterface::class, null, [ - DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + SerializerInterface::RETURN_RESULT => true, ]); self::assertInstanceOf(DenormalizationResult::class, $result); @@ -276,7 +277,7 @@ public function testItDenormalizesAndReturnsSuccessResult(): void public function testItDenormalizesAndReturnsFailureResult(): void { $result = $this->normalizer->denormalize('not-a-date', \DateTimeInterface::class, null, [ - DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + SerializerInterface::RETURN_RESULT => true, ]); self::assertInstanceOf(DenormalizationResult::class, $result); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php index e59be25bee1d4..c671a22bf2e75 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/UidNormalizerTest.php @@ -7,6 +7,7 @@ use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; use Symfony\Component\Serializer\Normalizer\UidNormalizer; use Symfony\Component\Serializer\Result\DenormalizationResult; +use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Uid\AbstractUid; use Symfony\Component\Uid\Ulid; use Symfony\Component\Uid\Uuid; @@ -177,7 +178,7 @@ public function testNormalizeWithNormalizationFormatNotValid() public function testItDenormalizesAndReturnsSuccessResult(string $uuidString, string $class): void { $result = $this->normalizer->denormalize($uuidString, $class, null, [ - DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + SerializerInterface::RETURN_RESULT => true, ]); self::assertInstanceOf(DenormalizationResult::class, $result); @@ -194,7 +195,7 @@ public function testItDenormalizesAndReturnsSuccessResult(string $uuidString, st public function testItDenormalizesAndReturnsFailureResult(string $class): void { $result = $this->normalizer->denormalize('not-an-uuid', $class, null, [ - DenormalizerInterface::COLLECT_INVARIANT_VIOLATIONS => true, + SerializerInterface::RETURN_RESULT => true, ]); self::assertInstanceOf(DenormalizationResult::class, $result); diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 70f1a1bcef949..0384d134989f5 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -42,6 +42,7 @@ use Symfony\Component\Serializer\Normalizer\UnwrappingDenormalizer; use Symfony\Component\Serializer\Result\DenormalizationResult; use Symfony\Component\Serializer\Serializer; +use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummy; use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummyFirstChild; use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummySecondChild; @@ -663,7 +664,7 @@ public function testDenormalizationSuccessResult(string $type, $normalizedData, $json = json_encode($normalizedData); $result = $serializer->deserialize($json, $type, 'json', [ - Serializer::COLLECT_INVARIANT_VIOLATIONS => true, + SerializerInterface::RETURN_RESULT => true, ]); self::assertInstanceOf(DenormalizationResult::class, $result); @@ -717,7 +718,7 @@ public function testDenormalizationFailureResult(string $type, $normalizedData, $json = json_encode($normalizedData); $result = $serializer->deserialize($json, $type, 'json', [ - Serializer::COLLECT_INVARIANT_VIOLATIONS => true, + SerializerInterface::RETURN_RESULT => true, ]); self::assertInstanceOf(DenormalizationResult::class, $result); @@ -779,7 +780,7 @@ public function testDenormalizationFailureResultWithUnwrapping() ]); $result = $serializer->deserialize($json, Dto::class, 'json', [ - Serializer::COLLECT_INVARIANT_VIOLATIONS => true, + SerializerInterface::RETURN_RESULT => true, UnwrappingDenormalizer::UNWRAP_PATH => '[wrapped][data]', ]); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy