Skip to content

Commit 2e1a76e

Browse files
RafaelKrnicolas-grekas
authored andcommitted
[Serializer] Adjust ObjectNormalizerTest for the accessor method changes from symfony#61097
1 parent de58845 commit 2e1a76e

File tree

1 file changed

+182
-10
lines changed

1 file changed

+182
-10
lines changed

src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php

Lines changed: 182 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPStan\PhpDocParser\Parser\PhpDocParser;
1515
use PHPUnit\Framework\MockObject\MockObject;
1616
use PHPUnit\Framework\TestCase;
17+
use Symfony\Component\PropertyAccess\PropertyAccessorBuilder;
1718
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1819
use Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor;
1920
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
@@ -938,10 +939,26 @@ public function testObjectNormalizerWithAttributeLoaderAndObjectHasStaticPropert
938939
$this->assertSame([], $normalizer->normalize($class));
939940
}
940941

941-
public function testNormalizeWithMethodNamesSimilarToAccessors()
942+
// accessors
943+
944+
protected function getNormalizerForAccessors($accessorPrefixes = null): ObjectNormalizer
942945
{
946+
$accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes;
943947
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
944-
$normalizer = new ObjectNormalizer($classMetadataFactory);
948+
$propertyAccessorBuilder = (new PropertyAccessorBuilder())
949+
->setReadInfoExtractor(
950+
new ReflectionExtractor([], $accessorPrefixes, null, false)
951+
);
952+
953+
return new ObjectNormalizer(
954+
$classMetadataFactory,
955+
propertyAccessor: $propertyAccessorBuilder->getPropertyAccessor(),
956+
);
957+
}
958+
959+
public function testNormalizeWithMethodNamesSimilarToAccessors()
960+
{
961+
$normalizer = $this->getNormalizerForAccessors();
945962

946963
$object = new ObjectWithAccessorishMethods();
947964
$normalized = $normalizer->normalize($object);
@@ -956,19 +973,94 @@ public function testNormalizeWithMethodNamesSimilarToAccessors()
956973
], $normalized);
957974
}
958975

959-
public function testNormalizeObjectWithBooleanPropertyAndIsserMethodWithSameName()
976+
public function testNormalizeObjectWithPublicPropertyAccessorPrecedence()
960977
{
961-
$classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
962-
$normalizer = new ObjectNormalizer($classMetadataFactory);
978+
$normalizer = $this->getNormalizerForAccessors();
963979

964-
$object = new ObjectWithBooleanPropertyAndIsserWithSameName();
980+
$object = new ObjectWithPropertyAndAllAccessorMethods(
981+
'foo',
982+
);
965983
$normalized = $normalizer->normalize($object);
966984

985+
// The getter method should take precedence over all other accessor methods
967986
$this->assertSame([
968987
'foo' => 'foo',
969-
'isFoo' => true,
970988
], $normalized);
971989
}
990+
991+
public function testNormalizeObjectWithPropertyAndAccessorMethodsWithSameName()
992+
{
993+
$normalizer = $this->getNormalizerForAccessors();
994+
995+
$object = new ObjectWithPropertyAndAccessorSameName(
996+
'foo',
997+
'getFoo',
998+
'canFoo',
999+
'hasFoo',
1000+
'isFoo'
1001+
);
1002+
$normalized = $normalizer->normalize($object);
1003+
1004+
// Accessor methods with exactly the same name as the property should take precedence
1005+
$this->assertSame([
1006+
'getFoo' => 'getFoo',
1007+
'canFoo' => 'canFoo',
1008+
'hasFoo' => 'hasFoo',
1009+
'isFoo' => 'isFoo',
1010+
// The getFoo accessor method is used for foo, thus it's also 'getFoo' instead of 'foo'
1011+
'foo' => 'getFoo',
1012+
], $normalized);
1013+
1014+
$denormalized = $this->normalizer->denormalize($normalized, ObjectWithPropertyAndAccessorSameName::class);
1015+
1016+
$this->assertSame('getFoo', $denormalized->getFoo());
1017+
1018+
// On the initial object the value was 'foo', but the normalizer prefers the accessor method 'getFoo'
1019+
// Thus on the denoramilzed object the value is 'getFoo'
1020+
$this->assertSame('foo', $object->foo);
1021+
$this->assertSame('getFoo', $denormalized->foo);
1022+
1023+
$this->assertSame('hasFoo', $denormalized->hasFoo());
1024+
$this->assertSame('canFoo', $denormalized->canFoo());
1025+
$this->assertSame('isFoo', $denormalized->isFoo());
1026+
}
1027+
1028+
/**
1029+
* Priority of accessor methods is defined by the PropertyReadInfoExtractorInterface passed to the PropertyAccessor
1030+
* component. By default ReflectionExtractor::$defaultAccessorPrefixes are used.
1031+
*/
1032+
public function testPrecedenceOfAccessorMethods()
1033+
{
1034+
// by default 'is' comes before 'has'
1035+
$defaultAccessorPrefixNormalizer = $this->getNormalizerForAccessors();
1036+
$swappedAccessorPrefixNormalizer = $this->getNormalizerForAccessors(['has', 'is']);
1037+
1038+
// Nearly equal class, only accessor order is different
1039+
$isserHasserObject = new ObjectWithPropertyIsserAndHasser('foo');
1040+
$hasserIsserObject = new ObjectWithPropertyHasserAndIsser('foo');
1041+
1042+
// default precedence (is, has)
1043+
$normalizedDefaultIsserHasser = $defaultAccessorPrefixNormalizer->normalize($isserHasserObject);
1044+
$normalizedDefaultHasserIsser = $defaultAccessorPrefixNormalizer->normalize($hasserIsserObject);
1045+
1046+
$this->assertSame([
1047+
'foo' => 'isFoo',
1048+
], $normalizedDefaultIsserHasser);
1049+
$this->assertSame([
1050+
'foo' => 'isFoo',
1051+
], $normalizedDefaultHasserIsser);
1052+
1053+
// swapped precedence (has, is)
1054+
$normalizedSwappedIsserHasser = $swappedAccessorPrefixNormalizer->normalize($isserHasserObject);
1055+
$normalizedSwappedHasserIsser = $swappedAccessorPrefixNormalizer->normalize($hasserIsserObject);
1056+
1057+
$this->assertSame([
1058+
'foo' => 'hasFoo',
1059+
], $normalizedSwappedIsserHasser);
1060+
$this->assertSame([
1061+
'foo' => 'hasFoo',
1062+
], $normalizedSwappedHasserIsser);
1063+
}
9721064
}
9731065

