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 2e6a44f

Browse filesBrowse files
committed
Add COLLECT_EXTRA_ATTRIBUTES_ERRORS and full deserialization path
1 parent fb508d5 commit 2e6a44f
Copy full SHA for 2e6a44f
Expand file treeCollapse file tree

15 files changed

+345
-27
lines changed

‎UPGRADE-6.2.md

Copy file name to clipboardExpand all lines: UPGRADE-6.2.md
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ Serializer
100100
* Deprecate calling `AttributeMetadata::setSerializedName()`, `ClassMetadata::setClassDiscriminatorMapping()` without arguments
101101
* Change the signature of `AttributeMetadataInterface::setSerializedName()` to `setSerializedName(?string)`
102102
* Change the signature of `ClassMetadataInterface::setClassDiscriminatorMapping()` to `setClassDiscriminatorMapping(?ClassDiscriminatorMapping)`
103+
* Change the signature of `PartialDenormalizationException::__construct($data, array $errors)` to `__construct(mixed $data, array $errors, array $extraAttributesErrors = [])`
104+
* Deprecate `PartialDenormalizationException::getErrors()`, call `getNotNormalizableValueErrors()` instead
103105

104106
Translation
105107
-----------

‎src/Symfony/Component/PropertyAccess/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyAccess/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ CHANGELOG
88
* Added method `isNullSafe()` to `PropertyPathInterface`, implementing the interface without implementing this method
99
is deprecated
1010
* Add support for the null-coalesce operator in property paths
11+
* Add `PropertyPath::append()`
1112

1213
6.0
1314
---

‎src/Symfony/Component/PropertyAccess/PropertyPath.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyAccess/PropertyPath.php
+29Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,4 +203,33 @@ public function isNullSafe(int $index): bool
203203

204204
return $this->isNullSafe[$index];
205205
}
206+
207+
/**
208+
* Utility method for dealing with property paths.
209+
* For more extensive functionality, use instances of this class.
210+
*
211+
* Appends a path to a given property path.
212+
*
213+
* If the base path is empty, the appended path will be returned unchanged.
214+
* If the base path is not empty, and the appended path starts with a
215+
* squared opening bracket ("["), the concatenation of the two paths is
216+
* returned. Otherwise, the concatenation of the two paths is returned,
217+
* separated by a dot (".").
218+
*/
219+
public static function append(string $basePath, string $subPath): string
220+
{
221+
if ('' === $subPath) {
222+
return $basePath;
223+
}
224+
225+
if ('[' === $subPath[0]) {
226+
return $basePath.$subPath;
227+
}
228+
229+
if ('' === $basePath) {
230+
return $subPath;
231+
}
232+
233+
return $basePath.'.'.$subPath;
234+
}
206235
}

‎src/Symfony/Component/PropertyAccess/Tests/PropertyPathTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyAccess/Tests/PropertyPathTest.php
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,24 @@ public function testIsIndexDoesNotAcceptNegativeIndices()
170170

171171
$propertyPath->isIndex(-1);
172172
}
173+
174+
/**
175+
* @dataProvider provideAppendPaths
176+
*/
177+
public function testAppend(string $basePath, string $subPath, string $expectedPath, string $message)
178+
{
179+
$this->assertSame($expectedPath, PropertyPath::append($basePath, $subPath), $message);
180+
}
181+
182+
public function provideAppendPaths()
183+
{
184+
return [
185+
['foo', '', 'foo', 'It returns the basePath if subPath is empty'],
186+
['', 'bar', 'bar', 'It returns the subPath if basePath is empty'],
187+
['foo', 'bar', 'foo.bar', 'It append the subPath to the basePath'],
188+
['foo', '[bar]', 'foo[bar]', 'It does not include the dot separator if subPath uses the array notation'],
189+
['0', 'bar', '0.bar', 'Leading zeros are kept.'],
190+
['0', 1, '0.1', 'Numeric subpaths do not cause PHP 7.4 errors.'],
191+
];
192+
}
173193
}

‎src/Symfony/Component/Serializer/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/CHANGELOG.md
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ CHANGELOG
1212
* Change the signature of `ClassMetadataInterface::setClassDiscriminatorMapping()` to `setClassDiscriminatorMapping(?ClassDiscriminatorMapping)`
1313
* Add option YamlEncoder::YAML_INDENTATION to YamlEncoder constructor options to configure additional indentation for each level of nesting. This allows configuring indentation in the service configuration.
1414
* Add `SerializedPath` annotation to flatten nested attributes
15+
* Add `COLLECT_EXTRA_ATTRIBUTES_ERRORS` option to `Serializer` to collect errors from nested denormalizations
16+
* Deprecate `PartialDenormalizationException::getErrors()`, call `getNotNormalizableValueErrors()` instead
1517

1618
6.1
1719
---

‎src/Symfony/Component/Serializer/Context/SerializerContextBuilder.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Context/SerializerContextBuilder.php
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Serializer\Context;
1313

14+
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
1415
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
1516
use Symfony\Component\Serializer\Serializer;
1617

@@ -36,4 +37,9 @@ public function withCollectDenormalizationErrors(?bool $collectDenormalizationEr
3637
{
3738
return $this->with(DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS, $collectDenormalizationErrors);
3839
}
40+
41+
public function withCollectExtraAttributesErrors(?bool $collectExtraAttributesErrors): static
42+
{
43+
return $this->with(DenormalizerInterface::COLLECT_EXTRA_ATTRIBUTES_ERRORS, $collectExtraAttributesErrors);
44+
}
3945
}

