Issue 1:
Symfony version(s) affected: 4.0.6

Description

So, let's say I have a JSON data corresponding to a class Child that has an attribute games that is an array of objects of class Game. So when using the serialisation using setters I get and the end a Child Object with an attribute games that is an array of several Game instances.

However for design reasons it is not possible to use the setters so I had to use the serialisation through the constructor, hence on my constructor I have someting like:

class Child
{

/**
* @var Game[]
*/
private $games;

/**
* Child constructor.
*
* @param Game[] $games
*/
public function __construct(
array $games,
) {
$this->games = $games;
}
}

setter denormalization

The problem here is that the denormalizer could not figure out that the games is an array of objects from the class Game and then I get instead of an object an array have the same attributes defined on the Game class which is not the same when I add a setter for the games.

When I debug it the code fro both setter serialization and constructor serialization I can see the difference between both methods getting into action.

For the first case of the setter denormalization the function validateAndDenormalize from the AbstractObjectNormalizer is handling the normalization and there is the code there is a check on the collection type if ($type->isCollection() && null !== ($collectionValueType = $type->getCollectionValueType()) && Type::BUILTIN_TYPE_OBJECT === $collectionValueType->getBuiltinType()) which can determine if the array has objects from a defined class.

Adding a setter like that is enough to get the right denormalization:

/**
* @param Game[] $games
* @return Child
*/
public function setGames(array $games): Child
{
$this->games = $games;
return $this;
}

constructor denormalization

However for the other case: the constructor denormalization: the function instatiateObject from the AbstractNormalizer class is being executed and there there are no check on the type collection $params[] = $parameterData; ==> that line exactly is responsible of giving me back an array of attributes instead of creating an object from the class Game which I really found a bad behaviour and mis-leading.

How to reproduce

Just setting a Child class with an attribute games which is an array of Game objects. And a class Game with some attributes. And then A Json Object that represents a child objects with a related array of games is enough to reproduces this behaviour.

A hint for the JSON:

{
"name": "test-boy",
"age": 12,
"games": [{
"name": "Age of Empire",
"genre": "Strategy"
}]

}

Possible Solution

The possible solution is to implement a collection verification as it is implemented on the first case (setter denormalization) would be enough I guess to fix this behaviour.

Issue 2:
Q
A

Bug report?
no

Feature request?
yes

BC Break report?
no

RFC?
no

Symfony version
?

Currenly PhpDocExtractor works only with class properties. It's not possible to clarify the type of the constructor parameter through docblock comment.

class A
{
private $b;

/**
* @param B[] $b
*/
public function __construct(array $b)
{
$this->b = $b;
}

public function getB()
{
return $this->b;
}
}

class B
{
private $c;

public function __construct($c)
{
$this->c = $c;
}

public function getC()
{
return $this->c;
}
}

$a = $serializer->denormalize(['b' => [['c' => 1], ['c' => 2], ['c' => 3]]], A::class);
print_r($a);

Expected result:

App\A Object
(
[b:App\A:private] => Array
(
[0] => App\B Object
(
[c:App\B:private] => 1
)
[1] => App\B Object
(
[c:App\B:private] => 2
)
[2] => App\B Object
(
[c:App\B:private] => 3
)
)
)

Actual result:

App\A Object
(
[b:App\A:private] => Array
(
[0] => Array
(
[c] => 1
)
[1] => Array
(
[c] => 2
)
[2] => Array
(
[c] => 3
)
)
)