Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/Symfony/Component/HttpKernel/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ CHANGELOG
8.1
---

* Validate typed route parameters before calling controllers and return an HTTP error when an invalid value is provided
* Add `ControllerAttributeEvent` et al. to dispatch events named after controller attributes
* Add support for `UploadedFile` when using `MapRequestPayload`
* Add support for bundles as compiler pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,34 +35,34 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable
return [];
}

$name = $argument->getName();

// do not support if no value can be resolved at all
// letting the \Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver be used
// or \Symfony\Component\HttpKernel\Controller\ArgumentResolver fail with a meaningful error.
if (!$request->attributes->has($argument->getName())) {
if (!$request->attributes->has($name)) {
return [];
}

$value = $request->attributes->get($argument->getName());

if (null === $value) {
if (null === $value = $request->attributes->get($name)) {
return [null];
}

if ($value instanceof \BackedEnum) {
return [$value];
}

/** @var class-string<\BackedEnum> $type */
$type = $argument->getType();

if (!\is_int($value) && !\is_string($value)) {
throw new \LogicException(\sprintf('Could not resolve the "%s $%s" controller argument: expecting an int or string, got "%s".', $argument->getType(), $argument->getName(), get_debug_type($value)));
throw new NotFoundHttpException(\sprintf('Could not resolve the "%s $%s" controller argument: expecting an int or string, got "%s".', $type, $name, get_debug_type($value)));
}

/** @var class-string<\BackedEnum> $enumType */
$enumType = $argument->getType();

try {
return [$enumType::from($value)];
return [$type::from($value)];
} catch (\ValueError|\TypeError $e) {
throw new NotFoundHttpException(\sprintf('Could not resolve the "%s $%s" controller argument: ', $argument->getType(), $argument->getName()).$e->getMessage(), $e);
throw new NotFoundHttpException(\sprintf('Could not resolve the "%s $%s" controller argument: ', $type, $name).$e->getMessage(), $e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
* Yields a non-variadic argument's value from the request attributes.
Expand All @@ -24,6 +25,45 @@ final class RequestAttributeValueResolver implements ValueResolverInterface
{
public function resolve(Request $request, ArgumentMetadata $argument): array
{
return !$argument->isVariadic() && $request->attributes->has($argument->getName()) ? [$request->attributes->get($argument->getName())] : [];
if ($argument->isVariadic()) {
return [];
}

$name = $argument->getName();
if (!$request->attributes->has($name)) {
return [];
}

$value = $request->attributes->get($name);

if (null === $value && $argument->isNullable()) {
return [null];
}

$type = $argument->getType();

// Skip when no type declaration or complex types; fall back to other resolvers/defaults
if (null === $type || str_contains($type, '|') || str_contains($type, '&')) {
return [$value];
}

if ('string' === $type) {
if (!\is_scalar($value) && !$value instanceof \Stringable) {
throw new NotFoundHttpException(\sprintf('The value for the "%s" route parameter is invalid.', $name));
}

$value = (string) $value;
} elseif ($filter = match ($type) {
'int' => \FILTER_VALIDATE_INT,
'float' => \FILTER_VALIDATE_FLOAT,
'bool' => \FILTER_VALIDATE_BOOL,
default => null,
}) {
if (null === $value = $request->attributes->filter($name, null, $filter, ['flags' => \FILTER_NULL_ON_FAILURE | \FILTER_REQUIRE_SCALAR])) {
throw new NotFoundHttpException(\sprintf('The value for the "%s" route parameter is invalid.', $name));
}
}

return [$value];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public function testResolveThrowsOnUnexpectedType()
$request = self::createRequest(['suit' => false]);
$metadata = self::createArgumentMetadata('suit', Suit::class);

$this->expectException(\LogicException::class);
$this->expectException(NotFoundHttpException::class);
$this->expectExceptionMessage('Could not resolve the "Symfony\Component\HttpKernel\Tests\Fixtures\Suit $suit" controller argument: expecting an int or string, got "bool".');

$resolver->resolve($request, $metadata);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* 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\RequestAttributeValueResolver;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class RequestAttributeValueResolverTest extends TestCase
{
public function testValidIntWithinRangeWorks()
{
$resolver = new RequestAttributeValueResolver();
$request = new Request();
$request->attributes->set('id', '123');
$metadata = new ArgumentMetadata('id', 'int', false, false, null);

$result = iterator_to_array($resolver->resolve($request, $metadata));

$this->assertSame([123], $result);
}

public function testInvalidStringBecomes404()
{
$resolver = new RequestAttributeValueResolver();
$request = new Request();
$request->attributes->set('id', 'abc');
$metadata = new ArgumentMetadata('id', 'int', false, false, null);

$this->expectException(NotFoundHttpException::class);
iterator_to_array($resolver->resolve($request, $metadata));
}

public function testOutOfRangeIntBecomes404()
{
$resolver = new RequestAttributeValueResolver();
$request = new Request();
// one more than PHP_INT_MAX on 64-bit (string input)
$request->attributes->set('id', '9223372036854775808');
$metadata = new ArgumentMetadata('id', 'int', false, false, null);

$this->expectException(NotFoundHttpException::class);
iterator_to_array($resolver->resolve($request, $metadata));
}

public function testNullableIntAllowsNull()
{
$resolver = new RequestAttributeValueResolver();
$request = new Request();
$request->attributes->set('id', null);
$metadata = new ArgumentMetadata('id', 'int', false, true, null);

$result = iterator_to_array($resolver->resolve($request, $metadata));

$this->assertSame([null], $result);
}
}
Loading