diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index bc64490ca79cf..2c38248e5f6f2 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -13,6 +13,7 @@ use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; +use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Serializer\Encoder\CsvEncoder; @@ -58,6 +59,12 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer */ public const SKIP_NULL_VALUES = 'skip_null_values'; + /** + * Flag to control whether uninitialized PHP>=7.4 typed class properties + * should be excluded when normalizing. + */ + public const SKIP_UNINITIALIZED_VALUES = 'skip_uninitialized_values'; + /** * Callback to allow to set a value for an attribute when the max depth has * been reached. @@ -180,7 +187,16 @@ public function normalize($object, string $format = null, array $context = []) } $attributeContext = $this->getAttributeNormalizationContext($object, $attribute, $context); - $attributeValue = $this->getAttributeValue($object, $attribute, $format, $attributeContext); + + try { + $attributeValue = $this->getAttributeValue($object, $attribute, $format, $attributeContext); + } catch (UninitializedPropertyException $e) { + if ($context[self::SKIP_UNINITIALIZED_VALUES] ?? $this->defaultContext[self::SKIP_UNINITIALIZED_VALUES] ?? false) { + continue; + } + throw $e; + } + if ($maxDepthReached) { $attributeValue = $maxDepthHandler($attributeValue, $object, $attribute, $format, $attributeContext); } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/Features/SkipUninitializedValuesTestTrait.php b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/SkipUninitializedValuesTestTrait.php new file mode 100644 index 0000000000000..66c5889db9a22 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/SkipUninitializedValuesTestTrait.php @@ -0,0 +1,38 @@ +getNormalizerForSkipUninitializedValues(); + $result = $normalizer->normalize($object, null, ['skip_uninitialized_values' => true, 'groups' => ['foo']]); + $this->assertSame(['initialized' => 'value'], $result); + } + + /** + * @requires PHP 7.4 + */ + public function testWithoutSkipUninitializedValues() + { + $object = new TypedPropertiesObject(); + + $normalizer = $this->getNormalizerForSkipUninitializedValues(); + $this->expectException(UninitializedPropertyException::class); + $normalizer->normalize($object, null, ['groups' => ['foo']]); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/Features/TypedPropertiesObject.php b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/TypedPropertiesObject.php new file mode 100644 index 0000000000000..d497b97d96337 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/Features/TypedPropertiesObject.php @@ -0,0 +1,23 @@ +