8000 [Hackday][Serializer] Deserialization ignores argument type hint from… · symfony/symfony@8741d00 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8741d00

Browse files
karserfabpot
authored andcommitted
[Hackday][Serializer] Deserialization ignores argument type hint from phpdoc for array in constructor argument
1 parent 9e84e0f commit 8741d00

File tree

3 files changed

+162
-12
lines changed

3 files changed

+162
-12
lines changed

src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -358,20 +358,9 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
358358
unset($data[$key]);
359359
continue;
360360
}
361-
try {
362-
if (null !== $constructorParameter->getClass()) {
363-
if (!$this->serializer instanceof DenormalizerInterface) {
364-
throw new LogicException(sprintf('Cannot create an instance of %s from serialized data because the serializer inject in "%s" is not a denormalizer', $constructorParameter->getClass(), static::class));
365-
}
366-
$parameterClass = $constructorParameter->getClass()->getName();
367-
$parameterData = $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $paramName));
368-
}
369-
} catch (\ReflectionException $e) {
370-
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $key), 0, $e);
371-
}
372361

373362
// Don't run set for a parameter passed to the constructor
374-
$params[] = $parameterData;
363+
$params[] = $this->denormalizeParameter($reflectionClass, $constructorParameter, $paramName, $parameterData, $context, $format);
375364
unset($data[$key]);
376365
} elseif ($constructorParameter->isDefaultValueAvailable()) {
377366
$params[] = $constructorParameter->getDefaultValue();
@@ -390,6 +379,27 @@ protected function instantiateObject(array &$data, $class, array &$context, \Ref
390379
return new $class();
391380
}
392381

382+
/**
383+
* @internal
384+
*/
385+
protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
386+
{
387+
try {
388+
if (null !== $parameter->getClass()) {
389+
if (!$this->serializer instanceof DenormalizerInterface) {
390+
throw new LogicException(sprintf('Cannot create an instance of %s from serialized data because the serializer inject in "%s" is not a denormalizer', $parameter->getClass(), static::class));
391+
}
392+
$parameterClass = $parameter->getClass()->getName();
393+
394+
return $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName));
395+
}
396+
397+
return $parameterData;
398+
} catch (\ReflectionException $e) {
399+
throw new RuntimeException(sprintf('Could not determine the class of the parameter "%s".', $parameterName), 0, $e);
400+
}
401+
}
402+
393403
/**
394404
* @param array $parentContext
395405
* @param string $attribute

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,18 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma
304304
throw new NotNormalizableValueException(sprintf('The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, implode('", "', array_keys($expectedTypes)), \gettype($data)));
305305
}
306306

307+
/**
308+
* @internal
309+
*/
310+
protected function denormalizeParameter(\ReflectionClass $class, \ReflectionParameter $parameter, $parameterName, $parameterData, array $context, $format = null)
311+
{
312+
if (null === $this->propertyTypeExtractor || null === $types = $this->propertyTypeExtractor->getTypes($class->getName(), $parameterName)) {
313+
return parent::denormalizeParameter($class, $parameter, $parameterName, $parameterData, $context, $format);
314+
}
315+
316+
return $this->validateAndDenormalize($class->getName(), $parameterName, $parameterData, $format, $context);
317+
}
318+
307319
/**
308320
* Sets an attribute and apply the name converter if necessary.
309321
*
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
16+
use Symfony\Component\Serializer\Encoder\JsonEncoder;
17+
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
18+
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
19+
use Symfony\Component\Serializer\Serializer;
20+
21+
class DeserializeNestedArrayOfObjectsTest extends TestCase
22+
{
A93C
23+
public function provider()
24+
{
25+
return array(
26+
//from property PhpDoc
27+
array(Zoo::class),
28+
//from argument constructor PhpDoc
29+
array(ZooImmutable::class),
30+
);
31+
}
32+
33+
/**
34+
* @dataProvider provider
35+
*/
36+
public function testPropertyPhpDoc($class)
37+
{
38+
//GIVEN
39+
$json = <<<EOF
40+
{
41+
"animals": [
42+
{"name": "Bug"}
43+
]
44+
}
45+
EOF;
46+
$serializer = new Serializer(array(
47+
new ObjectNormalizer(null, null, null, new PhpDocExtractor()),
48+
new ArrayDenormalizer(),
49+
), array('json' => new JsonEncoder()));
50+
//WHEN
51+
/** @var Zoo $zoo */
52+
$zoo = $serializer->deserialize($json, $class, 'json');
53+
//THEN
54+
self::assertCount(1, $zoo->getAnimals());
55+
self::assertInstanceOf(Animal::class, $zoo->getAnimals()[0]);
56+
}
57+
}
58+
59+
class Zoo
60+
{
61+
/** @var Animal[] */
62+
private $animals = array();
63+
64+
/**
65+
* @return Animal[]
66+
*/
67+
public function getAnimals()
68+
{
69+
return $this->animals;
70+
}
71+
72+
/**
73+
* @param Animal[] $animals
74+
*/
75+
public function setAnimals(array $animals)
76+
{
77+
$this->animals = $animals;
78+
}
79+
}
80+
81+
class ZooImmutable
82+
{
83+
/** @var Animal[] */
84+
private $animals = array();
85+
86+
/**
87+
* @param Animal[] $animals
88+
*/
89+
public function __construct(array $animals = array())
90+
{
91+
$this->animals = $animals;
92+
}
93+
94+
/**
95+
* @return Animal[]
96+
*/
97+
public function getAnimals()
98+
{
99+
return $this->animals;
100+
}
101+
}
102+
103+
class Animal
104+
{
105+
/** @var string */
106+
private $name;
107+
108+
public function __construct()
109+
{
110+
echo '';
111+
}
112+
113+
/**
114+
* @return string|null
115+
*/
116+
public function getName()
117+
{
118+
return $this->name;
119+
}
120+
121+
/**
122+
* @param string|null $name
123+
*/
124+
public function setName($name)
125+
{
126+
$this->name = $name;
127+
}
128+
}

0 commit comments

Comments
 (0)
0