‎src/Symfony/Component/Serializer/Exception/PartialDenormalizationException.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Exception/PartialDenormalizationException.php
+34-5Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,51 @@
1616
*/
1717
class PartialDenormalizationException extends UnexpectedValueException
1818
{
19-
private $data;
20-
private $errors;
19+
private ?ExtraAttributesException $extraAttributesError = null;
2120

22-
public function __construct($data, array $errors)
21+
public function __construct(
22+
private mixed $data,
23+
/**
24+
* @var NotNormalizableValueException[]
25+
*/
26+
private array $notNormalizableErrors,
27+
array $extraAttributesErrors = []
28+
)
2329
{
2430
$this->data = $data;
25-
$this->errors = $errors;
31+
$this->notNormalizableErrors = $notNormalizableErrors;
32+
$extraAttributes = [];
33+
foreach ($extraAttributesErrors as $error) {
34+
$extraAttributes = \array_merge($extraAttributes, $error->getExtraAttributes());
35+
}
36+
if ($extraAttributes) {
37+
$this->extraAttributesError = new ExtraAttributesException($extraAttributes);
38+
}
2639
}
2740

2841
public function getData()
2942
{
3043
return $this->data;
3144
}
3245

46+
/**
47+
* @deprecated since Symfony 6.2, use getNotNormalizableValueErrors() instead.
48+
*/
3349
public function getErrors(): array
3450
{
35-
return $this->errors;
51+
return $this->getNotNormalizableValueErrors();
52+
}
53+
54+
/**
55+
* @return NotNormalizableValueException[]
56+
*/
57+
public function getNotNormalizableValueErrors(): array
58+
{
59+
return $this->notNormalizableErrors;
60+
}
61+
62+
public function getExtraAttributesError(): ?ExtraAttributesException
63+
{
64+
return $this->extraAttributesError;
3665
}
3766
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Serializer\Normalizer;
1313

14+
use Symfony\Component\PropertyAccess\PropertyPath;
1415
use Symfony\Component\Serializer\Exception\CircularReferenceException;
1516
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
1617
use Symfony\Component\Serializer\Exception\LogicException;
@@ -505,7 +506,7 @@ protected function getAttributeNormalizationContext(object $object, string $attr
505506
*/
506507
protected function getAttributeDenormalizationContext(string $class, string $attribute, array $context): array
507508
{
508-
$context['deserialization_path'] = ($context['deserialization_path'] ?? false) ? $context['deserialization_path'].'.'.$attribute : $attribute;
509+
$context['deserialization_path'] = PropertyPath::append($context['deserialization_path'] ?? '', $attribute);
509510

510511
if (null === $metadata = $this->getAttributeMetadata($class, $attribute)) {
511512
return $context;

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php
+9-4Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
1616
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
1717
use Symfony\Component\PropertyAccess\PropertyAccess;
18+
use Symfony\Component\PropertyAccess\PropertyPath;
1819
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
1920
use Symfony\Component\PropertyInfo\Type;
2021
use Symfony\Component\Serializer\Encoder\CsvEncoder;
@@ -228,12 +229,12 @@ protected function instantiateObject(array &$data, string $class, array &$contex
228229
{
229230
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
230231
if (!isset($data[$mapping->getTypeProperty()])) {
231-
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('Type property "%s" not found for the abstract object "%s".', $mapping->getTypeProperty(), $class), null, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), false);
232+
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('Type property "%s" not found for the abstract object "%s".', $mapping->getTypeProperty(), $class), null, ['string'], PropertyPath::append($context['deserialization_path'] ?? '', $mapping->getTypeProperty()), false);
232233
}
233234

234235
$type = $data[$mapping->getTypeProperty()];
235236
if (null === ($mappedClass = $mapping->getClassForType($type))) {
236-
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type "%s" is not a valid value.', $type), $type, ['string'], isset($context['deserialization_path']) ? $context['deserialization_path'].'.'.$mapping->getTypeProperty() : $mapping->getTypeProperty(), true);
237+
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type "%s" is not a valid value.', $type), $type, ['string'], PropertyPath::append($context['deserialization_path'] ?? '', $mapping->getTypeProperty()), true);
237238
}
238239

239240
if ($mappedClass !== $class) {
@@ -398,8 +399,12 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
398399
}
399400
}
400401

401-
if ($extraAttributes) {
402-
throw new ExtraAttributesException($extraAttributes);
402+
if (!$extraAttributes) {
403+
$extraAttributeException = new ExtraAttributesException(array_map(fn (string $extraAttribute) => PropertyPath::append($context['deserialization_path'] ?? '', $extraAttribute), $extraAttributes));
404+
if (!isset($context['extra_attributes_exceptions'])) {
405+
throw $extraAttributeException;
406+
}
407+
$context['extra_attributes_exceptions'][] = $extraAttributeException;
403408
}
404409

405410
return $object;

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Serializer\Normalizer;
1313

14+
use Symfony\Component\PropertyAccess\PropertyPath;
1415
use Symfony\Component\PropertyInfo\Type;
1516
use Symfony\Component\Serializer\Exception\BadMethodCallException;
1617
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
@@ -47,7 +48,7 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
4748
$builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null;
4849
foreach ($data as $key => $value) {
4950
$subContext = $context;
50-
$subContext['deserialization_path'] = ($context['deserialization_path'] ?? false) ? sprintf('%s[%s]', $context['deserialization_path'], $key) : "[$key]";
51+
$subContext['deserialization_path'] = PropertyPath::append($context['deserialization_path'] ?? '', "[$key]");
5152

5253
if (null !== $builtinType && !('is_'.$builtinType)($key)) {
5354
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, get_debug_type($key)), $key, [$builtinType], $subContext['deserialization_path'] ?? null, true);

0 commit comments

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