8000 Fix denormalizing empty string into object|null parameter · symfony/symfony@85ca4e0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 85ca4e0

Browse files
committed
Fix denormalizing empty string into object|null parameter
1 parent 5a062e9 commit 85ca4e0

File tree

3 files changed

+48
-50
lines changed

3 files changed

+48
-50
lines changed

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ abstract protected function extractAttributes(object $object, string $format = n
290290

291291
/**
292292
* Gets the attribute value.
293+
*
294+
* @return mixed
293295
*/
294296
abstract protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = []);
295297

@@ -303,6 +305,9 @@ public function supportsDenormalization(mixed $data, string $type, string $forma
303305
return class_exists($type) || (interface_exists($type, false) && null !== $this->classDiscriminatorResolver?->getMappingForClass($type));
304306
}
305307

308+
/**
309+
* @return mixed
310+
*/
306311
public function denormalize(mixed $data, string $type, string $format = null, array $context = [])
307312
{
308313
if (!isset($context['cache_key'])) {
@@ -429,16 +434,11 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
429434
$isUnionType = \count($types) > 1;
430435
$extraAttributesException = null;
431436
$missingConstructorArgumentsException = null;
432-
$hasNonObjectType = false;
433-
$isUnionTypeOrNullable = $isUnionType;
434-
435437
foreach ($types as $type) {
436438
if (null === $data && $type->isNullable()) {
437439
return null;
438440
}
439441

440-
$isUnionTypeOrNullable = $isUnionTypeOrNullable ?: $type->isNullable();
441-
$hasNonObjectType = $hasNonObjectType ?: Type::BUILTIN_TYPE_OBJECT !== $type->getBuiltinType();
442442
$collectionValueType = $type->isCollection() ? $type->getCollectionValueTypes()[0] ?? null : null;
443443

444444
// Fix a collection that contains the only one element
@@ -463,7 +463,11 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
463463
return [];
464464
}
465465

466-
if ($type->isNullable() && \in_array($builtinType, [Type::BUILTIN_TYPE_BOOL, Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE 8000 _FLOAT], true)) {
466+
if (Type::BUILTIN_TYPE_STRING === $builtinType) {
467+
return '';
468+
}
469+
470+
if ($type->isNullable()) {
467471
return null;
468472
}
469473
}
@@ -565,28 +569,24 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
565569
return $data;
566570
}
567571
} catch (NotNormalizableValueException|InvalidArgumentException $e) {
568-
if (!$isUnionTypeOrNullable) {
572+
if (!$isUnionType) {
569573
throw $e;
570574
}
571575
} catch (ExtraAttributesException $e) {
572-
if (!$isUnionTypeOrNullable) {
576+
if (!$isUnionType) {
573577
throw $e;
574578
}
575579

576580
$extraAttributesException ??= $e;
577581
} catch (MissingConstructorArgumentsException $e) {
578-
if (!$isUnionTypeOrNullable) {
582+
if (!$isUnionType) {
579583
throw $e;
580584
}
581585

582586
$missingConstructorArgumentsException ??= $e;
583587
}
584588
}
585589

586-
if ('' === $data && $isUnionTypeOrNullable && !$hasNonObjectType && (XmlEncoder::FORMAT === $format || CsvEncoder::FORMAT === $format)) {
587-
return null;
588-
}
589-
590590
if ($extraAttributesException) {
591591
throw $extraAttributesException;
592592
}
@@ -595,10 +595,6 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
595595
throw $missingConstructorArgumentsException;
596596
}
597597

598-
if (!$isUnionType && isset($e)) {
599-
throw $e;
600-
}
601-
602598
if ($context[self::DISABLE_TYPE_ENFORCEMENT] ?? $this->defaultContext[self::DISABLE_TYPE_ENFORCEMENT] ?? false) {
603599
return $data;
604600
}

src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Symfony\Component\Serializer\Exception\ExtraAttributesException;
2525
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
2626
use Symfony\Component\Serializer\Exception\LogicException;
27+
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;
2728
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
2829
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata;
2930
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping;
@@ -38,6 +39,7 @@
3839
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
3940
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
4041
use Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer;
42+
use Symfony\Component\Serializer\Normalizer\CustomNormalizer;
4143
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
4244
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
4345
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
@@ -49,6 +51,9 @@
4951
use Symfony\Component\Serializer\Tests\Fixtures\Annotations\AbstractDummySecondChild;
5052
use Symfony\Component\Serializer\Tests\Fixtures\DummyFirstChildQuux;
5153
use Symfony\Component\Serializer\Tests\Fixtures\DummySecondChildQuux;
54+
use Symfony\Component\Serializer\Tests\Fixtures\DummyWithNotNormalizable;
55+
use Symfony\Component\Serializer\Tests\Fixtures\DummyWithObjectOrBool;
56+
use Symfony\Component\Serializer\Tests\Fixtures\DummyWithObjectOrNull;
5257
use Symfony\Component\Serializer\Tests\Normalizer\Features\ObjectDummyWithContextAttribute;
5358

5459
class AbstractObjectNormalizerTest extends TestCase
@@ -835,6 +840,36 @@ public function testDenormalizeWithCorrectOrderOfAttributeAndProperty()
835840
$test = $normalizer->denormalize($data, $obj::class);
836841
$this->assertSame('nested-id', $test->id);
837842
}
843+
844+
public function testDenormalizeUntypedFormat()
845+
{
846+
$serializer = new Serializer([new ObjectNormalizer(propertyTypeExtractor: new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]))]);
847+
$actual = $serializer->denormalize(['value' => ''], DummyWithObjectOrNull::class, 'xml');
848+
849+
$this->assertEquals(new DummyWithObjectOrNull(null), $actual);
850+
}
851+
852+
public function testDenormalizeUntypedFormatNotNormalizable()
853+
{
854+
$this->expectException(NotNormalizableValueException::class);
855+
$serializer = new Serializer([new CustomNormalizer(), new ObjectNormalizer(propertyTypeExtractor: new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]))]);
856+
$serializer->denormalize(['value' => 'test'], DummyWithNotNormalizable::class, 'xml');
857+
}
858+
859+
public function testDenormalizeUntypedFormatMissingArg()
860+
{
861+
$this->expectException(MissingConstructorArgumentsException::class);
862+
$serializer = new Serializer([new ObjectNormalizer(propertyTypeExtractor: new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]))]);
863+
$serializer->denormalize(['value' => 'invalid'], DummyWithObjectOrNull::class, 'xml');
864+
}
865+
866+
public function testDenormalizeUntypedFormatScalar()
867+
{
868+
$serializer = new Serializer([new ObjectNormalizer(propertyTypeExtractor: new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]))]);
869+
$actual = $serializer->denormalize(['value' => 'false'], DummyWithObjectOrBool::class, 'xml');
870+
871+
$this->assertEquals(new DummyWithObjectOrBool(false), $actual);
872+
}
838873
}
839874

