Skip to content

Commit 584c210

Browse files
feature #49553 [Serializer] Add flag to require all properties to be listed in the input (Christian Kolb)
This PR was merged into the 6.3 branch. Discussion ---------- [Serializer] Add flag to require all properties to be listed in the input | Q | A | ------------- | --- | Branch? | 6.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #49504 | License | MIT | Doc PR | symfony/symfony-docs#17979 The PR #40522 introduced a fallback for nullable properties to be set to `null` when they aren't provided as parameters. A drawback of that approach is that it easier for bugs to appear through typos or renamings of those properties. I think the current implementation makes perfect sense as a default. Therefore, this PR introduces a new context flag that prevents that fallback behaviour. This way nothing changes for existing systems, but for people wanting more control, it's possible to set a flag. ### Example ```php final class Product { public function __construct( public string $name, public ?int $costsInCent, ) { } } // This works and results in $costsInCent as null $product = $this->serializer->deserialize( '{"name": "foo"}', Product::class, JsonEncoder::FORMAT, ); // When using the flag, only the following JSON is valid $product = $this->serializer->deserialize( '{"name": "foo", "costsInCent": null}', Product::class, JsonEncoder::FORMAT, [ AbstractNormalizer::PREVENT_NULLABLE_FALLBACK => true, ], ); // This would result in an error due to missing parameters $product = $this->serializer->deserialize( '{"name": "foo"}', Product::class, JsonEncoder::FORMAT, [ AbstractNormalizer::PREVENT_NULLABLE_FALLBACK => true, ], ); ``` Commits ------- d62410a [Serializer] Add flag to require all properties to be listed in the input
2 parents 0825373 + d62410a commit 584c210

File tree

5 files changed

+30
-1
lines changed

5 files changed

+30
-1
lines changed

src/Symfony/Component/Serializer/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
6.3
55
---
66

7+
* Add `AbstractNormalizer::REQUIRE_ALL_PROPERTIES` context flag to require all properties to be listed in the input instead of falling back to null for nullable ones
78
* Add `XmlEncoder::SAVE_OPTIONS` context option
89
* Add `BackedEnumNormalizer::ALLOW_INVALID_VALUES` context option
910
* Add `UnsupportedFormatException` which is thrown when there is no decoder for a given format

src/Symfony/Component/Serializer/Context/Normalizer/AbstractNormalizerContextBuilder.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,13 @@ public function withIgnoredAttributes(?array $ignoredAttributes): static
164164
{
165165
return $this->with(AbstractNormalizer::IGNORED_ATTRIBUTES, $ignoredAttributes);
166166
}
167+
168+
/**
169+
* Configures requiring all properties to be listed in the input instead
170+
* of falling back to null for nullable ones.
171+
*/
172+
public function withRequireAllProperties(?bool $requireAllProperties = true): static
173+
{
174+
return $this->with(AbstractNormalizer::REQUIRE_ALL_PROPERTIES, $requireAllProperties);
175+
}
167176
}

src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
112112
*/
113113
public const IGNORED_ATTRIBUTES = 'ignored_attributes';
114114

115+
/**
116+
* Require all properties to be listed in the input instead of falling
117+
* back to null for nullable ones.
118+
*/
119+
public const REQUIRE_ALL_PROPERTIES = 'require_all_properties';
120+
115121
/**
116122
* @internal
117123
*/
@@ -383,7 +389,7 @@ protected function instantiateObject(array &$data, string $class, array &$contex
383389
$params[] = $this->defaultContext[self::DEFAULT_CONSTRUCTOR_ARGUMENTS][$class][$key];
384390
} elseif ($constructorParameter->isDefaultValueAvailable()) {
385391
$params[] = $constructorParameter->getDefaultValue();
386-
} elseif ($constructorParameter->hasType() && $constructorParameter->getType()->allowsNull()) {
392+
} elseif (!($context[self::REQUIRE_ALL_PROPERTIES] ?? $this->defaultContext[self::REQUIRE_ALL_PROPERTIES] ?? false) && $constructorParameter->hasType() && $constructorParameter->getType()->allowsNull()) {
387393
$params[] = null;
388394
} else {
389395
if (!isset($context['not_normalizable_value_exceptions'])) {

src/Symfony/Component/Serializer/Tests/Context/Normalizer/AbstractNormalizerContextBuilderTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public function testWithers(array $values)
4545
->withCallbacks($values[AbstractNormalizer::CALLBACKS])
4646
->withCircularReferenceHandler($values[AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER])
4747
->withIgnoredAttributes($values[AbstractNormalizer::IGNORED_ATTRIBUTES])
48+
->withRequireAllProperties($values[AbstractNormalizer::REQUIRE_ALL_PROPERTIES])
4849
->toArray();
4950

5051
$this->assertEquals($values, $context);
@@ -65,6 +66,7 @@ public static function withersDataProvider(): iterable
6566
AbstractNormalizer::CALLBACKS => [static function (): void {}],
6667
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => static function (): void {},
6768
AbstractNormalizer::IGNORED_ATTRIBUTES => ['attribute3'],
69+
AbstractNormalizer::REQUIRE_ALL_PROPERTIES => true,
6870
]];
6971

7072
yield 'With null values' => [[
@@ -77,6 +79,7 @@ public static function withersDataProvider(): iterable
7779
AbstractNormalizer::CALLBACKS => null,
7880
AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => null,
7981
AbstractNormalizer::IGNORED_ATTRIBUTES => null,
82+
AbstractNormalizer::REQUIRE_ALL_PROPERTIES => null,
8083
]];
8184
}
8285

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use PHPUnit\Framework\TestCase;
1616
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1717
use Symfony\Component\Serializer\Encoder\JsonEncoder;
18+
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;
1819
use Symfony\Component\Serializer\Mapping\AttributeMetadata;
1920
use Symfony\Component\Serializer\Mapping\ClassMetadata;
2021
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
@@ -166,6 +167,15 @@ public function testObjectWithNullableNonOptionalConstructorArgumentWithoutInput
166167
$this->assertNull($dummy->getFoo());
167168
}
168169

170+
public function testObjectWithNullableNonOptionalConstructorArgumentWithoutInputAndRequireAllProperties()
171+
{
172+
$normalizer = new ObjectNormalizer();
173+
174+
$this->expectException(MissingConstructorArgumentsException::class);
175+
176+
$normalizer->denormalize([], NullableConstructorArgumentDummy::class, null, [AbstractNormalizer::REQUIRE_ALL_PROPERTIES => true]);
177+
}
178+
169179
/**
170180
* @dataProvider getNormalizer
171181
*/

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy