From 5cb0dc3028e0f32888fed2d467b7762824fef843 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 27 Sep 2021 20:46:41 +0200 Subject: [PATCH 1/4] [Serializer] Fix denormalizing of array with empty body This happens for example with XML empty tags denormalizing to an object array attribute. --- Normalizer/AbstractObjectNormalizer.php | 5 +++++ Tests/Normalizer/Features/ObjectDummy.php | 3 +++ Tests/Normalizer/ObjectNormalizerTest.php | 13 +++++++++++++ 3 files changed, 21 insertions(+) diff --git a/Normalizer/AbstractObjectNormalizer.php b/Normalizer/AbstractObjectNormalizer.php index 527fa8e13..3c790d03d 100644 --- a/Normalizer/AbstractObjectNormalizer.php +++ b/Normalizer/AbstractObjectNormalizer.php @@ -16,6 +16,7 @@ use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Encoder\XmlEncoder; use Symfony\Component\Serializer\Exception\ExtraAttributesException; use Symfony\Component\Serializer\Exception\LogicException; use Symfony\Component\Serializer\Exception\NotNormalizableValueException; @@ -411,6 +412,10 @@ private function validateAndDenormalize(string $currentClass, string $attribute, $data = [$data]; } + if (XmlEncoder::FORMAT === $format && '' === $data && Type::BUILTIN_TYPE_ARRAY === $type->getBuiltinType()) { + return []; + } + if (null !== $collectionValueType && Type::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) { $builtinType = Type::BUILTIN_TYPE_OBJECT; $class = $collectionValueType->getClassName().'[]'; diff --git a/Tests/Normalizer/Features/ObjectDummy.php b/Tests/Normalizer/Features/ObjectDummy.php index e12772457..ac610f098 100644 --- a/Tests/Normalizer/Features/ObjectDummy.php +++ b/Tests/Normalizer/Features/ObjectDummy.php @@ -5,6 +5,9 @@ class ObjectDummy { protected $foo; + /** + * @var array + */ public $bar; private $baz; protected $camelCase; diff --git a/Tests/Normalizer/ObjectNormalizerTest.php b/Tests/Normalizer/ObjectNormalizerTest.php index 4d145a5c8..477027cef 100644 --- a/Tests/Normalizer/ObjectNormalizerTest.php +++ b/Tests/Normalizer/ObjectNormalizerTest.php @@ -165,6 +165,19 @@ public function testDenormalize() $this->assertTrue($obj->isBaz()); } + public function testDenormalizeEmptyXmlArray() + { + $normalizer = $this->getDenormalizerForObjectToPopulate(); + $obj = $normalizer->denormalize( + ['bar' => ''], + ObjectDummy::class, + 'xml' + ); + + $this->assertIsArray($obj->bar); + $this->assertEmpty($obj->bar); + } + public function testDenormalizeWithObject() { $data = new \stdClass(); From 36b95b004073cc176031c7cc22c9163cf03910a5 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 17 Nov 2021 16:11:44 +0100 Subject: [PATCH 2/4] [Serializer] fix support for unset properties on PHP < 7.4 --- Normalizer/ObjectNormalizer.php | 18 ++++++------------ Normalizer/PropertyNormalizer.php | 18 ++++++------------ Tests/Normalizer/ObjectNormalizerTest.php | 10 ++++++++++ Tests/Normalizer/PropertyNormalizerTest.php | 10 ++++++++++ 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/Normalizer/ObjectNormalizer.php b/Normalizer/ObjectNormalizer.php index b96ce3b1c..1a3ed992e 100644 --- a/Normalizer/ObjectNormalizer.php +++ b/Normalizer/ObjectNormalizer.php @@ -103,23 +103,17 @@ protected function extractAttributes($object, $format = null, array $context = [ } } - $checkPropertyInitialization = \PHP_VERSION_ID >= 70400; - // properties + $propertyValues = (array) $object; foreach ($reflClass->getProperties() as $reflProperty) { - $isPublic = $reflProperty->isPublic(); - - if ($checkPropertyInitialization) { - if (!$isPublic) { - $reflProperty->setAccessible(true); - } - if (!$reflProperty->isInitialized($object)) { + if (!\array_key_exists($reflProperty->name, $propertyValues)) { + if ($reflProperty->isPublic() + || ($reflProperty->isProtected() && !\array_key_exists("\0*\0{$reflProperty->name}", $propertyValues)) + || ($reflProperty->isPrivate() && !\array_key_exists("\0{$reflProperty->class}\0{$reflProperty->name}", $propertyValues)) + ) { unset($attributes[$reflProperty->name]); - continue; } - } - if (!$isPublic) { continue; } diff --git a/Normalizer/PropertyNormalizer.php b/Normalizer/PropertyNormalizer.php index 9e2a53216..83ec5325b 100644 --- a/Normalizer/PropertyNormalizer.php +++ b/Normalizer/PropertyNormalizer.php @@ -101,21 +101,15 @@ protected function extractAttributes($object, $format = null, array $context = [ { $reflectionObject = new \ReflectionObject($object); $attributes = []; - $checkPropertyInitialization = \PHP_VERSION_ID >= 70400; + $propertyValues = (array) $object; do { foreach ($reflectionObject->getProperties() as $property) { - if ($checkPropertyInitialization) { - if (!$property->isPublic()) { - $property->setAccessible(true); - } - - if (!$property->isInitialized($object)) { - continue; - } - } - - if (!$this->isAllowedAttribute($reflectionObject->getName(), $property->name, $format, $context)) { + if (($property->isPublic() && !\array_key_exists($property->name, $propertyValues)) + || ($property->isProtected() && !\array_key_exists("\0*\0{$property->name}", $propertyValues)) + || ($property->isPrivate() && !\array_key_exists("\0{$property->class}\0{$property->name}", $propertyValues)) + || !$this->isAllowedAttribute($reflectionObject->getName(), $property->name, $format, $context) + ) { continue; } diff --git a/Tests/Normalizer/ObjectNormalizerTest.php b/Tests/Normalizer/ObjectNormalizerTest.php index 477027cef..94c5db936 100644 --- a/Tests/Normalizer/ObjectNormalizerTest.php +++ b/Tests/Normalizer/ObjectNormalizerTest.php @@ -132,6 +132,16 @@ public function testNormalizeObjectWithUninitializedProperties() ); } + public function testNormalizeObjectWithUnsetProperties() + { + $obj = new ObjectInner(); + unset($obj->foo); + $this->assertEquals( + ['bar' => null], + $this->normalizer->normalize($obj, 'any') + ); + } + /** * @requires PHP 7.4 */ diff --git a/Tests/Normalizer/PropertyNormalizerTest.php b/Tests/Normalizer/PropertyNormalizerTest.php index a2aefb4ed..98c1fc601 100644 --- a/Tests/Normalizer/PropertyNormalizerTest.php +++ b/Tests/Normalizer/PropertyNormalizerTest.php @@ -101,6 +101,16 @@ public function testNormalizeObjectWithUninitializedProperties() ); } + public function testNormalizeObjectWithUnsetProperties() + { + $obj = new PropertyDummy(); + unset($obj->foo); + $this->assertEquals( + ['bar' => null, 'camelCase' => null], + $this->normalizer->normalize($obj, 'any') + ); + } + public function testDenormalize() { $obj = $this->normalizer->denormalize( From 4c12f7d43a28762e091fcba770921a3124f93ecf Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 18 Nov 2021 11:50:29 +0100 Subject: [PATCH 3/4] [Serializer] fix support for lazy properties --- Normalizer/ObjectNormalizer.php | 4 ++-- Normalizer/PropertyNormalizer.php | 10 ++++++---- Tests/Normalizer/ObjectNormalizerTest.php | 20 ++++++++++++++++++++ Tests/Normalizer/PropertyNormalizerTest.php | 20 ++++++++++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/Normalizer/ObjectNormalizer.php b/Normalizer/ObjectNormalizer.php index 1a3ed992e..874b4788d 100644 --- a/Normalizer/ObjectNormalizer.php +++ b/Normalizer/ObjectNormalizer.php @@ -104,9 +104,9 @@ protected function extractAttributes($object, $format = null, array $context = [ } // properties - $propertyValues = (array) $object; + $propertyValues = !method_exists($object, '__get') ? (array) $object : null; foreach ($reflClass->getProperties() as $reflProperty) { - if (!\array_key_exists($reflProperty->name, $propertyValues)) { + if (null !== $propertyValues && !\array_key_exists($reflProperty->name, $propertyValues)) { if ($reflProperty->isPublic() || ($reflProperty->isProtected() && !\array_key_exists("\0*\0{$reflProperty->name}", $propertyValues)) || ($reflProperty->isPrivate() && !\array_key_exists("\0{$reflProperty->class}\0{$reflProperty->name}", $propertyValues)) diff --git a/Normalizer/PropertyNormalizer.php b/Normalizer/PropertyNormalizer.php index 83ec5325b..9989c60e4 100644 --- a/Normalizer/PropertyNormalizer.php +++ b/Normalizer/PropertyNormalizer.php @@ -101,13 +101,15 @@ protected function extractAttributes($object, $format = null, array $context = [ { $reflectionObject = new \ReflectionObject($object); $attributes = []; - $propertyValues = (array) $object; + $propertyValues = !method_exists($object, '__get') ? (array) $object : null; do { foreach ($reflectionObject->getProperties() as $property) { - if (($property->isPublic() && !\array_key_exists($property->name, $propertyValues)) - || ($property->isProtected() && !\array_key_exists("\0*\0{$property->name}", $propertyValues)) - || ($property->isPrivate() && !\array_key_exists("\0{$property->class}\0{$property->name}", $propertyValues)) + if ((null !== $propertyValues && ( + ($property->isPublic() && !\array_key_exists($property->name, $propertyValues)) + || ($property->isProtected() && !\array_key_exists("\0*\0{$property->name}", $propertyValues)) + || ($property->isPrivate() && !\array_key_exists("\0{$property->class}\0{$property->name}", $propertyValues)) + )) || !$this->isAllowedAttribute($reflectionObject->getName(), $property->name, $format, $context) ) { continue; diff --git a/Tests/Normalizer/ObjectNormalizerTest.php b/Tests/Normalizer/ObjectNormalizerTest.php index 94c5db936..50ed2ad0f 100644 --- a/Tests/Normalizer/ObjectNormalizerTest.php +++ b/Tests/Normalizer/ObjectNormalizerTest.php @@ -142,6 +142,16 @@ public function testNormalizeObjectWithUnsetProperties() ); } + public function testNormalizeObjectWithLazyProperties() + { + $obj = new LazyObjectInner(); + unset($obj->foo); + $this->assertEquals( + ['foo' => 123, 'bar' => null], + $this->normalizer->normalize($obj, 'any') + ); + } + /** * @requires PHP 7.4 */ @@ -1093,6 +1103,16 @@ class ObjectInner public $bar; } +class LazyObjectInner extends ObjectInner +{ + public function __get($name) + { + if ('foo' === $name) { + return $this->foo = 123; + } + } +} + class FormatAndContextAwareNormalizer extends ObjectNormalizer { protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = []): bool diff --git a/Tests/Normalizer/PropertyNormalizerTest.php b/Tests/Normalizer/PropertyNormalizerTest.php index 98c1fc601..be8b17124 100644 --- a/Tests/Normalizer/PropertyNormalizerTest.php +++ b/Tests/Normalizer/PropertyNormalizerTest.php @@ -111,6 +111,16 @@ public function testNormalizeObjectWithUnsetProperties() ); } + public function testNormalizeObjectWithLazyProperties() + { + $obj = new LazyPropertyDummy(); + unset($obj->foo); + $this->assertEquals( + ['foo' => 123, 'bar' => null, 'camelCase' => null], + $this->normalizer->normalize($obj, 'any') + ); + } + public function testDenormalize() { $obj = $this->normalizer->denormalize( @@ -508,6 +518,16 @@ public function setCamelCase($camelCase) } } +class LazyPropertyDummy extends PropertyDummy +{ + public function __get($name) + { + if ('foo' === $name) { + return $this->foo = 123; + } + } +} + class PropertyConstructorDummy { protected $foo; From b5704a9e7ec142368da020f15d7b30fdcf356b0d Mon Sep 17 00:00:00 2001 From: satalaondrej Date: Wed, 10 Nov 2021 20:33:30 +0100 Subject: [PATCH 4/4] [Serializer] PropertyNormalizer - return unique (i.e. filter duplicate ) attributes in extractAttributes function --- Normalizer/PropertyNormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Normalizer/PropertyNormalizer.php b/Normalizer/PropertyNormalizer.php index 9989c60e4..8ecd34081 100644 --- a/Normalizer/PropertyNormalizer.php +++ b/Normalizer/PropertyNormalizer.php @@ -119,7 +119,7 @@ protected function extractAttributes($object, $format = null, array $context = [ } } while ($reflectionObject = $reflectionObject->getParentClass()); - return $attributes; + return array_unique($attributes); } /** pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy