Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8741d00

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

File tree

3 files changed

+162
-12
lines changed
Filter options

3 files changed

+162
-12
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
+22-12Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
+12Lines 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
*
+128Lines 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+
{
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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.