Skip to content

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

@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

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy