Skip to content

Commit 5f1acdd

Browse files
alamiraultfabpot
authored andcommitted
Add visibility context option in PropertyNormalizer
1 parent 86c4a2d commit 5f1acdd

File tree

5 files changed

+140
-4
lines changed

5 files changed

+140
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ CHANGELOG
55
---
66

77
* Add support for constructor promoted properties to `Context` attribute
8+
* Add context option `PropertyNormalizer::NORMALIZE_VISIBILITY` with bitmask flags `PropertyNormalizer::NORMALIZE_PUBLIC`, `PropertyNormalizer::NORMALIZE_PROTECTED`, `PropertyNormalizer::NORMALIZE_PRIVATE`
9+
* Add method `withNormalizeVisibility` to `PropertyNormalizerContextBuilder`
810

911
6.1
1012
---

Context/Normalizer/PropertyNormalizerContextBuilder.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,20 @@
1111

1212
namespace Symfony\Component\Serializer\Context\Normalizer;
1313

14+
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
15+
1416
/**
1517
* A helper providing autocompletion for available PropertyNormalizer options.
1618
*
1719
* @author Mathias Arlaud <[email protected]>
1820
*/
1921
final class PropertyNormalizerContextBuilder extends AbstractObjectNormalizerContextBuilder
2022
{
23+
/**
24+
* Configures whether fields should be output based on visibility.
25+
*/
26+
public function withNormalizeVisibility(int $normalizeVisibility): static
27+
{
28+
return $this->with(PropertyNormalizer::NORMALIZE_VISIBILITY, $normalizeVisibility);
29+
}
2130
}

Normalizer/PropertyNormalizer.php

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
namespace Symfony\Component\Serializer\Normalizer;
1313

1414
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
15+
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
16+
use Symfony\Component\Serializer\Mapping\ClassDiscriminatorResolverInterface;
17+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
18+
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
1519

1620
/**
1721
* Converts between objects and arrays by mapping properties.
@@ -32,6 +36,24 @@
3236
*/
3337
class PropertyNormalizer extends AbstractObjectNormalizer
3438
{
39+
public const NORMALIZE_PUBLIC = 1;
40+
public const NORMALIZE_PROTECTED = 2;
41+
public const NORMALIZE_PRIVATE = 4;
42+
43+
/**
44+
* Flag to control whether fields should be output based on visibility.
45+
*/
46+
public const NORMALIZE_VISIBILITY = 'normalize_visibility';
47+
48+
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, callable $objectClassResolver = null, array $defaultContext = [])
49+
{
50+
parent::__construct($classMetadataFactory, $nameConverter, $propertyTypeExtractor, $classDiscriminatorResolver, $objectClassResolver, $defaultContext);
51+
52+
if (!isset($this->defaultContext[self::NORMALIZE_VISIBILITY])) {
53+
$this->defaultContext[self::NORMALIZE_VISIBILITY] = self::NORMALIZE_PUBLIC | self::NORMALIZE_PROTECTED | self::NORMALIZE_PRIVATE;
54+
}
55+
}
56+
3557
/**
3658
* {@inheritdoc}
3759
*
@@ -90,14 +112,29 @@ protected function isAllowedAttribute(object|string $classOrObject, string $attr
90112

91113
try {
92114
$reflectionProperty = $this->getReflectionProperty($classOrObject, $attribute);
93-
if ($reflectionProperty->isStatic()) {
94-
return false;
95-
}
96115
} catch (\ReflectionException) {
97116
return false;
98117
}
99118

100-
return true;
119+
if ($reflectionProperty->isStatic()) {
120+
return false;
121+
}
122+
123+
$normalizeVisibility = $context[self::NORMALIZE_VISIBILITY] ?? $this->defaultContext[self::NORMALIZE_VISIBILITY];
124+
125+
if ((self::NORMALIZE_PUBLIC & $normalizeVisibility) && $reflectionProperty->isPublic()) {
126+
return true;
127+
}
128+
129+
if ((self::NORMALIZE_PROTECTED & $normalizeVisibility) && $reflectionProperty->isProtected()) {
130+
return true;
131+
}
132+
133+
if ((self::NORMALIZE_PRIVATE & $normalizeVisibility) && $reflectionProperty->isPrivate()) {
134+
return true;
135+
}
136+
137+
return false;
101138
}
102139

103140
/**
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Context\Normalizer;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Serializer\Context\Normalizer\PropertyNormalizerContextBuilder;
16+
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
17+
18+
/**
19+
* @author Antoine Lamirault <[email protected]>
20+
*/
21+
class PropertyNormalizerContextBuilderTest extends TestCase
22+
{
23+
private PropertyNormalizerContextBuilder $contextBuilder;
24+
25+
protected function setUp(): void
26+
{
27+
$this->contextBuilder = new PropertyNormalizerContextBuilder();
28+
}
29+
30+
public function testWithNormalizeVisibility()
31+
{
32+
$context = $this->contextBuilder
33+
->withNormalizeVisibility(PropertyNormalizer::NORMALIZE_PUBLIC | PropertyNormalizer::NORMALIZE_PROTECTED)
34+
->toArray();
35+
36+
$this->assertSame([
37+
PropertyNormalizer::NORMALIZE_VISIBILITY => PropertyNormalizer::NORMALIZE_PUBLIC | PropertyNormalizer::NORMALIZE_PROTECTED,
38+
], $context);
39+
}
40+
}

