Skip to content

Navigation Menu

Sign in
Appearance settings

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

Commit 5ec600e

Browse filesBrowse files
bug #48233 [Serializer] Prevent GetSetMethodNormalizer from creating invalid magic method call (klaussilveira)
This PR was merged into the 5.4 branch. Discussion ---------- [Serializer] Prevent `GetSetMethodNormalizer` from creating invalid magic method call | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - When serializing an object that has an `isser` for a property, but not a `getter`, and the object happens to have a `__call` magic method, the `GetSetMethodNormalizer` will attempt to call the magic method with the attribute. This may cause unexpected behavior, or, a fatal. It depends on the `__call` implementation. This undefined behavior is caused by the use of `is_callable` on `getAttributeValue`, which will return true since there is a `__call` implementation. The correct behavior can be achieved by using `method_exists` instead. A test case has been added that illustrates the issue. Commits ------- d4e1edd [Serializer] Prevent GetSetMethodNormalizer from creating invalid magic method call
2 parents f115d7a + d4e1edd commit 5ec600e
Copy full SHA for 5ec600e

File tree

2 files changed

+42
-4
lines changed
Filter options

2 files changed

+42
-4
lines changed

‎src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
+11-4Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,17 +126,17 @@ protected function getAttributeValue(object $object, string $attribute, string $
126126
$ucfirsted = ucfirst($attribute);
127127

128128
$getter = 'get'.$ucfirsted;
129-
if (\is_callable([$object, $getter])) {
129+
if (method_exists($object, $getter) && \is_callable([$object, $getter])) {
130130
return $object->$getter();
131131
}
132132

133133
$isser = 'is'.$ucfirsted;
134-
if (\is_callable([$object, $isser])) {
134+
if (method_exists($object, $isser) && \is_callable([$object, $isser])) {
135135
return $object->$isser();
136136
}
137137

138138
$haser = 'has'.$ucfirsted;
139-
if (\is_callable([$object, $haser])) {
139+
if (method_exists($object, $haser) && \is_callable([$object, $haser])) {
140140
return $object->$haser();
141141
}
142142

@@ -152,7 +152,14 @@ protected function setAttributeValue(object $object, string $attribute, $value,
152152
$key = \get_class($object).':'.$setter;
153153

154154
if (!isset(self::$setterAccessibleCache[$key])) {
155-
self::$setterAccessibleCache[$key] = \is_callable([$object, $setter]) && !(new \ReflectionMethod($object, $setter))->isStatic();
155+
try {
156+
// We have to use is_callable() here since method_exists()
157+
// does not "see" protected/private methods
158+
self::$setterAccessibleCache[$key] = \is_callable([$object, $setter]) && !(new \ReflectionMethod($object, $setter))->isStatic();
159+
} catch (\ReflectionException $e) {
160+
// Method does not exist in the class, probably a magic method
161+
self::$setterAccessibleCache[$key] = false;
162+
}
156163
}
157164

158165
if (self::$setterAccessibleCache[$key]) {

‎src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php
+31Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,22 @@ public function testHasGetterNormalize()
453453
);
454454
}
455455

456+
public function testCallMagicMethodDenormalize()
457+
{
458+
$obj = $this->normalizer->denormalize(['active' => true], ObjectWithMagicMethod::class);
459+
$this->assertTrue($obj->isActive());
460+
}
461+
462+
public function testCallMagicMethodNormalize()
463+
{
464+
$obj = new ObjectWithMagicMethod();
465+
466+
$this->assertSame(
467+
['active' => true],
468+
$this->normalizer->normalize($obj, 'any')
469+
);
470+
}
471+
456472
protected function getObjectCollectionWithExpectedArray(): array
457473
{
458474
return [[
@@ -722,3 +738,18 @@ public function hasFoo()
722738
return $this->foo;
723739
}
724740
}
741+
742+
class ObjectWithMagicMethod
743+
{
744+
private $active = true;
745+
746+
public function isActive()
747+
{
748+
return $this->active;
749+
}
750+
751+
public function __call($key, $value)
752+
{
753+
throw new \RuntimeException('__call should not be called. Called with: '.$key);
754+
}
755+
}

0 commit comments

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