Skip to content

Commit 0ed0115

Browse files
committed
Merge branch '7.2' into 7.3
* 7.2: [Serializer] Handle invalid mapping type property type [Config] Do not generate unreachable configuration paths [WebProfilerBundle] Fix missing indent on non php files opended in the profiler
2 parents 1b724a1 + 91b01d6 commit 0ed0115

File tree

4 files changed

+73
-1
lines changed

4 files changed

+73
-1
lines changed

Normalizer/AbstractObjectNormalizer.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,10 @@ private function getMappedClass(array $data, string $class, array $context): str
12081208
throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('Type property "%s" not found for the abstract object "%s".', $mapping->getTypeProperty(), $class), null, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), false);
12091209
}
12101210

1211+
if (!\is_string($type) && (!\is_object($type) || !method_exists($type, '__toString'))) {
1212+
throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type property "%s" for the abstract object "%s" must be a string or a stringable object.', $mapping->getTypeProperty(), $class), $type, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), false);
1213+
}
1214+
12111215
if (null === $mappedClass = $mapping->getClassForType($type)) {
12121216
throw NotNormalizableValueException::createForUnexpectedDataType(\sprintf('The type "%s" is not a valid value.', $type), $type, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), true);
12131217
}

Tests/Fixtures/Attributes/AbstractDummyFirstChild.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
class AbstractDummyFirstChild extends AbstractDummy
1717
{
1818
public $bar;
19+
public $baz;
1920

2021
/** @var DummyFirstChildQuux|null */
2122
public $quux;

Tests/Normalizer/AbstractObjectNormalizerTest.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,73 @@ private function getDenormalizerForStringCollection()
528528
return $denormalizer;
529529
}
530530

531+
/**
532+
* @dataProvider provideInvalidDiscriminatorTypes
533+
*/
534+
public function testDenormalizeWithDiscriminatorMapHandlesInvalidTypeValue(mixed $typeValue, bool $shouldFail)
535+
{
536+
if ($shouldFail) {
537+
$this->expectException(NotNormalizableValueException::class);
538+
$this->expectExceptionMessage(
539+
'The type property "type" for the abstract object "Symfony\Component\Serializer\Tests\Fixtures\Attributes\AbstractDummy" must be a string or a stringable object.'
540+
);
541+
}
542+
543+
$factory = new ClassMetadataFactory(new AttributeLoader());
544+
545+
$loaderMock = new class implements ClassMetadataFactoryInterface {
546+
public function getMetadataFor($value): ClassMetadataInterface
547+
{
548+
if (AbstractDummy::class === $value) {
549+
return new ClassMetadata(
550+
AbstractDummy::class,
551+
new ClassDiscriminatorMapping('type', [
552+
'first' => AbstractDummyFirstChild::class,
553+
'second' => AbstractDummySecondChild::class,
554+
])
555+
);
556+
}
557+
558+
throw new InvalidArgumentException();
559+
}
560+
561+
public function hasMetadataFor($value): bool
562+
{
563+
return AbstractDummy::class === $value;
564+
}
565+
};
566+
567+
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock);
568+
$normalizer = new AbstractObjectNormalizerDummy($factory, null, new ReflectionExtractor(), $discriminatorResolver);
569+
$serializer = new Serializer([$normalizer]);
570+
$normalizer->setSerializer($serializer);
571+
$normalizedData = $normalizer->denormalize(['foo' => 'foo', 'baz' => 'baz', 'quux' => ['value' => 'quux'], 'type' => $typeValue], AbstractDummy::class);
572+
573+
$this->assertInstanceOf(DummyFirstChildQuux::class, $normalizedData->quux);
574+
}
575+
576+
/**
577+
* @return iterable<array{0: mixed, 1: bool}>
578+
*/
579+
public static function provideInvalidDiscriminatorTypes(): array
580+
{
581+
$toStringObject = new class {
582+
public function __toString()
583+
{
584+
return 'first';
585+
}
586+
};
587+
588+
return [
589+
[[], true],
590+
[new \stdClass(), true],
591+
[123, true],
592+
[false, true],
593+
['first', false],
594+
[$toStringObject, false],
595+
];
596+
}
597+
531598
public function testDenormalizeWithDiscriminatorMapUsesCorrectClassname()
532599
{
533600
$factory = new ClassMetadataFactory(new AttributeLoader());

Tests/SerializerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ public function hasMetadataFor($value): bool
454454
$discriminatorResolver = new ClassDiscriminatorFromClassMetadata($loaderMock);
455455
$serializer = new Serializer([new ObjectNormalizer(null, null, null, new PhpDocExtractor(), $discriminatorResolver)], ['json' => new JsonEncoder()]);
456456

457-
$jsonData = '{"type":"first","quux":{"value":"quux"},"bar":"bar-value","foo":"foo-value"}';
457+
$jsonData = '{"type":"first","quux":{"value":"quux"},"bar":"bar-value","baz":null,"foo":"foo-value"}';
458458

459459
$deserialized = $serializer->deserialize($jsonData, AbstractDummy::class, 'json');
460460
$this->assertEquals($example, $deserialized);

0 commit comments

Comments
 (0)