diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index f07be74c2e8ab..b46ea1a2676a5 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -408,16 +408,22 @@ protected function instantiateObject(array &$data, string $class, array &$contex continue; } - $constructorParameterType = 'unknown'; + $constructorParameterTypes = []; $reflectionType = $constructorParameter->getType(); - if ($reflectionType instanceof \ReflectionNamedType) { - $constructorParameterType = $reflectionType->getName(); + if ($reflectionType instanceof \ReflectionUnionType) { + foreach ($reflectionType->getTypes() as $reflectionType) { + $constructorParameterTypes[] = (string) $reflectionType; + } + } elseif ($reflectionType instanceof \ReflectionType) { + $constructorParameterTypes[] = (string) $reflectionType; + } else { + $constructorParameterTypes[] = 'unknown'; } $exception = NotNormalizableValueException::createForUnexpectedDataType( \sprintf('Failed to create object because the class misses the "%s" property.', $constructorParameter->name), null, - [$constructorParameterType], + $constructorParameterTypes, $attributeContext['deserialization_path'] ?? null, true ); diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/DummyWithUnion.php b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyWithUnion.php new file mode 100644 index 0000000000000..c40bc1867248b --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/DummyWithUnion.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Fixtures; + +/** + * @author Dmitrii + */ +class DummyWithUnion +{ + public function __construct( + public int|float $value, + public string|int $value2, + ) { + } +} diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index db16e81db0faa..b1b56e5c07aff 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -62,6 +62,7 @@ use Symfony\Component\Serializer\Tests\Fixtures\DummyObjectWithEnumConstructor; use Symfony\Component\Serializer\Tests\Fixtures\DummyObjectWithEnumProperty; use Symfony\Component\Serializer\Tests\Fixtures\DummyWithObjectOrNull; +use Symfony\Component\Serializer\Tests\Fixtures\DummyWithUnion; use Symfony\Component\Serializer\Tests\Fixtures\DummyWithVariadicParameter; use Symfony\Component\Serializer\Tests\Fixtures\FalseBuiltInDummy; use Symfony\Component\Serializer\Tests\Fixtures\FooImplementationDummy; @@ -1392,6 +1393,60 @@ public function testCollectDenormalizationErrorsWithInvalidConstructorTypes() $this->assertSame($expected, $exceptionsAsArray); } + public function testCollectDenormalizationErrorsWithUnionConstructorTypes() + { + $json = '{}'; + + $serializer = new Serializer( + [new ObjectNormalizer()], + ['json' => new JsonEncoder()] + ); + + try { + $serializer->deserialize( + $json, + DummyWithUnion::class, + 'json', + [DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS => true] + ); + + $this->fail(); + } catch (\Throwable $th) { + $this->assertInstanceOf(PartialDenormalizationException::class, $th); + } + + $exceptionsAsArray = array_map(fn (NotNormalizableValueException $e): array => [ + 'currentType' => $e->getCurrentType(), + 'expectedTypes' => $e->getExpectedTypes(), + 'path' => $e->getPath(), + 'useMessageForUser' => $e->canUseMessageForUser(), + 'message' => $e->getMessage(), + ], $th->getErrors()); + + $expected = [ + [ + 'currentType' => 'null', + 'expectedTypes' => [ + 'int', 'float', + ], + 'path' => 'value', + 'useMessageForUser' => true, + 'message' => 'Failed to create object because the class misses the "value" property.', + ], + [ + 'currentType' => 'null', + 'expectedTypes' => [ + 'string', 'int', + ], + 'path' => 'value2', + 'useMessageForUser' => true, + 'message' => 'Failed to create object because the class misses the "value2" property.', + ], + ]; + + $this->assertSame($expected, $exceptionsAsArray); + } + public function testCollectDenormalizationErrorsWithEnumConstructor() { $serializer = new Serializer(