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

Skip to content

[Serializer] ObjectNormalizer throws exception normalizing classes with the method get #58012

Closed
@mihai-stancu

Description

@mihai-stancu

Symfony version(s) affected

>= 6.4.x

Description

If a class has a method called get that method will get confused for an accessor and it will go through logic relating to property-access for the property named substr('get', 3).

Eventually a new PropertyPath instance will be created with an empty path which will trigger an exception.

How to reproduce

$serializer->serialize(new class { 
    public function get() {} 
}, 'json');

Possible Solution

A naive solution ObjectNormalizer::isAllowedAttribute() at line 193

-                self::$isReadableCache[$class.$attribute] = (\is_object($classOrObject) && $this->propertyAccessor->isReadable($classOrObject, $attribute)) || $this->propertyInfoExtractor->isReadable($class, $attribute) || $this->hasAttributeAccessorMethod($class, $attribute);
+                self::$isReadableCache[$class.$attribute] = (\is_object($classOrObject) && !empty($attribute) && $this->propertyAccessor->isReadable($classOrObject, $attribute)) || $this->propertyInfoExtractor->isReadable($class, $attribute) || $this->hasAttributeAccessorMethod($class, $attribute);

But most likely this object would need to be skipped earlier in the normalization process, perhaps here:

-            if (str_starts_with($name, 'get') || str_starts_with($name, 'has') || str_starts_with($name, 'can')) {
+            if ($name !== 'get' && str_starts_with($name, 'get')
+            || $name !== 'has' && str_starts_with($name, 'has')
+            || $name !== 'can' && str_starts_with($name, 'can')) {
                // getters, hassers and canners
                $attributeName = substr($name, 3);

                if (!$reflClass->hasProperty($attributeName)) {
                    $attributeName = lcfirst($attributeName);
                }
-            } elseif (str_starts_with($name, 'is')) {
+            } elseif ($name !== 'is' && str_starts_with($name, 'is')) {
                // issers
                $attributeName = substr($name, 2);

                if (!$reflClass->hasProperty($attributeName)) {
                    $attributeName = lcfirst($attributeName);
                }
            }

Additional Context

Exception is triggered around here $propertyAccessor->isReadable() instantiates a PropertyPath.

// ObjectNormalizer::isAllowedAttribute() line 193
        if ($context['_read_attributes'] ?? true) {
            if (!isset(self::$isReadableCache[$class.$attribute])) {
                self::$isReadableCache[$class.$attribute] = (\is_object($classOrObject) && $this->propertyAccessor->isReadable($classOrObject, $attribute)) || $this->propertyInfoExtractor->isReadable($class, $attribute) || $this->hasAttributeAccessorMethod($class, $attribute);
            }

            return self::$isReadableCache[$class.$attribute];
        }

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