Skip to content

Commit 096cddc

Browse files
committed
[Serializer] Fix object normalizer when properties has the same name as their accessor
1 parent 05137a5 commit 096cddc

File tree

6 files changed

+238
-5
lines changed

6 files changed

+238
-5
lines changed

Mapping/Loader/AnnotationLoader.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata)
106106

107107
$accessorOrMutator = preg_match('/^(get|is|has|set)(.+)$/i', $method->name, $matches);
108108
if ($accessorOrMutator) {
109-
$attributeName = lcfirst($matches[2]);
109+
$attributeName = $reflectionClass->hasProperty($method->name) ? $method->name : lcfirst($matches[2]);
110110

111111
if (isset($attributesMetadata[$attributeName])) {
112112
$attributeMetadata = $attributesMetadata[$attributeName];

Normalizer/ObjectNormalizer.php

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,25 @@ protected function extractAttributes(object $object, ?string $format = null, arr
8686

8787
if (str_starts_with($name, 'get') || str_starts_with($name, 'has')) {
8888
// getters and hassers
89-
$attributeName = substr($name, 3);
89+
$attributeName = $name;
9090

9191
if (!$reflClass->hasProperty($attributeName)) {
92-
$attributeName = lcfirst($attributeName);
92+
$attributeName = substr($attributeName, 3);
93+
94+
if (!$reflClass->hasProperty($attributeName)) {
95+
$attributeName = lcfirst($attributeName);
96+
}
9397
}
9498
} elseif (str_starts_with($name, 'is')) {
9599
// issers
96-
$attributeName = substr($name, 2);
100+
$attributeName = $name;
97101

98102
if (!$reflClass->hasProperty($attributeName)) {
99-
$attributeName = lcfirst($attributeName);
103+
$attributeName = substr($attributeName, 2);
104+
105+
if (!$reflClass->hasProperty($attributeName)) {
106+
$attributeName = lcfirst($attributeName);
107+
}
100108
}
101109
}
102110

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Tests\Fixtures;
13+
14+
class SamePropertyAsMethodDummy
15+
{
16+
private $freeTrial;
17+
private $hasSubscribe;
18+
private $getReady;
19+
private $isActive;
20+
21+
public function __construct($freeTrial, $hasSubscribe, $getReady, $isActive)
22+
{
23+
$this->freeTrial = $freeTrial;
24+
$this->hasSubscribe = $hasSubscribe;
25+
$this->getReady = $getReady;
26+
$this->isActive = $isActive;
27+
}
28+
29+
public function getFreeTrial()
30+
{
31+
return $this->freeTrial;
32+
}
33+
34+
public function hasSubscribe()
35+
{
36+
return $this->hasSubscribe;
37+
}
38+
39+
public function getReady()
40+
{
41+
return $this->getReady;
42+
}
43+
44+
public function isActive()
45+
{
46+
return $this->isActive;
47+
}
48+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Tests\Fixtures;
13+
14+
use Symfony\Component\Serializer\Annotation\SerializedName;
15+
16+
class SamePropertyAsMethodWithMethodSerializedNameDummy
17+
{
18+
private $freeTrial;
19+
private $hasSubscribe;
20+
private $getReady;
21+
private $isActive;
22+
23+
public function __construct($freeTrial, $hasSubscribe, $getReady, $isActive)
24+
{
25+
$this->freeTrial = $freeTrial;
26+
$this->hasSubscribe = $hasSubscribe;
27+
$this->getReady = $getReady;
28+
$this->isActive = $isActive;
29+
}
30+
31+
/**
32+
* @SerializedName("free_trial_method")
33+
*/
34+
public function getFreeTrial()
35+
{
36+
return $this->freeTrial;
37+
}
38+
39+
/**
40+
* @SerializedName("has_subscribe_method")
41+
*/
42+
public function hasSubscribe()
43+
{
44+
return $this->hasSubscribe;
45+
}
46+
47+
/**
48+
* @SerializedName("get_ready_method")
49+
*/
50+
public function getReady()
51+
{
52+
return $this->getReady;
53+
}
54+
55+
/**
56+
* @SerializedName("is_active_method")
57+
*/
58+
public function isActive()
59+
{
60+
return $this->isActive;
61+
}
62+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Serializer\Tests\Fixtures;
13+
14+
use Symfony\Component\Serializer\Annotation\SerializedName;
15+
16+
class SamePropertyAsMethodWithPropertySerializedNameDummy
17+
{
18+
/**
19+
* @SerializedName("free_trial_property")
20+
*/
21+
private $freeTrial;
22+
23+
/**
24+
* @SerializedName("has_subscribe_property")
25+
*/
26+
private $hasSubscribe;
27+
28+
/**
29+
* @SerializedName("get_ready_property")
30+
*/
31+
private $getReady;
32+
33+
/**
34+
* @SerializedName("is_active_property")
35+
*/
36+
private $isActive;
37+
38+
public function __construct($freeTrial, $hasSubscribe, $getReady, $isActive)
39+
{
40+
$this->freeTrial = $freeTrial;
41+
$this->hasSubscribe = $hasSubscribe;
42+
$this->getReady = $getReady;
43+
$this->isActive = $isActive;
44+
}
45+
46+
public function getFreeTrial()
47+
{
48+
return $this->freeTrial;
49+
}
50+
51+
public function hasSubscribe()
52+
{
53+
return $this->hasSubscribe;
54+
}
55+
56+
public function getReady()
57+
{
58+
return $this->getReady;
59+
}
60+
61+
public function isActive()
62+
{
63+
return $this->isActive;
64+
}
65+
}

Tests/Normalizer/ObjectNormalizerTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
use Symfony\Component\Serializer\Tests\Fixtures\Php74Dummy;
4141
use Symfony\Component\Serializer\Tests\Fixtures\Php74DummyPrivate;
4242
use Symfony\Component\Serializer\Tests\Fixtures\Php80Dummy;
43+
use Symfony\Component\Serializer\Tests\Fixtures\SamePropertyAsMethodDummy;
44+
use Symfony\Component\Serializer\Tests\Fixtures\SamePropertyAsMethodWithMethodSerializedNameDummy;
45+
use Symfony\Component\Serializer\Tests\Fixtures\SamePropertyAsMethodWithPropertySerializedNameDummy;
4346
use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder;
4447
use Symfony\Component\Serializer\Tests\Normalizer\Features\AttributesTestTrait;
4548
use Symfony\Component\Serializer\Tests\Normalizer\Features\CacheableObjectAttributesTestTrait;
@@ -869,6 +872,53 @@ public function testNormalizeStdClass()
869872

870873
$this->assertSame(['baz' => 'baz'], $this->normalizer->normalize($o2));
871874
}
875+
876+
public function testSamePropertyAsMethod()
877+
{
878+
$object = new SamePropertyAsMethodDummy('free_trial', 'has_subscribe', 'get_ready', 'is_active');
879+
$expected = [
880+
'freeTrial' => 'free_trial',
881+
'hasSubscribe' => 'has_subscribe',
882+
'getReady' => 'get_ready',
883+
'isActive' => 'is_active',
884+
];
885+
886+
$this->assertSame($expected, $this->normalizer->normalize($object));
887+
}
888+
889+
public function testSamePropertyAsMethodWithPropertySerializedName()
890+
{
891+
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
892+
$this->normalizer = new ObjectNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory));
893+
$this->normalizer->setSerializer($this->serializer);
894+
895+
$object = new SamePropertyAsMethodWithPropertySerializedNameDummy('free_trial', 'has_subscribe', 'get_ready', 'is_active');
896+
$expected = [
897+
'free_trial_property' => 'free_trial',
898+
'has_subscribe_property' => 'has_subscribe',
899+
'get_ready_property' => 'get_ready',
900+
'is_active_property' => 'is_active',
901+
];
902+
903+
$this->assertSame($expected, $this->normalizer->normalize($object));
904+
}
905+
906+
public function testSamePropertyAsMethodWithMethodSerializedName()
907+
{
908+
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
909+
$this->normalizer = new ObjectNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory));
910+
$this->normalizer->setSerializer($this->serializer);
911+
912+
$object = new SamePropertyAsMethodWithMethodSerializedNameDummy('free_trial', 'has_subscribe', 'get_ready', 'is_active');
913+
$expected = [
914+
'free_trial_method' => 'free_trial',
915+
'has_subscribe_method' => 'has_subscribe',
916+
'get_ready_method' => 'get_ready',
917+
'is_active_method' => 'is_active',
918+
];
919+
920+
$this->assertSame($expected, $this->normalizer->normalize($object));
921+
}
872922
}
873923

874924
class ProxyObjectDummy extends ObjectDummy

0 commit comments

Comments
 (0)