Description
Symfony version(s) affected: tested on various versions including very latest 5.3.6
Description
I have an object which may contain list of other objects in one of properties. It is allowed to not have this list in this case it is set to null.
Use case scenario: null value has a meaning that some checks has to be performed, empty list has a meaning that there were no checks required to be performed.
When object with nulled list is serialized everything works properly, but when it is deserialized it throws InvalidArgumentException
with message Data expected to be an array, null given.
.
How to reproduce
Main serialized class code:
<?php
namespace App\Model;
class Audit
{
/** @var ?AuditResult[] */
private $auditResults;
/**
* @return ?AuditResult[]
*/
public function getAuditResults(): ?array
{
return $this->auditResults;
}
/**
* @param ?AuditResult[] $auditResults
*/
public function setAuditResults(?array $auditResults): void
{
$this->auditResults = $auditResults;
}
/**
* @param AuditResult $auditResult
*/
public function addAuditResult(AuditResult $auditResult): void
{
if (!is_array($this->auditResults)) {
$this->auditResults = [];
}
$this->auditResults[] = $auditResult;
}
}
Subclass (used in the list):
<?php
namespace App\Model;
class AuditResult
{
private string $check;
/**
* @param string $check
*/
public function __construct(string $check)
{
$this->check = $check;
}
/**
* @return string
*/
public function getCheck(): string
{
return $this->check;
}
/**
* @param string $check
*/
public function setCheck(string $check): void
{
$this->check = $check;
}
}
Example code to show the issue:
public function test()
{
$audit = new Audit();
$audit->setAuditResults([new AuditResult('check1'), new AuditResult('check2')]);
dump($audit);
dump($serializedAudit = $this->serializer->serialize($audit, 'json'));
dump($this->serializer->deserialize($serializedAudit, Audit::class, 'json'));
$audit = new Audit();
dump($audit);
dump($serializedAudit = $this->serializer->serialize($audit, 'json'));
dump($this->serializer->deserialize($serializedAudit, Audit::class, 'json'));
}
Possible Solution
Not found.
I have not yet found a correct solution. Playing with various combinations of PhpDoc / Type Hint annotations I am able to prevent the code to fail with exception, but in the same time it prevents correct deserialization of the items in the list to objects.
Same code worked in 4.* branch of symfony so I expect that there might be some breaking change in the meantime, but I was unable to pinpoint it.
I see visible behavior change by commenting Audit::addAuditResult
method but it also prevents correct discovery of object type in the list.
Additional context
I have created configured skeleton Symfony application which is available here: https://github.com/noofaq/symfony-issue-42412
All changes to reproduce the issue are included in the very last commit.
Navigating to "/test" url should show the results.