diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 73c8cfecc13ec..a1aa4250e2155 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -281,13 +281,16 @@ 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*/) { + $format = func_num_args() >= 6 ? func_get_arg(5) : null; + if ( isset($context[static::OBJECT_TO_POPULATE]) && is_object($context[static::OBJECT_TO_POPULATE]) && @@ -319,8 +322,18 @@ 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 + $parameterData = $data[$key]; + try { + if (null !== $constructorParameter->getClass()) { + $parameterClass = $constructorParameter->getClass()->getName(); + $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 + $params[] = $parameterData; unset($data[$key]); } elseif ($constructorParameter->isDefaultValueAvailable()) { $params[] = $constructorParameter->getDefaultValue(); 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/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index a0dc515ca30c8..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->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes); + $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes, $format); $classMethods = get_class_methods($object); foreach ($normalizedData as $attribute => $value) { 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..06d36c4bd85ca --- /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/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index 5660dbc361d7d..c9df3c955f031 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 = array(); + + $normalizer = new AbstractObjectNormalizerDummy(); + $normalizer->instantiateObject($data, $class, $context, new \ReflectionClass($class), array()); + } } class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer @@ -45,6 +58,11 @@ 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 diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 8a09e516cd230..ad937e78d99bd 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; @@ -157,6 +158,49 @@ public function testConstructorWithObjectDenormalize() $this->assertEquals('bar', $obj->bar); } + public function testConstructorWithObjectTypeHintDenormalize() + { + $data = array( + 'id' => 10, + 'inner' => array( + 'foo' => 'oof', + 'bar' => 'rab', + ), + ); + + $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->assertInstanceOf(ObjectInner::class, $obj->getInner()); + $this->assertEquals('oof', $obj->getInner()->foo); + $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())); @@ -782,3 +826,32 @@ 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; + } +} + +class DummyWithConstructorInexistingObject +{ + public function __construct($id, Unknown $unknown) + { + } +}