8000 bug #53107 [HttpKernel] Don't validate partially denormalized object … · symfony/symfony@eef0d54 · GitHub
[go: up one dir, main page]

Skip to content

Commit eef0d54

Browse files
bug #53107 [HttpKernel] Don't validate partially denormalized object (HypeMC)
This PR was merged into the 6.3 branch. Discussion ---------- [HttpKernel] Don't validate partially denormalized object | Q | A | ------------- | --- | Branch? | 6.3 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | Fix #53075 | License | MIT When one or more required constructor arguments are missing, the object is instantiated with `newInstanceWithoutConstructor()`: https://github.com/symfony/symfony/blob/0af28a02dbfabef351de8a739dee65830ed0271a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php#L415-L421 Since this means that all properties are empty, validating the entire object is unnecessary. This PR suggests avoiding the call to the validator when violations have already been identified through the `PartialDenormalizationException`. Commits ------- f9a0126 [HttpKernel] Don't validate partially denormalized object
2 parents 5a2ebb7 + f9a0126 commit eef0d54

File tree

2 files changed

+54
-8
lines changed

2 files changed

+54
-8
lines changed

src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event): vo
119119
$payload = $e->getData();
120120
}
121121

122-
if (null !== $payload) {
122+
if (null !== $payload && !\count($violations)) {
123123
$violations->addAll($this->validator->validate($payload, null, $argument->validationGroups ?? null));
124124
}
125125

src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/RequestPayloadValueResolverTest.php

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
2828
use Symfony\Component\Serializer\Serializer;
2929
use Symfony\Component\Validator\Constraints as Assert;
30-
use Symfony\Component\Validator\ConstraintViolation;
3130
use Symfony\Component\Validator\ConstraintViolationList;
3231
use Symfony\Component\Validator\Exception\ValidationFailedException;
3332
use Symfony\Component\Validator\Validator\ValidatorInterface;
@@ -226,14 +225,11 @@ public function testWithoutValidatorAndCouldNotDenormalize()
226225
public function testValidationNotPassed()
227226
{
228227
$content = '{"price": 50, "title": ["not a string"]}';
229-
$payload = new RequestPayload(50);
230228
$serializer = new Serializer([new ObjectNormalizer()], ['json' => new JsonEncoder()]);
231229

232230
$validator = $this->createMock(ValidatorInterface::class);
233-
$validator->expects($this->once())
234-
->method('validate')
235-
->with($payload)
236-
->willReturn(new ConstraintViolationList([new ConstraintViolation('Test', null, [], '', null, '')]));
231+
$validator->expects($this->never())
232+
->method('validate');
237233

238234
$resolver = new RequestPayloadValueResolver($serializer, $validator);
239235

@@ -253,7 +249,36 @@ public function testValidationNotPassed()
253249
$validationFailedException = $e->getPrevious();
254250
$this->assertInstanceOf(ValidationFailedException::class, $validationFailedException);
255251
$this->assertSame('This value should be of type unknown.', $validationFailedException->getViolations()[0]->getMessage());
256-
$this->assertSame('Test', $validationFailedException->getViolations()[1]->getMessage());
252+
}
253+
}
254+
255+
public function testValidationNotPerformedWhenPartialDenormalizationReturnsViolation()
256+
{
257+
$content = '{"password": "abc"}';
258+
$serializer = new Serializer([new ObjectNormalizer()], ['json' => new JsonEncoder()]);
259+
260+
$validator = $this->createMock(ValidatorInterface::class);
261+
$validator->expects($this->never())
262+
->method('validate');
263+
264+
$resolver = new RequestPayloadValueResolver($serializer, $validator);
265+
266+
$argument = new ArgumentMetadata('invalid', User::class, false, false, null, false, [
267+
MapRequestPayload::class => new MapRequestPayload(),
268+
]);
269+
$request = Request::create('/', 'POST', server: ['CONTENT_TYPE' => 'application/json'], content: $content);
270+
271+
$kernel = $this->createMock(HttpKernelInterface::class);
272+
$arguments = $resolver->resolve($request, $argument);
273+
$event = new ControllerArgumentsEvent($kernel, function () {}, $arguments, $request, HttpKernelInterface::MAIN_REQUEST);
274+
275+
try {
276+
$resolver->onKernelControllerArguments($event);
277+
$this->fail(sprintf('Expected "%s" to be thrown.', HttpException::class));
278+
} catch (HttpException $e) {
279+
$validationFailedException = $e->getPrevious();
280+
$this->assertInstanceOf(ValidationFailedException::class, $validationFailedException);
281+
$this->assertSame('This value should be of type unknown.', $validationFailedException->getViolations()[0]->getMessage());
257282
}
258283
}
259284

@@ -612,3 +637,24 @@ public function __construct(public readonly float $price)
612637
{
613638
}
614639
}
640+
641+
class User
642+
{
643+
public function __construct(
644+
#[Assert\NotBlank, Assert\Email]
645+
private string $email,
646+
#[Assert\NotBlank]
647+
private string $password,
648+
) {
649+
}
650+
651+
public function getEmail(): string
652+
{
653+
return $this->email;
654+
}
655+
656+
public function getPassword(): string
657+
{
658+
return $this->password;
659+
}
660+
}

0 commit comments

Comments
 (0)
0