From 7bd4ac5937eda99391f7fb344623b06370ed5dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sun, 3 Jul 2016 11:11:08 +0100 Subject: [PATCH 01/13] Test --- .../Serializer/Normalizer/AbstractNormalizer.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 73c8cfecc13ec..5d1e7e0230c78 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -319,9 +319,13 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref $params = array_merge($params, $data[$paramName]); } } elseif ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) { - $params[] = $data[$key]; - // don't run set for a parameter passed to the constructor - unset($data[$key]); + $parameterData = $data[$key]; + if (null !== $constructogrParameter->getClass()) { + $parameterData = $this->serializer->denormalize($parameterData, $constructorParameter->getClass()->getName(), null, $context); + } + + // Don't run set for a parameter passed to the constructor + $params[] = $parameterData; } elseif ($constructorParameter->isDefaultValueAvailable()) { $params[] = $constructorParameter->getDefaultValue(); } else { From e99a90b9e98e6abe26feea4baacb295efc688fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sun, 3 Jul 2016 12:09:45 +0100 Subject: [PATCH 02/13] Add tests --- .../Normalizer/AbstractNormalizer.php | 6 ++- .../Normalizer/AbstractObjectNormalizer.php | 2 +- .../Tests/Normalizer/ObjectNormalizerTest.php | 40 +++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 5d1e7e0230c78..083a23e1e8a55 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -281,12 +281,13 @@ protected function getConstructor(array &$data, $class, array &$context, \Reflec * @param array $context * @param \ReflectionClass $reflectionClass * @param array|bool $allowedAttributes + * @param string|null $format * * @return object * * @throws RuntimeException */ - protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes) + protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, $format = null) { if ( isset($context[static::OBJECT_TO_POPULATE]) && @@ -320,12 +321,13 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref } } elseif ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) { $parameterData = $data[$key]; - if (null !== $constructogrParameter->getClass()) { + if (null !== $constructorParameter->getClass()) { $parameterData = $this->serializer->denormalize($parameterData, $constructorParameter->getClass()->getName(), null, $context); } // Don't run set for a parameter passed to the constructor $params[] = $parameterData; + unset($data[$key]); } elseif ($constructorParameter->isDefaultValueAvailable()) { $params[] = $constructorParameter->getDefaultValue(); } else { diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 088b01f9f14c5..ba14000158b55 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -175,7 +175,7 @@ public function denormalize($data, $class, $format = null, array $context = arra $normalizedData = $this->prepareForDenormalization($data); $reflectionClass = new \ReflectionClass($class); - $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes); + $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format); foreach ($normalizedData as $attribute => $value) { if ($this->nameConverter) { diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 8a09e516cd230..12cc6e7213fbd 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -157,6 +157,24 @@ public function testConstructorWithObjectDenormalize() $this->assertEquals('bar', $obj->bar); } + public function testConstructorWithObjectTypeHintDenormalize() + { + $data = [ + 'id' => 10, + 'inner' => [ + 'foo' => 'oof', + 'bar' => 'rab', + ], + ]; + + $obj = $this->normalizer->denormalize($data, DummyWithConstructorObject::class); + $this->assertInstanceOf(DummyWithConstructorObject::class, $obj); + $this->assertEquals(10, $obj->getId); + $this->assertInstanceOf(ObjectInner::class, $obj->getInner()); + $this->assertEquals('foo', $obj->getInner()->foo); + $this->assertEquals('bar', $obj->getInner()->bar); + } + public function testGroupsNormalize() { $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); @@ -782,3 +800,25 @@ protected function isAllowedAttribute($classOrObject, $attribute, $format = null return false; } } + +class DummyWithConstructorObject +{ + private $id; + private $inner; + + public function __construct($id, ObjectInner $inner) + { + $this->id = $id; + $this->inner = $inner; + } + + public function getId() + { + return $this->id; + } + + public function getInner() + { + return $this->inner; + } +} From e64e999a39ba3ee1a6c46560bedd22e32358c257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sun, 3 Jul 2016 12:55:57 +0100 Subject: [PATCH 03/13] Address comments --- .../Normalizer/AbstractNormalizer.php | 45 +++++++++++++------ .../Normalizer/AbstractObjectNormalizer.php | 2 +- .../Normalizer/GetSetMethodNormalizer.php | 2 +- .../Tests/Normalizer/ObjectNormalizerTest.php | 8 ++-- 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 083a23e1e8a55..1072d248f4cdd 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -268,6 +268,15 @@ protected function getConstructor(array &$data, $class, array &$context, \Reflec return $reflectionClass->getConstructor(); } + /** + * @see instantiateComplexObject + * @deprecated Since 3.1, will be removed in 4.0. Use instantiateComplexObject instead. + */ + protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes) + { + return $this->instantiateComplexObject($data, $class, $context, $reflectionClass, $allowedAttributes); + } + /** * Instantiates an object using constructor parameters when needed. * @@ -287,7 +296,7 @@ protected function getConstructor(array &$data, $class, array &$context, \Reflec * * @throws RuntimeException */ - protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, $format = null) + protected function instantiateComplexObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, $format = null) { if ( isset($context[static::OBJECT_TO_POPULATE]) && @@ -319,33 +328,41 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref $params = array_merge($params, $data[$paramName]); } - } elseif ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) { + + continue; + } + + if ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) { $parameterData = $data[$key]; if (null !== $constructorParameter->getClass()) { - $parameterData = $this->serializer->denormalize($parameterData, $constructorParameter->getClass()->getName(), null, $context); + $parameterData = $this->serializer->deserialize($parameterData, $constructorParameter->getClass()->getName(), null, $context); } // Don't run set for a parameter passed to the constructor $params[] = $parameterData; unset($data[$key]); - } elseif ($constructorParameter->isDefaultValueAvailable()) { + + continue; + } + + if ($constructorParameter->isDefaultValueAvailable()) { $params[] = $constructorParameter->getDefaultValue(); - } else { - throw new RuntimeException( - sprintf( - 'Cannot create an instance of %s from serialized data because its constructor requires parameter "%s" to be present.', - $class, - $constructorParameter->name - ) - ); } + + throw new RuntimeException( + sprintf( + 'Cannot create an instance of %s from serialized data because its constructor requires parameter "%s" to be present.', + $class, + $constructorParameter->name + ) + ); } if ($constructor->isConstructor()) { return $reflectionClass->newInstanceArgs($params); - } else { - return $constructor->invokeArgs(null, $params); } + + return $constructor->invokeArgs(null, $params); } return new $class(); diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index ba14000158b55..e947e57db1d26 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -175,7 +175,7 @@ public function denormalize($data, $class, $format = null, array $context = arra $normalizedData = $this->prepareForDenormalization($data); $reflectionClass = new \ReflectionClass($class); - $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format); + $object = $this->instantiateComplexObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format); foreach ($normalizedData as $attribute => $value) { if ($this->nameConverter) { diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index a0dc515ca30c8..0c0b87a954fc8 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -47,7 +47,7 @@ public function denormalize($data, $class, $format = null, array $context = arra $normalizedData = $this->prepareForDenormalization($data); $reflectionClass = new \ReflectionClass($class); - $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes); + $object = $this->instantiateComplexObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format); $classMethods = get_class_methods($object); foreach ($normalizedData as $attribute => $value) { diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 12cc6e7213fbd..464d72b5eea7e 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -159,13 +159,13 @@ public function testConstructorWithObjectDenormalize() public function testConstructorWithObjectTypeHintDenormalize() { - $data = [ + $data = array( 'id' => 10, - 'inner' => [ + 'inner' => array( 'foo' => 'oof', 'bar' => 'rab', - ], - ]; + ), + ); $obj = $this->normalizer->denormalize($data, DummyWithConstructorObject::class); $this->assertInstanceOf(DummyWithConstructorObject::class, $obj); From 4884a2e8806adf1e2a0746f917599b1615ee39c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sun, 3 Jul 2016 22:22:03 +0100 Subject: [PATCH 04/13] f1 --- .../Component/Serializer/Normalizer/AbstractNormalizer.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 1072d248f4cdd..e8e99d586b2d6 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -347,6 +347,8 @@ protected function instantiateComplexObject(array &$data, $class, array &$contex if ($constructorParameter->isDefaultValueAvailable()) { $params[] = $constructorParameter->getDefaultValue(); + + continue; } throw new RuntimeException( From f361e52c096158de02c648336909ac6023ecf158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Mon, 4 Jul 2016 21:56:52 +0100 Subject: [PATCH 05/13] fix tests --- .../Normalizer/AbstractNormalizer.php | 2 +- .../DenormalizerDecoratorSerializer.php | 43 +++++++++++++++++++ .../Tests/Normalizer/ObjectNormalizerTest.php | 13 ++++-- .../Component/Serializer/composer.json | 4 +- 4 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index e8e99d586b2d6..df8111feaa6e3 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -335,7 +335,7 @@ protected function instantiateComplexObject(array &$data, $class, array &$contex if ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) { $parameterData = $data[$key]; if (null !== $constructorParameter->getClass()) { - $parameterData = $this->serializer->deserialize($parameterData, $constructorParameter->getClass()->getName(), null, $context); + $parameterData = $this->serializer->deserialize($parameterData, $constructorParameter->getClass()->getName(), $format, $context); } // Don't run set for a parameter passed to the constructor diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php b/src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php new file mode 100644 index 0000000000000..44cae33722fe6 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php @@ -0,0 +1,43 @@ + + */ +class DenormalizerDecoratorSerializer implements SerializerInterface +{ + private $normalizer; + + /** + * @param NormalizerInterface|DenormalizerInterface $normalizer + */ + public function __construct($normalizer) + { + if (false === $normalizer instanceof NormalizerInterface && false === $normalizer instanceof DenormalizerInterface) { + throw new \InvalidArgumentException(); + } + + $this->normalizer = $normalizer; + } + + /** + * {@inheritdoc} + */ + public function serialize($data, $format, array $context = array()) + { + return $this->normalizer->normalize($data, $format, $context); + } + + /** + * {@inheritdoc} + */ + public function deserialize($data, $type, $format, array $context = array()) + { + return $this->normalizer->denormalize($data, $type, $format, $context); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 464d72b5eea7e..7f1f3807912cb 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -21,6 +21,7 @@ use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy; +use Symfony\Component\Serializer\Tests\Fixtures\DenormalizerDecoratorSerializer; use Symfony\Component\Serializer\Tests\Fixtures\MaxDepthDummy; use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; @@ -167,12 +168,16 @@ public function testConstructorWithObjectTypeHintDenormalize() ), ); - $obj = $this->normalizer->denormalize($data, DummyWithConstructorObject::class); + $normalizer = new ObjectNormalizer(); + $serializer = new DenormalizerDecoratorSerializer($normalizer); + $normalizer->setSerializer($serializer); + + $obj = $normalizer->denormalize($data, DummyWithConstructorObject::class); $this->assertInstanceOf(DummyWithConstructorObject::class, $obj); - $this->assertEquals(10, $obj->getId); + $this->assertEquals(10, $obj->getId()); $this->assertInstanceOf(ObjectInner::class, $obj->getInner()); - $this->assertEquals('foo', $obj->getInner()->foo); - $this->assertEquals('bar', $obj->getInner()->bar); + $this->assertEquals('oof', $obj->getInner()->foo); + $this->assertEquals('rab', $obj->getInner()->bar); } public function testGroupsNormalize() diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index 13d60e1a9e602..e71dad24be50e 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -16,12 +16,12 @@ } ], "require": { - "php": ">=5.5.9" + "php": ">=5.5.9", + "symfony/property-access": "^3.1" }, "require-dev": { "symfony/yaml": "~2.8|~3.0", "symfony/config": "~2.8|~3.0", - "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", From f46a1766d5c2243a720da1f6ad151df22dc89385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Mon, 4 Jul 2016 22:12:08 +0100 Subject: [PATCH 06/13] Apply patch --- .../Tests/Fixtures/DenormalizerDecoratorSerializer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php b/src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php index 44cae33722fe6..06d36c4bd85ca 100644 --- a/src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/DenormalizerDecoratorSerializer.php @@ -21,10 +21,10 @@ public function __construct($normalizer) if (false === $normalizer instanceof NormalizerInterface && false === $normalizer instanceof DenormalizerInterface) { throw new \InvalidArgumentException(); } - + $this->normalizer = $normalizer; } - + /** * {@inheritdoc} */ From 93608dcea0c3a394d90e9219a6c0f356c045f9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Tue, 5 Jul 2016 11:41:11 +0100 Subject: [PATCH 07/13] Add deprecation message --- .../Normalizer/AbstractNormalizer.php | 2 ++ .../AbstractObjectNormalizerTest.php | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index df8111feaa6e3..970e153f32bdf 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -274,6 +274,8 @@ protected function getConstructor(array &$data, $class, array &$context, \Reflec */ protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes) { + @trigger_error(sprintf('"%s()" has been deprecated sin Symfony 3.1 and will be removed in version 4.0. Use "%s::instantiateComplexObject()" instead.', __METHOD__, __CLASS__)); + return $this->instantiateComplexObject($data, $class, $context, $reflectionClass, $allowedAttributes); } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index 5660dbc361d7d..d628b829b9b7e 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -24,6 +24,19 @@ public function testDenormalize() $this->assertNull($normalizedData->bar); $this->assertSame('baz', $normalizedData->baz); } + + /** + * @group legacy + */ + public function testInstantiateObjectDenormalizer() + { + $data = array('foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz'); + $class = __NAMESPACE__.'\Dummy'; + $context = []; + + $normalizer = new AbstractObjectNormalizerDummy(); + $normalizer->instantiateObject($data, $class, $context, new \ReflectionClass($class), []); + } } class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer @@ -45,6 +58,13 @@ protected function isAllowedAttribute($classOrObject, $attribute, $format = null { return in_array($attribute, array('foo', 'baz')); } + + public function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes) + { + return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes); + } + + } class Dummy From d4cdb00b32778cda9668d77919d3b5f99d770767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Tue, 5 Jul 2016 14:32:28 +0100 Subject: [PATCH 08/13] fix CS --- .../Tests/Normalizer/AbstractObjectNormalizerTest.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index d628b829b9b7e..c9df3c955f031 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -32,10 +32,10 @@ public function testInstantiateObjectDenormalizer() { $data = array('foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz'); $class = __NAMESPACE__.'\Dummy'; - $context = []; - + $context = array(); + $normalizer = new AbstractObjectNormalizerDummy(); - $normalizer->instantiateObject($data, $class, $context, new \ReflectionClass($class), []); + $normalizer->instantiateObject($data, $class, $context, new \ReflectionClass($class), array()); } } @@ -63,8 +63,6 @@ public function instantiateObject(array &$data, $class, array &$context, \Reflec { return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes); } - - } class Dummy From 5556fa5470ebb01809012c0e7404e9ae8d7a7f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Wed, 6 Jul 2016 14:46:23 +0100 Subject: [PATCH 09/13] fix --- .../Component/Serializer/Normalizer/AbstractNormalizer.php | 2 +- src/Symfony/Component/Serializer/composer.json | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 970e153f32bdf..7bf41c2f59c23 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -274,7 +274,7 @@ protected function getConstructor(array &$data, $class, array &$context, \Reflec */ protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes) { - @trigger_error(sprintf('"%s()" has been deprecated sin Symfony 3.1 and will be removed in version 4.0. Use "%s::instantiateComplexObject()" instead.', __METHOD__, __CLASS__)); + @trigger_error(sprintf('"%s()" has been deprecated since Symfony 3.1 and will be removed in version 4.0. Use "%s::instantiateComplexObject()" instead.', __METHOD__, __CLASS__), E_USER_DEPRECATED); return $this->instantiateComplexObject($data, $class, $context, $reflectionClass, $allowedAttributes); } diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index e71dad24be50e..7639b1e907059 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -16,8 +16,7 @@ } ], "require": { - "php": ">=5.5.9", - "symfony/property-access": "^3.1" + "php": ">=5.5.9" }, "require-dev": { "symfony/yaml": "~2.8|~3.0", From 3fe9802eadde75e6252859275e39832451dc56c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Wed, 6 Jul 2016 17:37:20 +0100 Subject: [PATCH 10/13] revert CS --- .../Normalizer/AbstractNormalizer.php | 34 +++++++------------ .../Component/Serializer/composer.json | 1 + 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 7bf41c2f59c23..4d2a9ade6c65b 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -330,11 +330,7 @@ protected function instantiateComplexObject(array &$data, $class, array &$contex $params = array_merge($params, $data[$paramName]); } - - continue; - } - - if ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) { + } elseif ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) { $parameterData = $data[$key]; if (null !== $constructorParameter->getClass()) { $parameterData = $this->serializer->deserialize($parameterData, $constructorParameter->getClass()->getName(), $format, $context); @@ -343,30 +339,24 @@ protected function instantiateComplexObject(array &$data, $class, array &$contex // Don't run set for a parameter passed to the constructor $params[] = $parameterData; unset($data[$key]); - - continue; - } - - if ($constructorParameter->isDefaultValueAvailable()) { + } elseif ($constructorParameter->isDefaultValueAvailable()) { $params[] = $constructorParameter->getDefaultValue(); - - continue; + } else { + throw new RuntimeException( + sprintf( + 'Cannot create an instance of %s from serialized data because its constructor requires parameter "%s" to be present.', + $class, + $constructorParameter->name + ) + ); } - - throw new RuntimeException( - sprintf( - 'Cannot create an instance of %s from serialized data because its constructor requires parameter "%s" to be present.', - $class, - $constructorParameter->name - ) - ); } if ($constructor->isConstructor()) { return $reflectionClass->newInstanceArgs($params); + } else { + return $constructor->invokeArgs(null, $params); } - - return $constructor->invokeArgs(null, $params); } return new $class(); diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index 7639b1e907059..13d60e1a9e602 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -21,6 +21,7 @@ "require-dev": { "symfony/yaml": "~2.8|~3.0", "symfony/config": "~2.8|~3.0", + "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", From e437e0408efa3c5bc7ff48843208588b4d561331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Wed, 6 Jul 2016 17:52:20 +0100 Subject: [PATCH 11/13] fix reflection type --- .../Normalizer/AbstractNormalizer.php | 10 +++++-- .../Tests/Normalizer/ObjectNormalizerTest.php | 28 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 4d2a9ade6c65b..fc147f557ae14 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -332,8 +332,14 @@ protected function instantiateComplexObject(array &$data, $class, array &$contex } } elseif ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) { $parameterData = $data[$key]; - if (null !== $constructorParameter->getClass()) { - $parameterData = $this->serializer->deserialize($parameterData, $constructorParameter->getClass()->getName(), $format, $context); + if (null !== $constructorParameter->getType()) { + try { + $parameterClass = $constructorParameter->getClass()->getName(); + } catch (\ReflectionException $e) { + throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $key), 0, $e); + } + + $parameterData = $this->serializer->deserialize($parameterData, $parameterClass, $format, $context); } // Don't run set for a parameter passed to the constructor diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 7f1f3807912cb..ad937e78d99bd 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -180,6 +180,27 @@ public function testConstructorWithObjectTypeHintDenormalize() $this->assertEquals('rab', $obj->getInner()->bar); } + /** + * @expectedException \Symfony\Component\Serializer\Exception\RuntimeException + * @expectedExceptionMessage Could not determine the class of the parameter "unknown". + */ + public function testConstructorWithUnknownObjectTypeHintDenormalize() + { + $data = array( + 'id' => 10, + 'unknown' => array( + 'foo' => 'oof', + 'bar' => 'rab', + ), + ); + + $normalizer = new ObjectNormalizer(); + $serializer = new DenormalizerDecoratorSerializer($normalizer); + $normalizer->setSerializer($serializer); + + $normalizer->denormalize($data, DummyWithConstructorInexistingObject::class); + } + public function testGroupsNormalize() { $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); @@ -827,3 +848,10 @@ public function getInner() return $this->inner; } } + +class DummyWithConstructorInexistingObject +{ + public function __construct($id, Unknown $unknown) + { + } +} From 7b5d55d5f03a81577dea771c187c402b13affb0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 10 Jul 2016 18:21:22 +0200 Subject: [PATCH 12/13] Prevent BC in instantiateObject --- .../Serializer/Normalizer/AbstractNormalizer.php | 15 +++------------ .../Normalizer/AbstractObjectNormalizer.php | 2 +- .../Normalizer/GetSetMethodNormalizer.php | 2 +- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index fc147f557ae14..9a47138a2e0bd 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -268,17 +268,6 @@ protected function getConstructor(array &$data, $class, array &$context, \Reflec return $reflectionClass->getConstructor(); } - /** - * @see instantiateComplexObject - * @deprecated Since 3.1, will be removed in 4.0. Use instantiateComplexObject instead. - */ - protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes) - { - @trigger_error(sprintf('"%s()" has been deprecated since Symfony 3.1 and will be removed in version 4.0. Use "%s::instantiateComplexObject()" instead.', __METHOD__, __CLASS__), E_USER_DEPRECATED); - - return $this->instantiateComplexObject($data, $class, $context, $reflectionClass, $allowedAttributes); - } - /** * Instantiates an object using constructor parameters when needed. * @@ -298,8 +287,10 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref * * @throws RuntimeException */ - protected function instantiateComplexObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, $format = null) + protected function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes/*, $format = null*/) { + $format = func_num_args() >= 6 ? func_get_arg(5) : null; + if ( isset($context[static::OBJECT_TO_POPULATE]) && is_object($context[static::OBJECT_TO_POPULATE]) && diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index e947e57db1d26..ba14000158b55 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -175,7 +175,7 @@ public function denormalize($data, $class, $format = null, array $context = arra $normalizedData = $this->prepareForDenormalization($data); $reflectionClass = new \ReflectionClass($class); - $object = $this->instantiateComplexObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format); + $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format); foreach ($normalizedData as $attribute => $value) { if ($this->nameConverter) { diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index 0c0b87a954fc8..6ea0f00409d00 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -47,7 +47,7 @@ public function denormalize($data, $class, $format = null, array $context = arra $normalizedData = $this->prepareForDenormalization($data); $reflectionClass = new \ReflectionClass($class); - $object = $this->instantiateComplexObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format); + $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format); $classMethods = get_class_methods($object); foreach ($normalizedData as $attribute => $value) { From 988eba11b6057192cf292dcdd550ea9cfea042d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Sun, 10 Jul 2016 18:16:12 +0100 Subject: [PATCH 13/13] fix tests --- .../Serializer/Normalizer/AbstractNormalizer.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 9a47138a2e0bd..a1aa4250e2155 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -323,14 +323,13 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref } } elseif ($allowed && !$ignored && (isset($data[$key]) || array_key_exists($key, $data))) { $parameterData = $data[$key]; - if (null !== $constructorParameter->getType()) { - try { + try { + if (null !== $constructorParameter->getClass()) { $parameterClass = $constructorParameter->getClass()->getName(); - } catch (\ReflectionException $e) { - throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $key), 0, $e); + $parameterData = $this->serializer->deserialize($parameterData, $parameterClass, $format, $context); } - - $parameterData = $this->serializer->deserialize($parameterData, $parameterClass, $format, $context); + } catch (\ReflectionException $e) { + throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $key), 0, $e); } // Don't run set for a parameter passed to the constructor