840875
class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer

src/Symfony/Component/Serializer/Tests/SerializerTest.php

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
use Symfony\Component\Serializer\Exception\ExtraAttributesException;
2323
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
2424
use Symfony\Component\Serializer\Exception\LogicException;
25-
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;
2625
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
2726
use Symfony\Component\Serializer\Exception\PartialDenormalizationException;
2827
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
@@ -60,8 +59,6 @@
6059
use Symfony\Component\Serializer\Tests\Fixtures\DummyMessageNumberTwo;
6160
use Symfony\Component\Serializer\Tests\Fixtures\DummyObjectWithEnumConstructor;
6261
use Symfony\Component\Serializer\Tests\Fixtures\DummyObjectWithEnumProperty;
63-
use Symfony\Component\Serializer\Tests\Fixtures\DummyWithNotNormalizable;
64-
use Symfony\Component\Serializer\Tests\Fixtures\DummyWithObjectOrBool;
6562
use Symfony\Component\Serializer\Tests\Fixtures\DummyWithObjectOrNull;
6663
use Symfony\Component\Serializer\Tests\Fixtures\FalseBuiltInDummy;
6764
use Symfony\Component\Serializer\Tests\Fixtures\FooImplementationDummy;
@@ -856,36 +853,6 @@ public function testDeserializeUntypedFormat()
856853
$this->assertEquals(new DummyWithObjectOrNull(null), $actual);
857854
}
858855

859-
public function testDenormalizeUntypedFormat()
860-
{
861-
$serializer = new Serializer([new ObjectNormalizer(propertyTypeExtractor: new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]))]);
862-
$actual = $serializer->denormalize(['value' => ''], DummyWithObjectOrNull::class, 'xml');
863-
864-
$this->assertEquals(new DummyWithObjectOrNull(null), $actual);
865-
}
866-
867-
public function testDenormalizeUntypedFormatNotNormalizable()
868-
{
869-
$this->expectException(NotNormalizableValueException::class);
870-
$serializer = new Serializer([new CustomNormalizer(), new ObjectNormalizer(propertyTypeExtractor: new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]))]);
871-
$serializer->denormalize(['value' => 'test'], DummyWithNotNormalizable::class, 'xml');
872-
}
873-
874-
public function testDenormalizeUntypedFormatMissingArg()
875-
{
876-
$this->expectException(MissingConstructorArgumentsException::class);
877-
$serializer = new Serializer([new ObjectNormalizer(propertyTypeExtractor: new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]))]);
878-
$serializer->denormalize(['value' => 'invalid'], DummyWithObjectOrNull::class, 'xml');
879-
}
880-
881-
public function testDenormalizeUntypedFormatScalar()
882-
{
883-
$serializer = new Serializer([new ObjectNormalizer(propertyTypeExtractor: new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]))]);
884-
$actual = $serializer->denormalize(['value' => 'false'], DummyWithObjectOrBool::class, 'xml');
885-
886-
$this->assertEquals(new DummyWithObjectOrBool(false), $actual);
887-
}
888-
889856
private function serializerWithClassDiscriminator()
890857
{
891858
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));

0 commit comments

Comments
 (0)
0