diff --git a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php index cbce0c529dac9..71df2e6bdfb7c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DateTimeNormalizer.php @@ -91,34 +91,36 @@ public function denormalize($data, string $type, string $format = null, array $c $dateTimeFormat = $context[self::FORMAT_KEY] ?? null; $timezone = $this->getTimezone($context); - if (null === $data || (\is_string($data) && '' === trim($data))) { - throw NotNormalizableValueException::createForUnexpectedDataType('The data is either an empty string or null, you should pass a string that can be parsed with the passed format or a valid DateTime string.', $data, [Type::BUILTIN_TYPE_STRING], $context['deserialization_path'] ?? null, true); + if (null === $data || !\is_string($data) || '' === trim($data)) { + throw NotNormalizableValueException::createForUnexpectedDataType('The data is either not an string, an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.', $data, [Type::BUILTIN_TYPE_STRING], $context['deserialization_path'] ?? null, true); } - if (null !== $dateTimeFormat) { - $object = \DateTime::class === $type ? \DateTime::createFromFormat($dateTimeFormat, $data, $timezone) : \DateTimeImmutable::createFromFormat($dateTimeFormat, $data, $timezone); + try { + if (null !== $dateTimeFormat) { + $object = \DateTime::class === $type ? \DateTime::createFromFormat($dateTimeFormat, $data, $timezone) : \DateTimeImmutable::createFromFormat($dateTimeFormat, $data, $timezone); - if (false !== $object) { - return $object; - } + if (false !== $object) { + return $object; + } - $dateTimeErrors = \DateTime::class === $type ? \DateTime::getLastErrors() : \DateTimeImmutable::getLastErrors(); + $dateTimeErrors = \DateTime::class === $type ? \DateTime::getLastErrors() : \DateTimeImmutable::getLastErrors(); - throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('Parsing datetime string "%s" using format "%s" resulted in %d errors: ', $data, $dateTimeFormat, $dateTimeErrors['error_count'])."\n".implode("\n", $this->formatDateTimeErrors($dateTimeErrors['errors'])), $data, [Type::BUILTIN_TYPE_STRING], $context['deserialization_path'] ?? null, true); - } + throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('Parsing datetime string "%s" using format "%s" resulted in %d errors: ', $data, $dateTimeFormat, $dateTimeErrors['error_count'])."\n".implode("\n", $this->formatDateTimeErrors($dateTimeErrors['errors'])), $data, [Type::BUILTIN_TYPE_STRING], $context['deserialization_path'] ?? null, true); + } - $defaultDateTimeFormat = $this->defaultContext[self::FORMAT_KEY] ?? null; + $defaultDateTimeFormat = $this->defaultContext[self::FORMAT_KEY] ?? null; - if (null !== $defaultDateTimeFormat) { - $object = \DateTime::class === $type ? \DateTime::createFromFormat($defaultDateTimeFormat, $data, $timezone) : \DateTimeImmutable::createFromFormat($defaultDateTimeFormat, $data, $timezone); + if (null !== $defaultDateTimeFormat) { + $object = \DateTime::class === $type ? \DateTime::createFromFormat($defaultDateTimeFormat, $data, $timezone) : \DateTimeImmutable::createFromFormat($defaultDateTimeFormat, $data, $timezone); - if (false !== $object) { - return $object; + if (false !== $object) { + return $object; + } } - } - try { return \DateTime::class === $type ? new \DateTime($data, $timezone) : new \DateTimeImmutable($data, $timezone); + } catch (NotNormalizableValueException $e) { + throw $e; } catch (\Exception $e) { throw NotNormalizableValueException::createForUnexpectedDataType($e->getMessage(), $data, [Type::BUILTIN_TYPE_STRING], $context['deserialization_path'] ?? null, false, $e->getCode(), $e); } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php index 8f368deca68b3..674dfaab5382d 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php @@ -243,24 +243,31 @@ public function testDenormalizeInvalidDataThrowsException() $this->normalizer->denormalize('invalid date', \DateTimeInterface::class); } + public function testDenormalizeWrongTypeThrowsException() + { + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage('The data is either not an string, an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.'); + $this->normalizer->denormalize(['date' => '2023-03-03 00:00:00.000000', 'timezone_type' => 1, 'timezone' => '+01:00'], \DateTimeInterface::class); + } + public function testDenormalizeNullThrowsException() { $this->expectException(UnexpectedValueException::class); - $this->expectExceptionMessage('The data is either an empty string or null, you should pass a string that can be parsed with the passed format or a valid DateTime string.'); + $this->expectExceptionMessage('The data is either not an string, an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.'); $this->normalizer->denormalize(null, \DateTimeInterface::class); } public function testDenormalizeEmptyStringThrowsException() { $this->expectException(UnexpectedValueException::class); - $this->expectExceptionMessage('The data is either an empty string or null, you should pass a string that can be parsed with the passed format or a valid DateTime string.'); + $this->expectExceptionMessage('The data is either not an string, an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.'); $this->normalizer->denormalize('', \DateTimeInterface::class); } public function testDenormalizeStringWithSpacesOnlyThrowsAnException() { $this->expectException(UnexpectedValueException::class); - $this->expectExceptionMessage('The data is either an empty string or null, you should pass a string that can be parsed with the passed format or a valid DateTime string.'); + $this->expectExceptionMessage('The data is either not an string, an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.'); $this->normalizer->denormalize(' ', \DateTimeInterface::class); } diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 957330faa3e39..bf35b3f78eb16 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -940,7 +940,7 @@ public function testCollectDenormalizationErrors(?ClassMetadataFactory $classMet ], 'path' => 'dateTime', 'useMessageForUser' => true, - 'message' => 'The data is either an empty string or null, you should pass a string that can be parsed with the passed format or a valid DateTime string.', + 'message' => 'The data is either not an string, an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.', ], [ 'currentType' => 'null', @@ -949,7 +949,7 @@ public function testCollectDenormalizationErrors(?ClassMetadataFactory $classMet ], 'path' => 'dateTimeImmutable', 'useMessageForUser' => true, - 'message' => 'The data is either an empty string or null, you should pass a string that can be parsed with the passed format or a valid DateTime string.', + 'message' => 'The data is either not an string, an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.', ], [ 'currentType' => 'null',