From ce650a9a38df56b8914779022f001671246e2a35 Mon Sep 17 00:00:00 2001 From: Thomas Hanke Date: Mon, 16 Jan 2023 21:56:46 +0100 Subject: [PATCH 1/4] add RequestValidator --- .../Resources/config/validator.php | 8 ++ .../Validator/Attribute/RequestValidator.php | 29 ++++++ .../RequestValidationSubscriber.php | 88 +++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 src/Symfony/Component/Validator/Attribute/RequestValidator.php create mode 100644 src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php index c397e73d42505..116417132ed80 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php @@ -19,6 +19,7 @@ use Symfony\Component\Validator\Constraints\NotCompromisedPasswordValidator; use Symfony\Component\Validator\Constraints\WhenValidator; use Symfony\Component\Validator\ContainerConstraintValidatorFactory; +use Symfony\Component\Validator\EventListener\RequestValidationSubscriber; use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader; use Symfony\Component\Validator\Validation; use Symfony\Component\Validator\Validator\ValidatorInterface; @@ -109,5 +110,12 @@ service('property_info'), ]) ->tag('validator.auto_mapper') + + ->set('validator.request_validator', RequestValidationSubscriber::class) + ->args([ + service('serializer')->nullOnInvalid(), + service('validator') + ]) + ->tag('kernel.event_subscriber') ; }; diff --git a/src/Symfony/Component/Validator/Attribute/RequestValidator.php b/src/Symfony/Component/Validator/Attribute/RequestValidator.php new file mode 100644 index 0000000000000..d00fc345e62ff --- /dev/null +++ b/src/Symfony/Component/Validator/Attribute/RequestValidator.php @@ -0,0 +1,29 @@ +class = $class; + $this->finalize = $finalize; + } + + public function getClass(): string + { + return $this->class; + } + + public function getFinalize(): ?string + { + return $this->finalize; + } +} diff --git a/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php b/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php new file mode 100644 index 0000000000000..ff406d7ea6697 --- /dev/null +++ b/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php @@ -0,0 +1,88 @@ + 'validateRequest' + ]; + } + + public function validateRequest(ControllerArgumentsEvent $event): void { + $controller = $event->getController(); + $arguments = $event->getArguments(); + $reflectionMethod = $this->getReflectionMethod($controller); + $request = $event->getRequest(); + + $attributes = $reflectionMethod->getAttributes(RequestValidator::class, \ReflectionAttribute::IS_INSTANCEOF); + + if(count($attributes) === 0) { + return; + } + + // only first attribute can validate + $attribute = $attributes[0]; + + $class = $attribute->getArguments()['class']; + + $object = new $class(); + + // if serializer is installed serialize input body + if(null !== $this->serializer) { + $object = $this->serializer->deserialize($request->getContent(), $class, 'json'); + } + + // set input variables in object + foreach ($request->request as $key => $input) { + $object->{$key} = $input; + } + + // set parameter variables in object + foreach ($request->attributes as $key => $parameter) { + $object->{$key} = $parameter; + } + + $violations = $this->validator->validate($object); + + if(count($violations) > 0) { + throw new ValidationFailedException(sprintf("Validation of %s failed!", $class), $violations); + } + + foreach ($arguments as $index => $argument) { + if(!$argument instanceof $class) { + continue; + } + $arguments[$index] = $object; + } + + $event->setArguments($arguments); + } + + private function getReflectionMethod(callable $controller): \ReflectionMethod + { + if (is_array($controller)) { + $class = $controller[0]; + $method = $controller[1]; + } else { + /** @var object $controller */ + $class = $controller; + $method = '__invoke'; + } + + return new \ReflectionMethod($class, $method); + } +} From e88d5abf79194dcd03149820bc1e93aa3ae2a2e4 Mon Sep 17 00:00:00 2001 From: Thomas Hanke Date: Mon, 16 Jan 2023 22:59:03 +0100 Subject: [PATCH 2/4] add order, override flag and serializerFormat --- .../Resources/config/validator.php | 2 +- .../Validator/Attribute/RequestValidator.php | 32 +++++----- .../RequestValidationSubscriber.php | 61 ++++++++++++++----- 3 files changed, 61 insertions(+), 34 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php index 116417132ed80..6eaafb704afdc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php @@ -113,8 +113,8 @@ ->set('validator.request_validator', RequestValidationSubscriber::class) ->args([ + service('validator'), service('serializer')->nullOnInvalid(), - service('validator') ]) ->tag('kernel.event_subscriber') ; diff --git a/src/Symfony/Component/Validator/Attribute/RequestValidator.php b/src/Symfony/Component/Validator/Attribute/RequestValidator.php index d00fc345e62ff..ef37b6b300707 100644 --- a/src/Symfony/Component/Validator/Attribute/RequestValidator.php +++ b/src/Symfony/Component/Validator/Attribute/RequestValidator.php @@ -5,25 +5,21 @@ #[\Attribute(\Attribute::TARGET_FUNCTION)] final class RequestValidator { - private string $class; - private ?string $finalize; + public const ORDER_ATTRIBUTES = 'attributes'; + public const ORDER_SERIALIZE = 'serialize'; + public const ORDER_QUERY = 'query'; + public const ORDER_REQUEST = 'request'; public function __construct( - string $class, - ?string $finalize = null - ) - { - $this->class = $class; - $this->finalize = $finalize; - } - - public function getClass(): string - { - return $this->class; - } - - public function getFinalize(): ?string - { - return $this->finalize; + public string $class, + public bool $override = true, + public array $order = [ + self::ORDER_SERIALIZE, + self::ORDER_ATTRIBUTES, + self::ORDER_QUERY, + self::ORDER_REQUEST, + ], + public string $serializedFormat = 'json' + ) { } } diff --git a/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php b/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php index ff406d7ea6697..0552e20bbce85 100644 --- a/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php +++ b/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php @@ -4,15 +4,19 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent; +use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Validator\Attribute\RequestValidator; +use Symfony\Component\Validator\Exception\LogicException; use Symfony\Component\Validator\Exception\ValidationFailedException; use Symfony\Component\Validator\Validator\ValidatorInterface; class RequestValidationSubscriber implements EventSubscriberInterface { - public function __construct(readonly ?SerializerInterface $serializer = null, readonly ValidatorInterface $validator) - { + public function __construct( + readonly ValidatorInterface $validator, + readonly ?SerializerInterface $serializer = null + ) { } public static function getSubscribedEvents() @@ -30,7 +34,7 @@ public function validateRequest(ControllerArgumentsEvent $event): void { $attributes = $reflectionMethod->getAttributes(RequestValidator::class, \ReflectionAttribute::IS_INSTANCEOF); - if(count($attributes) === 0) { + if (count($attributes) === 0) { return; } @@ -38,22 +42,30 @@ public function validateRequest(ControllerArgumentsEvent $event): void { $attribute = $attributes[0]; $class = $attribute->getArguments()['class']; + $override = $attribute->getArguments()['override']; + $serializedFormat = $attribute->getArguments()['serializedFormat']; + $order = $attribute->getArguments()['order']; $object = new $class(); - // if serializer is installed serialize input body - if(null !== $this->serializer) { - $object = $this->serializer->deserialize($request->getContent(), $class, 'json'); - } - - // set input variables in object - foreach ($request->request as $key => $input) { - $object->{$key} = $input; - } + foreach ($order as $type) { + switch ($type) { + case RequestValidator::ORDER_SERIALIZE: + $serializer = $this->getSerializer(); + $serializer->deserialize($request->getContent(), $class, $serializedFormat, + [AbstractNormalizer::OBJECT_TO_POPULATE => $object]); + continue 2; + case RequestValidator::ORDER_REQUEST: + $this->setProperties($object, $request->request, $override); + break; + case RequestValidator::ORDER_QUERY: + $this->setProperties($object, $request->query, $override); + break; + case RequestValidator::ORDER_ATTRIBUTES: + $this->setProperties($object, $request->attributes, $override); + break; + } - // set parameter variables in object - foreach ($request->attributes as $key => $parameter) { - $object->{$key} = $parameter; } $violations = $this->validator->validate($object); @@ -72,6 +84,15 @@ public function validateRequest(ControllerArgumentsEvent $event): void { $event->setArguments($arguments); } + private function setProperties(object $object, \IteratorAggregate $bag, bool $override) { + foreach ($bag as $key => $value) { + if(false === $override && property_exists($object, $key)) { + continue; + } + $object->{$key} = $value; + } + } + private function getReflectionMethod(callable $controller): \ReflectionMethod { if (is_array($controller)) { @@ -85,4 +106,14 @@ private function getReflectionMethod(callable $controller): \ReflectionMethod return new \ReflectionMethod($class, $method); } + + private function getSerializer(): SerializerInterface + { + if (!class_exists(SerializerInterface::class)) { + throw new LogicException(sprintf('The "symfony/serializer" component is required to use the "%s" validator. Try running "composer require symfony/serializer".', + __CLASS__)); + } + + return $this->serializer; + } } From aa69324081232e76f53b0bcf78ec3fff49d2826f Mon Sep 17 00:00:00 2001 From: Thomas Hanke Date: Sat, 21 Jan 2023 14:57:39 +0100 Subject: [PATCH 3/4] fixing some bugs in requestValidation --- .../Validator/Attribute/RequestValidator.php | 2 +- .../RequestValidationSubscriber.php | 44 ++++++++++++++----- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/Validator/Attribute/RequestValidator.php b/src/Symfony/Component/Validator/Attribute/RequestValidator.php index ef37b6b300707..3ba871036947c 100644 --- a/src/Symfony/Component/Validator/Attribute/RequestValidator.php +++ b/src/Symfony/Component/Validator/Attribute/RequestValidator.php @@ -2,7 +2,7 @@ namespace Symfony\Component\Validator\Attribute; -#[\Attribute(\Attribute::TARGET_FUNCTION)] +#[\Attribute(\Attribute::TARGET_METHOD)] final class RequestValidator { public const ORDER_ATTRIBUTES = 'attributes'; diff --git a/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php b/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php index 0552e20bbce85..3c853c54ee283 100644 --- a/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php +++ b/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php @@ -5,6 +5,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; +use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Validator\Attribute\RequestValidator; use Symfony\Component\Validator\Exception\LogicException; @@ -41,28 +42,49 @@ public function validateRequest(ControllerArgumentsEvent $event): void { // only first attribute can validate $attribute = $attributes[0]; - $class = $attribute->getArguments()['class']; - $override = $attribute->getArguments()['override']; - $serializedFormat = $attribute->getArguments()['serializedFormat']; - $order = $attribute->getArguments()['order']; + $attributeArguments = $attribute->getArguments(); + if(key_exists('class', $attributeArguments)) { + $class = $attributeArguments['class']; + $override = key_exists('override', $attributeArguments) ? $attributeArguments['override'] : true; + $order = key_exists('order', $attributeArguments) ? $attributeArguments['order'] : [ + RequestValidator::ORDER_SERIALIZE, + RequestValidator::ORDER_ATTRIBUTES, + RequestValidator::ORDER_QUERY, + RequestValidator::ORDER_REQUEST, + ]; + $serializedFormat = key_exists('serializedFormat', $attributeArguments) ? $attributeArguments['json'] : 'json'; + }else { + $class = $attributeArguments[0]; + $override = key_exists(1, $attributeArguments) ? $attributeArguments[1] : true; + $order = key_exists(2, $attributeArguments) ? $attributeArguments[2] : [ + RequestValidator::ORDER_SERIALIZE, + RequestValidator::ORDER_ATTRIBUTES, + RequestValidator::ORDER_QUERY, + RequestValidator::ORDER_REQUEST, + ]; + $serializedFormat = key_exists(3, $attributeArguments) ? $attributeArguments[3] : 'json'; + } $object = new $class(); foreach ($order as $type) { switch ($type) { case RequestValidator::ORDER_SERIALIZE: + if(empty($request->getContent())) { + continue 2; + } $serializer = $this->getSerializer(); $serializer->deserialize($request->getContent(), $class, $serializedFormat, [AbstractNormalizer::OBJECT_TO_POPULATE => $object]); continue 2; case RequestValidator::ORDER_REQUEST: - $this->setProperties($object, $request->request, $override); + $this->setProperties($object, $request->request->all(), $override); break; case RequestValidator::ORDER_QUERY: - $this->setProperties($object, $request->query, $override); + $this->setProperties($object, $request->query->all(), $override); break; case RequestValidator::ORDER_ATTRIBUTES: - $this->setProperties($object, $request->attributes, $override); + $this->setProperties($object, $request->attributes->all(), $override); break; } @@ -84,9 +106,9 @@ public function validateRequest(ControllerArgumentsEvent $event): void { $event->setArguments($arguments); } - private function setProperties(object $object, \IteratorAggregate $bag, bool $override) { - foreach ($bag as $key => $value) { - if(false === $override && property_exists($object, $key)) { + private function setProperties(object $object, array $parameters, bool $override) { + foreach ($parameters as $key => $value) { + if(false === $override && property_exists($object, $key) && isset($object->{$key})) { continue; } $object->{$key} = $value; @@ -109,7 +131,7 @@ private function getReflectionMethod(callable $controller): \ReflectionMethod private function getSerializer(): SerializerInterface { - if (!class_exists(SerializerInterface::class)) { + if (!class_exists(Serializer::class)) { throw new LogicException(sprintf('The "symfony/serializer" component is required to use the "%s" validator. Try running "composer require symfony/serializer".', __CLASS__)); } From 5b12b32a4d60a2812105ccea6e3bf5443fd55711 Mon Sep 17 00:00:00 2001 From: Thomas Hanke Date: Sat, 21 Jan 2023 21:22:38 +0100 Subject: [PATCH 4/4] remove serializer form default --- src/Symfony/Component/Validator/Attribute/RequestValidator.php | 1 - .../Validator/EventListener/RequestValidationSubscriber.php | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/Symfony/Component/Validator/Attribute/RequestValidator.php b/src/Symfony/Component/Validator/Attribute/RequestValidator.php index 3ba871036947c..d949792863170 100644 --- a/src/Symfony/Component/Validator/Attribute/RequestValidator.php +++ b/src/Symfony/Component/Validator/Attribute/RequestValidator.php @@ -14,7 +14,6 @@ public function __construct( public string $class, public bool $override = true, public array $order = [ - self::ORDER_SERIALIZE, self::ORDER_ATTRIBUTES, self::ORDER_QUERY, self::ORDER_REQUEST, diff --git a/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php b/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php index 3c853c54ee283..1f7a2e341cafd 100644 --- a/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php +++ b/src/Symfony/Component/Validator/EventListener/RequestValidationSubscriber.php @@ -47,7 +47,6 @@ public function validateRequest(ControllerArgumentsEvent $event): void { $class = $attributeArguments['class']; $override = key_exists('override', $attributeArguments) ? $attributeArguments['override'] : true; $order = key_exists('order', $attributeArguments) ? $attributeArguments['order'] : [ - RequestValidator::ORDER_SERIALIZE, RequestValidator::ORDER_ATTRIBUTES, RequestValidator::ORDER_QUERY, RequestValidator::ORDER_REQUEST, @@ -57,7 +56,6 @@ public function validateRequest(ControllerArgumentsEvent $event): void { $class = $attributeArguments[0]; $override = key_exists(1, $attributeArguments) ? $attributeArguments[1] : true; $order = key_exists(2, $attributeArguments) ? $attributeArguments[2] : [ - RequestValidator::ORDER_SERIALIZE, RequestValidator::ORDER_ATTRIBUTES, RequestValidator::ORDER_QUERY, RequestValidator::ORDER_REQUEST, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy