Skip to content

Commit b7b9bdb

Browse files
committed
fix(serializer): use attribute denormalization context for constructor arguments
1 parent 717c7e5 commit b7b9bdb

File tree

3 files changed

+34
-7
lines changed

3 files changed

+34
-7
lines changed

features/security/strong_typing.feature

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ Feature: Handle properly invalid data submitted to the API
8989
And the response should be in JSON
9090
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
9191

92+
Scenario: Ignore date with wrong format
93+
When I add "Content-Type" header equal to "application/ld+json"
94+
And I send a "POST" request to "/dummies" with body:
95+
"""
96+
{
97+
"name": "Invalid date format",
98+
"dummyDateWithFormat": "2020-01-01T00:00:00+00:00"
99+
}
100+
"""
101+
Then the response status code should be 400
102+
And the response should be in JSON
103+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
104+
92105
Scenario: Send non-array data when an array is expected
93106
When I add "Content-Type" header equal to "application/ld+json"
94107
And I send a "POST" request to "/dummies" with body:

src/Serializer/AbstractItemNormalizer.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@ protected function instantiateObject(array &$data, string $class, array &$contex
317317
foreach ($constructorParameters as $constructorParameter) {
318318
$paramName = $constructorParameter->name;
319319
$key = $this->nameConverter ? $this->nameConverter->normalize($paramName, $class, $format, $context) : $paramName;
320+
$attributeContext = $this->getAttributeDenormalizationContext($class, $paramName, $context);
321+
$attributeContext['deserialization_path'] = $attributeContext['deserialization_path'] ?? $key;
320322

321323
$allowed = false === $allowedAttributes || (\is_array($allowedAttributes) && \in_array($paramName, $allowedAttributes, true));
322324
$ignored = !$this->isAllowedAttribute($class, $paramName, $format, $context);
@@ -329,10 +331,8 @@ protected function instantiateObject(array &$data, string $class, array &$contex
329331
$params[] = $data[$paramName];
330332
}
331333
} elseif ($allowed && !$ignored && (isset($data[$key]) || \array_key_exists($key, $data))) {
332-
$constructorContext = $context;
333-
$constructorContext['deserialization_path'] = $context['deserialization_path'] ?? $key;
334334
try {
335-
$params[] = $this->createConstructorArgument($data[$key], $key, $constructorParameter, $constructorContext, $format);
335+
$params[] = $this->createConstructorArgument($data[$key], $key, $constructorParameter, $attributeContext, $format);
336336
} catch (NotNormalizableValueException $exception) {
337337
if (!isset($context['not_normalizable_value_exceptions'])) {
338338
throw $exception;
@@ -351,7 +351,6 @@ protected function instantiateObject(array &$data, string $class, array &$contex
351351
$missingConstructorArguments[] = $constructorParameter->name;
352352
}
353353

354-
$attributeContext = $this->getAttributeDenormalizationContext($class, $paramName, $context);
355354
$constructorParameterType = 'unknown';
356355
$reflectionType = $constructorParameter->getType();
357356
if ($reflectionType instanceof \ReflectionNamedType) {
@@ -362,7 +361,7 @@ protected function instantiateObject(array &$data, string $class, array &$contex
362361
\sprintf('Failed to create object because the class misses the "%s" property.', $constructorParameter->name),
363362
null,
364363
[$constructorParameterType],
365-
$attributeContext['deserialization_path'] ?? null,
364+
$attributeContext['deserialization_path'],
366365
true
367366
);
368367
$context['not_normalizable_value_exceptions'][] = $exception;
@@ -405,7 +404,7 @@ protected function getClassDiscriminatorResolvedClass(array $data, string $class
405404
return $mappedClass;
406405
}
407406

408-
protected function createConstructorArgument($parameterData, string $key, \ReflectionParameter $constructorParameter, array &$context, ?string $format = null): mixed
407+
protected function createConstructorArgument($parameterData, string $key, \ReflectionParameter $constructorParameter, array $context, ?string $format = null): mixed
409408
{
410409
return $this->createAndValidateAttributeValue($constructorParameter->name, $parameterData, $format, $context);
411410
}

tests/Fixtures/TestBundle/Entity/Dummy.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Doctrine\Common\Collections\ArrayCollection;
2121
use Doctrine\Common\Collections\Collection;
2222
use Doctrine\ORM\Mapping as ORM;
23+
use Symfony\Component\Serializer\Attribute\Context;
2324
use Symfony\Component\Validator\Constraints as Assert;
2425

2526
/**
@@ -87,6 +88,14 @@ class Dummy
8788
#[ORM\Column(type: 'datetime', nullable: true)]
8889
public $dummyDate;
8990

91+
/**
92+
* @var \DateTime|null A dummy date with format
93+
*/
94+
#[Context(denormalizationContext: ['datetime_format' => 'Y-m-d'])]
95+
#[ApiProperty(iris: ['https://schema.org/DateTime'])]
96+
#[ORM\Column(type: 'datetime', nullable: true)]
97+
private $dummyDateWithFormat;
98+
9099
/**
91100
* @var float|null A dummy float
92101
*/
@@ -140,9 +149,10 @@ public static function staticMethod(): void
140149
{
141150
}
142151

143-
public function __construct()
152+
public function __construct(?\DateTime $dummyDateWithFormat = null)
144153
{
145154
$this->relatedDummies = new ArrayCollection();
155+
$this->dummyDateWithFormat = $dummyDateWithFormat;
146156
}
147157

148158
public function getId()
@@ -209,6 +219,11 @@ public function getDummyDate()
209219
return $this->dummyDate;
210220
}
211221

222+
public function getDummyDateWithFormat()
223+
{
224+
return $this->dummyDateWithFormat;
225+
}
226+
212227
public function setDummyPrice($dummyPrice)
213228
{
214229
$this->dummyPrice = $dummyPrice;

0 commit comments

Comments
 (0)