8000 [HttpKernel] PostValueResolver by jack-worman · Pull Request #47146 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[HttpKernel] PostValueResolver #47146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\HttpKernel\Controller\ArgumentResolver;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

final class PostValueResolver implements ArgumentValueResolverInterface
{
public function supports(Request $request, ArgumentMetadata $argument): bool
{
return [] !== $argument->getAttributesOfType(ResolvePostValue::class);
}

public function resolve(Request $request, ArgumentMetadata $argument): array
{
/**
* @psalm-ignore-var
*
* @var list<ResolvePostValue> $resolveRequestValues
*/
$resolveRequestValues = $argument->getAttributesOfType(ResolvePostValue::class);
if ([] === $resolveRequestValues) {
throw new \LogicException(sprintf('Argument does not have a "%s" attribute.', ResolvePostValue::class));
}
$resolveRequestValue = $resolveRequestValues[0];
$key = $resolveRequestValue->name ?? $argument->getName();
/** @var mixed $default */
$default = $resolveRequestValue->default ?? ($argument->hasDefaultValue() ? $argument->getDefaultValue() : null);
/** @psalm-suppress MixedArgument */
$value = $request->request->get($key, $default);
if (null === $value) {
if ($argument->isNullable()) {
return [null];
} else {
throw new BadRequestHttpException(sprintf('Request param "%s" does not exist.', $key));
}
}
$coercedValue = match ($argument->getType()) {
'bool' => filter_var($value, \FILTER_VALIDATE_BOOL, \FILTER_NULL_ON_FAILURE),
'int' => filter_var($value, \FILTER_VALIDATE_INT, \FILTER_NULL_ON_FAILURE),
'float' => filter_var($value, \FILTER_VALIDATE_FLOAT, \FILTER_NULL_ON_FAILURE),
default => $value,
};
if (null === $coercedValue) {
throw new BadRequestHttpException(sprintf('Request param "%s" could not be coerced to a "%s".', $key, $argument->getType()));
}

return [$coercedValue];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\HttpKernel\Controller\ArgumentResolver;

#[\Attribute(\Attribute::TARGET_PARAMETER)]
final class ResolvePostValue
{
public function __construct(
public readonly string|null $name = null,
public readonly mixed $default = null,
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public function getAttributes(string $name = null, int $flags = 0): array
* @param class-string<T> $name
* @param self::IS_INSTANCEOF|0 $flags
*
* @return array<T>
* @return list<T>
*/
public function getAttributesOfType(string $name, int $flags = 0): array
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\HttpKernel\Tests\Controller\ArgumentResolver;

use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\PostValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\ResolvePostValue;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class PostValueResolverTest extends TestCase
{
public function testSupports()
{
$sut = new PostValueResolver();
$request = new Request();
$argumentMetadata = new ArgumentMetadata(
'',
null,
false,
false,
null,
attributes: [new ResolvePostValue()],
);
$this->assertTrue($sut->supports($request, $argumentMetadata));
$argumentMetadata = new ArgumentMetadata(
'',
null,
false,
false,
null,
);
$this->assertFalse($sut->supports($request, $argumentMetadata));
}

/**
* @dataProvider provideForTestResolve
*/
public function testResolve(
array $post,
ArgumentMetadata $argumentMetadata,
mixed $expectedValue,
) {
$sut = new PostValueResolver();
$request = new Request(request: $post);
$this->assertSame([$expectedValue], $sut->resolve($request, $argumentMetadata));
}

public function provideForTestResolve(): iterable
{
yield 'arg name' => [
['arg_name' => 'value'],
new ArgumentMetadata(
'arg_name',
'string',
false,
false,
null,
false,
[new ResolvePostValue()],
),
'value',
];
yield 'attribute name' => [
['post_name' => 'value'],
new ArgumentMetadata(
'arg_name',
'string',
false,
false,
null,
false,
[new ResolvePostValue('post_name')],
),
'value',
];
yield 'attribute default' => [
[],
new ArgumentMetadata(
'arg_name',
'string',
false,
false,
null,
8000 false,
[new ResolvePostValue(default: 'value')],
),
'value',
];
yield 'argument default' => [
[],
new ArgumentMetadata(
'arg_name',
'string',
false,
true,
'value',
false,
[new ResolvePostValue()],
),
'value',
];
yield 'nullable argument' => [
[],
new ArgumentMetadata(
'arg_name',
'string',
false,
false,
null,
true,
[new ResolvePostValue()],
),
null,
];
yield 'bool coercion - false' => [
['arg_name' => '0'],
new ArgumentMetadata(
'arg_name',
'bool',
false,
false,
null,
false,
[new ResolvePostValue()],
),
false,
];
yield 'bool coercion - true' => [
['arg_name' => '1'],
new ArgumentMetadata(
'arg_name',
'bool',
false,
false,
null,
false,
[new ResolvePostValue()],
),
true,
];
yield 'int coercion' => [
['arg_name' => '13'],
new ArgumentMetadata(
'arg_name',
'int',
false,
false,
null,
false,
[new ResolvePostValue()],
),
13,
];
yield 'float coercion' => [
['arg_name' => '13.0'],
new ArgumentMetadata(
'arg_name',
'float',
false,
false,
null,
false,
[new ResolvePostValue()],
),
13.0,
];
}

public function testLogicException()
{
$sut = new PostValueResolver();
$request = new Request();
$argumentMetadata = new ArgumentMetadata('', null, false, false, null);
$this->expectException(\LogicException::class);
$this->expectExceptionMessage('Argument does not have a "Symfony\Component\HttpKernel\Controller\ArgumentResolver\ResolvePostValue" attribute.');
$sut->resolve($request, $argumentMetadata);
}

public function testPostParamDoesNotExistException()
{
$sut = new PostValueResolver();
$request = new Request();
$argumentMetadata = new ArgumentMetadata('arg_name', 'string', false, false, null, attributes: [new ResolvePostValue()]);
$this->expectException(BadRequestHttpException::class);
$this->expectExceptionMessage('Request param "arg_name" does not exist.');
$sut->resolve($request, $argumentMetadata);
}

public function testPostParamCanNotBeCoercedException()
{
$sut = new PostValueResolver();
$request = new Request(request: ['arg_name' => 'bogus']);
$argumentMetadata = new ArgumentMetadata('arg_name', 'bool', false, false, null, attributes: [new ResolvePostValue()]);
$this->expectException(BadRequestHttpException::class);
$this->expectExceptionMessage('Request param "arg_name" could not be coerced to a "bool".');
$sut->resolve($request, $argumentMetadata);
}
}
0