9741066
class ProxyObjectDummy extends ObjectDummy
@@ -1312,18 +1404,98 @@ public function isolate()
13121404
}
13131405
}
13141406

1315-
class ObjectWithBooleanPropertyAndIsserWithSameName
1407+
class ObjectWithPropertyAndAllAccessorMethods
13161408
{
1317-
private $foo = 'foo';
1318-
private $isFoo = true;
1409+
public function __construct(
1410+
private $foo,
1411+
) {
1412+
}
1413+
1414+
public function canFoo()
1415+
{
1416+
return 'canFoo';
1417+
}
13191418

13201419
public function getFoo()
13211420
{
13221421
return $this->foo;
13231422
}
13241423

1424+
public function hasFoo()
1425+
{
1426+
return 'hasFoo';
1427+
}
1428+
1429+
public function isFoo()
1430+
{
1431+
return 'isFoo';
1432+
}
1433+
}
1434+
1435+
class ObjectWithPropertyAndAccessorSameName
1436+
{
1437+
public function __construct(
1438+
public $foo,
1439+
private $getFoo,
1440+
private $canFoo = null,
1441+
private $hasFoo = null,
1442+
private $isFoo = null,
1443+
) {
1444+
}
1445+
1446+
public function getFoo()
1447+
{
1448+
return $this->getFoo;
1449+
}
1450+
1451+
public function canFoo()
1452+
{
1453+
return $this->canFoo;
1454+
}
1455+
1456+
public function hasFoo()
1457+
{
1458+
return $this->hasFoo;
1459+
}
1460+
13251461
public function isFoo()
13261462
{
13271463
return $this->isFoo;
13281464
}
13291465
}
1466+
1467+
class ObjectWithPropertyHasserAndIsser
1468+
{
1469+
public function __construct(
1470+
private $foo,
1471+
) {
1472+
}
1473+
1474+
public function hasFoo()
1475+
{
1476+
return 'hasFoo';
1477+
}
1478+
1479+
public function isFoo()
1480+
{
1481+
return 'isFoo';
1482+
}
1483+
}
1484+
1485+
class ObjectWithPropertyIsserAndHasser
1486+
{
1487+
public function __construct(
1488+
private $foo,
1489+
) {
1490+
}
1491+
1492+
public function isFoo()
1493+
{
1494+
return 'isFoo';
1495+
}
1496+
1497+
public function hasFoo()
1498+
{
1499+
return 'hasFoo';
1500+
}
1501+
}

0 commit comments

Comments
 (0)