From 46036271905b865c5e9ea96959b465cd37817740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 17 Aug 2016 12:41:54 +0200 Subject: [PATCH 1/2] [Serializer] Fix denormalization of arrays --- .../Normalizer/AbstractObjectNormalizer.php | 18 ++++++- .../Normalizer/ArrayDenormalizer.php | 19 +++++--- .../Tests/Normalizer/ObjectNormalizerTest.php | 47 +++++++++++++++++-- .../Component/Serializer/composer.json | 5 +- 4 files changed, 76 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 2a8018dc0b1bc..aba73568407d6 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -248,8 +248,22 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma return; } - $builtinType = $type->getBuiltinType(); - $class = $type->getClassName(); + if ( + $type->isCollection() && + null !== ($collectionValueType = $type->getCollectionValueType()) && + Type::BUILTIN_TYPE_OBJECT === ($collectionValueBuiltinType = $collectionValueType->getBuiltinType()) + ) { + $builtinType = Type::BUILTIN_TYPE_OBJECT; + $class = $collectionValueType->getClassName().'[]'; + + if (null !== ($collectionKeyType = $type->getCollectionKeyType())) { + $context['key_type'] = $collectionKeyType; + } + } else { + $builtinType = $type->getBuiltinType(); + $class = $type->getClassName(); + } + $expectedTypes[Type::BUILTIN_TYPE_OBJECT === $builtinType && $class ? $class : $builtinType] = true; if (Type::BUILTIN_TYPE_OBJECT === $builtinType) { diff --git a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php index 921e312bd0de9..7d3d87c510c55 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php @@ -13,6 +13,7 @@ use Symfony\Component\Serializer\Exception\BadMethodCallException; use Symfony\Component\Serializer\Exception\InvalidArgumentException; +use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\SerializerAwareInterface; use Symfony\Component\Serializer\SerializerInterface; @@ -30,6 +31,8 @@ class ArrayDenormalizer implements DenormalizerInterface, SerializerAwareInterfa /** * {@inheritdoc} + * + * @throws UnexpectedValueException */ public function denormalize($data, $class, $format = null, array $context = array()) { @@ -46,12 +49,16 @@ public function denormalize($data, $class, $format = null, array $context = arra $serializer = $this->serializer; $class = substr($class, 0, -2); - return array_map( - function ($data) use ($serializer, $class, $format, $context) { - return $serializer->denormalize($data, $class, $format, $context); - }, - $data - ); + $builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null; + foreach ($data as $key => $value) { + if (null !== $builtinType && !call_user_func('is_'.$builtinType, $key)) { + throw new UnexpectedValueException(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, gettype($key))); + } + + $data[$key] = $serializer->denormalize($value, $class, $format, $context); + } + + return $data; } /** diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 8a09e516cd230..df238d02bf145 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -12,9 +12,12 @@ namespace Symfony\Component\Serializer\Tests\Normalizer; use Doctrine\Common\Annotations\AnnotationReader; +use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; +use Symfony\Component\PropertyInfo\PropertyInfoExtractor; use Symfony\Component\Serializer\Exception\UnexpectedValueException; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; +use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; @@ -525,13 +528,21 @@ public function testThrowUnexpectedValueException() public function testDenomalizeRecursive() { - $normalizer = new ObjectNormalizer(null, null, null, new ReflectionExtractor()); - $serializer = new Serializer(array(new DateTimeNormalizer(), $normalizer)); + $extractor = new PropertyInfoExtractor(array(), array(new PhpDocExtractor(), new ReflectionExtractor())); + $normalizer = new ObjectNormalizer(null, null, null, $extractor); + $serializer = new Serializer(array(new ArrayDenormalizer(), new DateTimeNormalizer(), $normalizer)); + + $obj = $serializer->denormalize(array( + 'inner' => array('foo' => 'foo', 'bar' => 'bar'), + 'date' => '1988/01/21', + 'inners' => array(array('foo' => 1), array('foo' => 2)), + ), ObjectOuter::class); - $obj = $serializer->denormalize(array('inner' => array('foo' => 'foo', 'bar' => 'bar'), 'date' => '1988/01/21'), ObjectOuter::class); $this->assertEquals('foo', $obj->getInner()->foo); $this->assertEquals('bar', $obj->getInner()->bar); $this->assertEquals('1988-01-21', $obj->getDate()->format('Y-m-d')); + $this->assertEquals(1, $obj->getInners()[0]->foo); + $this->assertEquals(2, $obj->getInners()[1]->foo); } /** @@ -546,6 +557,21 @@ public function testRejectInvalidType() $serializer->denormalize(array('date' => 'foo'), ObjectOuter::class); } + /** + * @expectedException UnexpectedValueException + * @expectedExceptionMessage The type of the key "a" must be "int" ("string" given). + */ + public function testRejectInvalidKey() + { + $extractor = new PropertyInfoExtractor(array(), array(new PhpDocExtractor(), new ReflectionExtractor())); + $normalizer = new ObjectNormalizer(null, null, null, $extractor); + $serializer = new Serializer(array(new ArrayDenormalizer(), new DateTimeNormalizer(), $normalizer)); + + var_dump($serializer->denormalize(array( + 'inners' => array('a' => array('foo' => 1)), + ), ObjectOuter::class)); + } + public function testExtractAttributesRespectsFormat() { $normalizer = new FormatAndContextAwareNormalizer(); @@ -740,6 +766,11 @@ class ObjectOuter private $inner; private $date; + /** + * @var ObjectInner[] + */ + private $inners; + public function getInner() { return $this->inner; @@ -759,6 +790,16 @@ public function getDate() { return $this->date; } + + public function setInners(array $inners) + { + $this->inners = $inners; + } + + public function getInners() + { + return $this->inners; + } } class ObjectInner diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index 29d40aab0293a..01fc76bab53e0 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -24,9 +24,10 @@ "symfony/property-access": "~2.8|~3.0", "symfony/http-foundation": "~2.8|~3.0", "symfony/cache": "~3.1", - "symfony/property-info": "~2.8|~3.0", + "symfony/property-info": "~3.1", "doctrine/annotations": "~1.0", - "doctrine/cache": "~1.0" + "doctrine/cache": "~1.0", + "phpdocumentor/reflection-docblock": "~3.0" }, "conflict": { "symfony/property-access": ">=3.0,<3.0.4|>=2.8,<2.8.4" From a399b48083b03dd3ffcec723b388b6a99b089885 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 17 Aug 2016 23:18:22 +0200 Subject: [PATCH 2/2] Fix @nicolas-grekas's comments --- .../Serializer/Normalizer/AbstractObjectNormalizer.php | 8 ++------ .../Serializer/Tests/Normalizer/ObjectNormalizerTest.php | 4 +--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index aba73568407d6..9ff231e7a426c 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -248,15 +248,11 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma return; } - if ( - $type->isCollection() && - null !== ($collectionValueType = $type->getCollectionValueType()) && - Type::BUILTIN_TYPE_OBJECT === ($collectionValueBuiltinType = $collectionValueType->getBuiltinType()) - ) { + if ($type->isCollection() && null !== ($collectionValueType = $type->getCollectionValueType()) && Type::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) { $builtinType = Type::BUILTIN_TYPE_OBJECT; $class = $collectionValueType->getClassName().'[]'; - if (null !== ($collectionKeyType = $type->getCollectionKeyType())) { + if (null !== $collectionKeyType = $type->getCollectionKeyType()) { $context['key_type'] = $collectionKeyType; } } else { diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index df238d02bf145..ca1c4e0f59f82 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -567,9 +567,7 @@ public function testRejectInvalidKey() $normalizer = new ObjectNormalizer(null, null, null, $extractor); $serializer = new Serializer(array(new ArrayDenormalizer(), new DateTimeNormalizer(), $normalizer)); - var_dump($serializer->denormalize(array( - 'inners' => array('a' => array('foo' => 1)), - ), ObjectOuter::class)); + $serializer->denormalize(array('inners' => array('a' => array('foo' => 1))), ObjectOuter::class); } public function testExtractAttributesRespectsFormat() 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