From 6f1cdec9f8a45b67170510540cb76df1fc977a02 Mon Sep 17 00:00:00 2001 From: Konstantin Myakshin Date: Sun, 26 Feb 2023 00:06:56 +0200 Subject: [PATCH] [HttpKernel] Create `#[UploadedFile]` Attribute to map UploadedFile objects to Controller Arguments --- .../FrameworkBundle/Resources/config/web.php | 4 ++ .../HttpKernel/Attribute/MapUploadedFile.php | 30 +++++++++++ src/Symfony/Component/HttpKernel/CHANGELOG.md | 1 + .../UploadedFileValueResolver.php | 52 +++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 src/Symfony/Component/HttpKernel/Attribute/MapUploadedFile.php create mode 100644 src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/UploadedFileValueResolver.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php index 8c6b5a9ba966d..05f84e849a063 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php @@ -21,6 +21,7 @@ use Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\UidValueResolver; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\UploadedFileValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\VariadicValueResolver; use Symfony\Component\HttpKernel\Controller\ErrorController; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory; @@ -64,6 +65,9 @@ ->set('argument_resolver.request_attribute', RequestAttributeValueResolver::class) ->tag('controller.argument_value_resolver', ['priority' => 100, 'name' => RequestAttributeValueResolver::class]) + ->set('argument_resolver.uploaded_file', UploadedFileValueResolver::class) + ->tag('controller.targeted_value_resolver', ['name' => UploadedFileValueResolver::class]) + ->set('argument_resolver.request', RequestValueResolver::class) ->tag('controller.argument_value_resolver', ['priority' => 50, 'name' => RequestValueResolver::class]) diff --git a/src/Symfony/Component/HttpKernel/Attribute/MapUploadedFile.php b/src/Symfony/Component/HttpKernel/Attribute/MapUploadedFile.php new file mode 100644 index 0000000000000..e7519cc9bfaac --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Attribute/MapUploadedFile.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Attribute; + +use Symfony\Component\HttpKernel\Controller\ArgumentResolver\UploadedFileValueResolver; + +/** + * Controller parameter tag to map uploaded files. + * + * @author Konstantin Myakshin + */ +#[\Attribute(\Attribute::TARGET_PARAMETER | \Attribute::IS_REPEATABLE)] +final class MapUploadedFile extends ValueResolver +{ + public function __construct( + public readonly ?string $name = null, + string $resolver = UploadedFileValueResolver::class, + ) { + parent::__construct($resolver); + } +} diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index b3fbb240d4a65..60ae98b3646b7 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -11,6 +11,7 @@ CHANGELOG * Add `#[WithLogLevel]` for defining log levels for exceptions * Add `skip_response_headers` to the `HttpCache` options * Introduce targeted value resolvers with `#[ValueResolver]` and `#[AsTargetedValueResolver]` + * Add `#[MapUploadedFiles]` to map uploaded files to controller arguments 6.2 --- diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/UploadedFileValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/UploadedFileValueResolver.php new file mode 100644 index 0000000000000..3268a905c0231 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/UploadedFileValueResolver.php @@ -0,0 +1,52 @@ + + * + * 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\Attribute\MapUploadedFile; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + +/** + * @author Konstantin Myakshin + */ +final class UploadedFileValueResolver implements ValueResolverInterface +{ + public function resolve(Request $request, ArgumentMetadata $argument): iterable + { + if (!$attribute = $argument->getAttributesOfType(MapUploadedFile::class)[0] ?? null) { + return []; + } + + $name = $attribute->name ?? $argument->getName(); + + if (!$request->files->has($name)) { + if ($argument->isNullable() || $argument->hasDefaultValue()) { + return []; + } + + if ('array' === $argument->getType()) { + return [[]]; + } + + throw new NotFoundHttpException(sprintf('Missing uploaded file "%s".', $name)); + } + + $value = $request->files->all()[$name]; + if ('array' === $argument->getType()) { + $value = (array) $value; + } + + return $argument->isVariadic() ? $value : [$value]; + } +}