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

[AbstractObjectNormalizer][PropertyInfo] AbstractObjectNormalizer/PropertyInfoExtractor does not respect constructor type hinting. #30053

Copy link
Copy link
Closed
@gusarov112

Description

@gusarov112
Issue body actions

Symfony version(s) affected: v4.2.2 v4.1.11 v4.1.10 v3.4.21

Description
Commit 8741d00

  • \Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::denormalizeParameter()
  • calls $this->propertyTypeExtractor->getTypes()
  • from \Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface::getTypes()
  • whitch iterates over registered extractors and tries to extract property type.

PropertyInfoExtractor has extractors:

0 => \Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor,
1 => \Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor, 
2 => \Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor

Last two extractors are registered here:
vendor/symfony/framework-bundle/Resources/config/property_info.xml
and here:
\Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension::registerPropertyInfoConfiguration
and are tagged as property_info.type_extractor with priorities PhpDocExtractor[-1001], ReflectionExtractor[-1002]

The problem is that PropertyInfoExtractor does not respect constructor type hinting, but this is wrong.
Argument types from constructor are the only types that are valid for the class instantiation. And neither priority of the extractors nor other magic should affect this.

How to reproduce

<?php declare(strict_types=1);

namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class Test extends Command
{
    /** @var ContainerInterface */
    private $container;
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
        parent::__construct('t:t');
    }
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $serializer = $this->container->get('serializer');
        $obj = $serializer->denormalize(['uuid' => '74a6b770-2ba0-4b35-ae7f-4272ce5d613a'], DTO::class);
        var_dump($obj);
    }
}
interface UuidInterface {}

class DTO {
    /** @var UuidInterface */
    private $uuid;

    public function __construct(string $uuid)
    {
        $this->uuid = new class($uuid) implements UuidInterface {
            private $prop;
            public function __construct($uuid)
            {
                $this->prop = $uuid;
            }
        };
    }
}

Possible Solution
Respect constructor type hinting

Additional context
ErrorMessage:
The type of the "uuid" attribute for class "App\Command\DTO" must be one of "App\Command\UuidInterface" ("string" given).
Stack trace:

PropertyInfoExtractor.php:109, Symfony\Component\PropertyInfo\PropertyInfoExtractor->extract()
PropertyInfoExtractor.php:74, Symfony\Component\PropertyInfo\PropertyInfoExtractor->getTypes()
AbstractObjectNormalizer.php:383, Symfony\Component\Serializer\Normalizer\ObjectNormalizer->denormalizeParameter()
AbstractNormalizer.php:418, Symfony\Component\Serializer\Normalizer\ObjectNormalizer->instantiateObject()
AbstractObjectNormalizer.php:157, Symfony\Component\Serializer\Normalizer\ObjectNormalizer->instantiateObject()
AbstractObjectNormalizer.php:262, Symfony\Component\Serializer\Normalizer\ObjectNormalizer->denormalize()
Serializer.php:191, Symfony\Component\Serializer\Serializer->denormalize()
Serializer.php:142, Symfony\Component\Serializer\Serializer->deserialize()
SymfonySerializerAdapter.php:49, FOS\RestBundle\Serializer\SymfonySerializerAdapter->deserialize()
RequestBodyParamConverter.php:96, FOS\RestBundle\Request\RequestBodyParamConverter->apply()
ParamConverterManager.php:85, Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager->applyConverter()
ParamConverterManager.php:48, Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterManager->apply()
ParamConverterListener.php:78, Sensio\Bundle\FrameworkExtraBundle\EventListener\ParamConverterListener->onKernelController()
WrappedListener.php:111, Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke()
EventDispatcher.php:212, Symfony\Component\EventDispatcher\EventDispatcher->doDispatch()
EventDispatcher.php:44, Symfony\Component\EventDispatcher\EventDispatcher->dispatch()
TraceableEventDispatcher.php:145, Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher->dispatch()
HttpKernel.php:138, Symfony\Component\HttpKernel\HttpKernel->handleRaw()
HttpKernel.php:67, Symfony\Component\HttpKernel\HttpKernel->handle()
Kernel.php:198, App\Kernel->handle()
index.php:37, {main}()

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.