Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Serializer] ObjectNormalizer breaks when used with PropertyInfoExtractorInterface #54787

Closed
@Steveb-p

Description

@Steveb-p

Symfony version(s) affected

5.4.39, 6.4.7, 7.0.7

Description

In the following commit, a change was introduced to the ObjectNormalizer constructor signature.
900d034

Now, ObjectNormalizer requires PropertyInfoExtractorInterface to be passed as last argument (or a null value).
This is aliased in framework to match property_info service.

->alias(PropertyInfoExtractorInterface::class, 'property_info')

So, under normal circumstances in an application that uses Symfony framework, this is filled with PropertyInfoExtractorInterface (when autowiring, for example).

If null value is passed, ReflectionExtractor object is created in it's stead.

$this->propertyInfoExtractor = $propertyInfoExtractor ?: new ReflectionExtractor();

However, ReflectionExtractor is not a representation of PropertyInfoExtractorInterface.

class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTypeExtractorInterface, PropertyAccessExtractorInterface, PropertyInitializableExtractorInterface, PropertyReadInfoExtractorInterface, PropertyWriteInfoExtractorInterface, ConstructorArgumentTypeExtractorInterface

On the following line:

return $this->propertyInfoExtractor->isWritable($class, $attribute)
|| ($writeInfo = $this->propertyInfoExtractor->getWriteInfo($class, $attribute)) && PropertyWriteInfo::TYPE_NONE !== $writeInfo->getType();

...the property that can contain either PropertyInfoExtractorInterface or ReflectionExtractor is used - a method getWriteInfo() is called that exists only on ReflectionExtractor. This means that PropertyInfoExtractorInterface cannot be used and will cause a PHP error, which results in inability to denormalize that object.

Ergo, in case of our app, we cannot use the property_info service (nor it's caching), and must fall back to null.

How to reproduce

We have this error...

Caused by
 Error: Call to undefined method Symfony\Component\PropertyInfo\PropertyInfoExtractor::getWriteInfo()

...happen when ObjectNormalizer is asked to denormalize an object of the following type:

$objectState = $this->denormalizer->denormalize($data, ObjectState::class, $format, $context);
final class ObjectState
{
    /** @var string */
    public $identifier;

    /** @var string */
    public $groupIdentifier;

    private function __construct(string $identifier, string $groupIdentifier)
    {
        $this->identifier = $identifier;
        $this->groupIdentifier = $groupIdentifier;
    }
}

with data (partial extract):

action: assign_object_state
identifier: locked
groupIdentifier: ez_lock

The ObjectNormalizer service is defined as follows:

    foo.serializer.object_normalizer:
        class: Symfony\Component\Serializer\Normalizer\ObjectNormalizer
        arguments:
            $propertyTypeExtractor: '@property_info'
            $propertyInfoExtractor: '@property_info'
        tags:
            - serializer.normalizer # a different tag is used in our own code since we have a dedicated serializer, but you get the idea.

Possible Solution

ObjectNormalizer, in lieu of adding PropertyInfoExtractorInterface to ReflectionExtractor, should perform checks against method presence to better inform developers about what is actually going on and/or a fallback should exist in cases where we are not dealing with ReflectionExtractor instance.

Additional Context

No response

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