Closed
Description
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];
}