Skip to content

Commit b01db33

Browse files
committed
Add COLLECT_EXTRA_ATTRIBUTES_ERRORS and full deserialization path
1 parent c2af1fd commit b01db33

16 files changed

+346
-29
lines changed

UPGRADE-6.2.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ Serializer
9090
* Deprecate calling `AttributeMetadata::setSerializedName()`, `ClassMetadata::setClassDiscriminatorMapping()` without arguments
9191
* Change the signature of `AttributeMetadataInterface::setSerializedName()` to `setSerializedName(?string)`
9292
* Change the signature of `ClassMetadataInterface::setClassDiscriminatorMapping()` to `setClassDiscriminatorMapping(?ClassDiscriminatorMapping)`
93+
* Deprecate `PartialDenormalizationException::getErrors()`, call `getNotNormalizableValueErrors()` instead
9394

9495
Validator
9596
---------

src/Symfony/Component/Console/Helper/Table.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public function setHeaders(array $headers): static
192192
/**
193193
* @return $this
194194
*/
195-
public function setRows(array $rows)
195+
public function setRows(array $rows): static
196196
{
197197
$this->rows = [];
198198

@@ -251,7 +251,7 @@ public function appendRow(TableSeparator|array $row): static
251251
/**
252252
* @return $this
253253
*/
254-
public function setRow(int|string $column, array $row): static
254+
public function setRow(int|string $column, array $row)
255255
{
256256
$this->rows[$column] = $row;
257257

src/Symfony/Component/PropertyAccess/CHANGELOG.md

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

77
* Deprecate calling `PropertyAccessorBuilder::setCacheItemPool()` without arguments
88
* Added method `isNullSafe()` to `PropertyPathInterface`
9+
* Add `PropertyPath::append()`
910

1011
6.0
1112
---

src/Symfony/Component/PropertyAccess/PropertyPath.php

Lines 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

Lines 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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ CHANGELOG
1111
* Change the signature of `AttributeMetadataInterface::setSerializedName()` to `setSerializedName(?string)`
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.
14+
* Add `COLLECT_EXTRA_ATTRIBUTES_ERRORS` option to `Serializer` to collect errors from nested denormalizations
15+
* Deprecate `PartialDenormalizationException::getErrors()`, call `getNotNormalizableValueErrors()` instead
1416

1517
6.1
1618
---

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

Lines 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

Lines 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 $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 (\count($extraAttributes) > 0) {
37+
$this->extraAttributesError = new ExtraAttributesException($extraAttributes);
38+
}
2639
}
2740

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

46+
/**
47+
* @deprecated Use getNotNormalizableValueErrors() instead.
48+
*/
3349
public function getErrors(): array
3450
{
35-
return $this->errors;
51+
return $this->getNotNormalizableValueErrors();
52+
}
53+
54+
/**
55+
* @var 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

Lines 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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
1515
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
1616
use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
17+
use Symfony\Component\PropertyAccess\PropertyPath;
1718
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
1819
use Symfony\Component\PropertyInfo\Type;
1920
use Symfony\Component\Serializer\Encoder\CsvEncoder;
@@ -225,12 +226,12 @@ protected function instantiateObject(array &$data, string $class, array &$contex
225226
{
226227
if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) {
227228
if (!isset($data[$mapping->getTypeProperty()])) {
228-
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);
229+
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);
229230
}
230231

231232
$type = $data[$mapping->getTypeProperty()];
232233
if (null === ($mappedClass = $mapping->getClassForType($type))) {
233-
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);
234+
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type "%s" is not a valid value.', $type), $type, ['string'], PropertyPath::append($context['deserialization_path'] ?? '', $mapping->getTypeProperty()), true);
234235
}
235236

236237
if ($mappedClass !== $class) {
@@ -378,8 +379,12 @@ public function denormalize(mixed $data, string $type, string $format = null, ar
378379
}
379380
}
380381

381-
if ($extraAttributes) {
382-
throw new ExtraAttributesException($extraAttributes);
382+
if (!$extraAttributes) {
383+
$extraAttributeException = new ExtraAttributesException(array_map(fn (string $extraAttribute) => PropertyPath::append($context['deserialization_path'] ?? '', $extraAttribute), $extraAttributes));
384+
if (!isset($context['extra_attributes_exceptions'])) {
385+
throw $extraAttributeException;
386+
}
387+
$context['extra_attributes_exceptions'][] = $extraAttributeException;
383388
}
384389

385390
return $object;

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