Description
Symfony version(s) affected
7.0.6 (latest) and at least 6.4.3
Description
When I denormalize an array to a class,
If this class has a property declared in a trait whose type described in phpDoc is a class in the same namespace as the trait but not in the same namespace as the class, then I get this error :
PHP Fatal error: Uncaught Symfony\Component\Serializer\Exception\NotNormalizableValueException: The type of the "children" attribute for class "App\Example\Example" must be one of "App\Example\ExampleChild[]" ("array" given). in /home/aleborgne/Sites/bug-symfony-php-stan-extractor-traits/vendor/symfony/serializer/Exception/NotNormalizableValueException.php:32
Stack trace:
#0 /home/aleborgne/Sites/bug-symfony-php-stan-extractor-traits/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php(589): Symfony\Component\Serializer\Exception\NotNormalizableValueException::createForUnexpectedDataType()
#1 /home/aleborgne/Sites/bug-symfony-php-stan-extractor-traits/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php(358): Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer->validateAndDenormalize()
#2 /home/aleborgne/Sites/bug-symfony-php-stan-extractor-traits/vendor/symfony/serializer/Serializer.php(238): Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer->denormalize()
#3 /home/aleborgne/Sites/bug-symfony-php-stan-extractor-traits/index.php(55): Symfony\Component\Serializer\Serializer->denormalize()
#4 {main}
thrown in /home/aleborgne/Sites/bug-symfony-php-stan-extractor-traits/vendor/symfony/serializer/Exception/NotNormalizableValueException.php on line 32
How to reproduce
Clone https://github.com/alexandre-le-borgne/bug-symfony-php-stan-extractor-traits
and run php index.php
Dependencies:
"php": ">=8.3",
"symfony/property-info": "^7.0",
"phpdocumentor/reflection-docblock": "^5.3",
"symfony/serializer": "^7.0",
"symfony/property-access": "^7.0"
<?php
use App\Example\Example;
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
require_once __DIR__ . '/vendor/autoload.php';
$phpDocExtractor = new PhpDocExtractor();
$phpStanExtractor = new PhpStanExtractor();
$reflectionExtractor = new ReflectionExtractor();
$listExtractors = [$reflectionExtractor];
$typeExtractors = [
$phpStanExtractor, // Comment this line to avoid the bug
$phpDocExtractor,
$reflectionExtractor,
];
$descriptionExtractors = [$phpDocExtractor];
$accessExtractors = [$reflectionExtractor];
$propertyInitializableExtractors = [$reflectionExtractor];
$propertyInfo = new PropertyInfoExtractor(
$listExtractors,
$typeExtractors,
$descriptionExtractors,
$accessExtractors,
$propertyInitializableExtractors
);
$properties = $propertyInfo->getTypes(Example::class, 'children');
// MUST returns ""App\Child\ExampleChild"" but it returns "App\Example\ExampleChild"
dump($properties[0]->getCollectionValueTypes()[0]->getClassName());
$normalizers = [
new ArrayDenormalizer(),
new ObjectNormalizer(propertyTypeExtractor: $propertyInfo),
];
$serializer = new Serializer($normalizers);
$result = $serializer->denormalize([
'children' => [
'testProperty' => '123',
],
], Example::class);
dump($result);
Possible Solution
Disable phpStanExtractor
Fix phpStanExtractor
Additional Context
It works when using only PhpDocExtractor
I think this bug was fixed in the PhpDocExtractor and then reintroduced when phpDocExtractor was replaced by PhpStanExtractor
The fix of PhpDocExtractor: #40175
The replacement of PhpDocExtractor by PhpStanExtractor: #40457