Tests/Normalizer/PropertyNormalizerTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,54 @@ public function testNormalizeObjectWithLazyProperties()
122122
);
123123
}
124124

125+
public function testNormalizeOnlyPublic()
126+
{
127+
$obj = new PropertyDummy();
128+
$obj->foo = 'foo';
129+
$obj->setBar('bar');
130+
$obj->setCamelCase('camelcase');
131+
$this->assertEquals(
132+
['foo' => 'foo'],
133+
$this->normalizer->normalize($obj, 'any', ['normalize_visibility' => PropertyNormalizer::NORMALIZE_PUBLIC])
134+
);
135+
}
136+
137+
public function testNormalizeOnlyProtected()
138+
{
139+
$obj = new PropertyDummy();
140+
$obj->foo = 'foo';
141+
$obj->setBar('bar');
142+
$obj->setCamelCase('camelcase');
143+
$this->assertEquals(
144+
['camelCase' => 'camelcase'],
145+
$this->normalizer->normalize($obj, 'any', ['normalize_visibility' => PropertyNormalizer::NORMALIZE_PROTECTED])
146+
);
147+
}
148+
149+
public function testNormalizeOnlyPrivate()
150+
{
151+
$obj = new PropertyDummy();
152+
$obj->foo = 'foo';
153+
$obj->setBar('bar');
154+
$obj->setCamelCase('camelcase');
155+
$this->assertEquals(
156+
['bar' => 'bar'],
157+
$this->normalizer->normalize($obj, 'any', ['normalize_visibility' => PropertyNormalizer::NORMALIZE_PRIVATE])
158+
);
159+
}
160+
161+
public function testNormalizePublicAndProtected()
162+
{
163+
$obj = new PropertyDummy();
164+
$obj->foo = 'foo';
165+
$obj->setBar('bar');
166+
$obj->setCamelCase('camelcase');
167+
$this->assertEquals(
168+
['foo' => 'foo', 'camelCase' => 'camelcase'],
169+
$this->normalizer->normalize($obj, 'any', ['normalize_visibility' => PropertyNormalizer::NORMALIZE_PUBLIC | PropertyNormalizer::NORMALIZE_PROTECTED])
170+
);
171+
}
172+
125173
public function testDenormalize()
126174
{
127175
$obj = $this->normalizer->denormalize(

0 commit comments

Comments
 (0)