From 0a20a066d6398f6655ada2b675bc83ec63764e70 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Wed, 29 Sep 2021 13:21:18 +0000 Subject: [PATCH 01/17] feat: ParamConverter --- actions/class.PropertyValues.php | 25 ++++ manifest.php | 3 + .../Configuration/ConfigurationAnnotation.php | 35 +++++ .../Configuration/ParamConverter.php | 108 +++++++++++++++ .../Context/ParamConverterListenerContext.php | 80 +++++++++++ models/classes/ParamConverter/Event/Event.php | 13 ++ .../Event/ParamConverterEvent.php | 28 ++++ .../EventListener/ListenerInterface.php | 12 ++ .../EventListener/ParamConverterListener.php | 120 ++++++++++++++++ .../Request/ParamConverterInterface.php | 25 ++++ .../Request/ParamConverterManager.php | 128 ++++++++++++++++++ .../Request/QueryParamConverter.php | 65 +++++++++ .../ParamConverterServiceProvider.php | 56 ++++++++ models/classes/routing/ActionEnforcer.php | 51 ++++++- 14 files changed, 747 insertions(+), 2 deletions(-) create mode 100644 models/classes/ParamConverter/Configuration/ConfigurationAnnotation.php create mode 100644 models/classes/ParamConverter/Configuration/ParamConverter.php create mode 100644 models/classes/ParamConverter/Context/ParamConverterListenerContext.php create mode 100644 models/classes/ParamConverter/Event/Event.php create mode 100644 models/classes/ParamConverter/Event/ParamConverterEvent.php create mode 100644 models/classes/ParamConverter/EventListener/ListenerInterface.php create mode 100644 models/classes/ParamConverter/EventListener/ParamConverterListener.php create mode 100644 models/classes/ParamConverter/Request/ParamConverterInterface.php create mode 100644 models/classes/ParamConverter/Request/ParamConverterManager.php create mode 100644 models/classes/ParamConverter/Request/QueryParamConverter.php create mode 100644 models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php diff --git a/actions/class.PropertyValues.php b/actions/class.PropertyValues.php index 475d51478d..46cfd1f2aa 100644 --- a/actions/class.PropertyValues.php +++ b/actions/class.PropertyValues.php @@ -24,11 +24,28 @@ use GuzzleHttp\Psr7\ServerRequest; use oat\tao\model\http\HttpJsonResponseTrait; +use oat\tao\model\ParamConverter\Configuration\ParamConverter; use oat\tao\model\Lists\Business\Service\ValueCollectionService; +use oat\tao\model\Lists\DataAccess\Repository\DependentPropertiesRepository; use oat\tao\model\Lists\Presentation\Web\RequestHandler\ValueCollectionSearchRequestHandler; use oat\tao\model\Lists\DataAccess\Repository\DependsOnPropertyRepository; use oat\generis\model\OntologyAwareTrait; +class TestDTO +{ + private $propertyUri; + + public function __construct(string $propertyUri) + { + $this->propertyUri = $propertyUri; + } + + public function getPropertyUri(): string + { + return $this->propertyUri; + } +} + class tao_actions_PropertyValues extends tao_actions_CommonModule { use HttpJsonResponseTrait; @@ -67,6 +84,14 @@ public function getDependOnPropertyList(): void ); } + /** + * @ParamConverter("testDTO", converter="oat.tao.param_converter.query") + */ + public function test(TestDTO $testDTO): void + { + $this->setSuccessJsonResponse([]); + } + private function getRepository(): DependsOnPropertyRepository { return $this->getServiceLocator()->get(DependsOnPropertyRepository::class); diff --git a/manifest.php b/manifest.php index c900bb3590..bc562efdaa 100755 --- a/manifest.php +++ b/manifest.php @@ -265,5 +265,8 @@ ], 'extra' => [ 'structures' => $extpath . 'actions' . DIRECTORY_SEPARATOR . 'structures.xml', + ], + 'containerServiceProviders' => [ + \oat\tao\model\ParamConverter\ServiceProvider\ParamConverterServiceProvider::class, ] ]; diff --git a/models/classes/ParamConverter/Configuration/ConfigurationAnnotation.php b/models/classes/ParamConverter/Configuration/ConfigurationAnnotation.php new file mode 100644 index 0000000000..0dd731d631 --- /dev/null +++ b/models/classes/ParamConverter/Configuration/ConfigurationAnnotation.php @@ -0,0 +1,35 @@ + $value) { + $setter = 'set' . ucfirst($property); + + if (method_exists($this, $setter)) { + $this->$setter($value); + + continue; + } elseif (property_exists($this, $property)) { + $this->$property = $values; + + continue; + } + + throw new RuntimeException( + sprintf( + 'Unknown property "%s" for annotation "@%s".', + $property, + static::class + ) + ); + } + } +} diff --git a/models/classes/ParamConverter/Configuration/ParamConverter.php b/models/classes/ParamConverter/Configuration/ParamConverter.php new file mode 100644 index 0000000000..1f9bedda98 --- /dev/null +++ b/models/classes/ParamConverter/Configuration/ParamConverter.php @@ -0,0 +1,108 @@ +name; + } + + public function setName(string $name): void + { + $this->name = $name; + } + + public function setValue(string $name): void + { + $this->setName($name); + } + + public function getClass(): ?string + { + return $this->class; + } + + public function setClass(?string $class): void + { + $this->class = $class; + } + + public function getOptions(): array + { + return $this->options; + } + + public function setOptions(array $options): void + { + $this->options = $options; + } + + public function isOptional(): bool + { + return $this->isOptional; + } + + public function setIsOptional(bool $isOptional): void + { + $this->isOptional = $isOptional; + } + + public function getConverter(): ?string + { + return $this->converter; + } + + public function setConverter(?string $converter): void + { + $this->converter = $converter; + } +} diff --git a/models/classes/ParamConverter/Context/ParamConverterListenerContext.php b/models/classes/ParamConverter/Context/ParamConverterListenerContext.php new file mode 100644 index 0000000000..c696f911be --- /dev/null +++ b/models/classes/ParamConverter/Context/ParamConverterListenerContext.php @@ -0,0 +1,80 @@ +checkRequiredParameters($parameters); + + parent::__construct($parameters); + } + + protected function getRequiredParameters(): array + { + return [ + self::PARAM_REQUEST, + self::PARAM_CONTROLLER, + self::PARAM_METHOD, + ]; + } + + protected function getSupportedParameters(): array + { + return [ + self::PARAM_REQUEST, + self::PARAM_CONTROLLER, + self::PARAM_METHOD, + ]; + } + + protected function validateParameter(string $parameter, $parameterValue): void + { + if ($parameter === self::PARAM_REQUEST && $parameterValue instanceof Request) { + return; + } + + if ( + $parameter === self::PARAM_CONTROLLER + && (is_string($parameterValue) || is_object($parameterValue)) + ) { + return; + } + + if ($parameter === self::PARAM_METHOD && is_string($parameterValue)) { + return; + } + + throw new InvalidArgumentException( + sprintf( + 'Context parameter %s is not valid.', + $parameter + ) + ); + } + + private function checkRequiredParameters(array $parameters): void + { + $missedParameters = array_diff($this->getRequiredParameters(), array_keys($parameters)); + + if (!empty($missedParameters)) { + throw new InvalidArgumentException( + sprintf( + 'The following required context parameters are missing: %s.', + implode(', ', $missedParameters) + ) + ); + } + } +} diff --git a/models/classes/ParamConverter/Event/Event.php b/models/classes/ParamConverter/Event/Event.php new file mode 100644 index 0000000000..6906678292 --- /dev/null +++ b/models/classes/ParamConverter/Event/Event.php @@ -0,0 +1,13 @@ +context = $context; + } + + public function getContext(): ContextInterface + { + return $this->context; + } + + public function getName(): string + { + return self::class; + } +} diff --git a/models/classes/ParamConverter/EventListener/ListenerInterface.php b/models/classes/ParamConverter/EventListener/ListenerInterface.php new file mode 100644 index 0000000000..9255025447 --- /dev/null +++ b/models/classes/ParamConverter/EventListener/ListenerInterface.php @@ -0,0 +1,12 @@ +paramConverterManager = $paramConverterManager; + $this->autoConvert = $autoConvert; + } + + public function handleEvent(Event $event): void + { + if (!$event instanceof ParamConverterEvent) { + return; + } + + $context = $event->getContext(); + /** @var Request $request */ + $request = $context->getParameter(ParamConverterListenerContext::PARAM_REQUEST); + + $configurations = []; + $requestConfigurations = $request->attributes->get('_converters', []); + + if (!is_array($requestConfigurations)) { + $requestConfigurations = [$requestConfigurations]; + } + + /** @var ParamConverter $requestConfiguration */ + foreach ($requestConfigurations as $requestConfiguration) { + $configurations[$requestConfiguration->getName()] = $requestConfiguration; + } + + $controller = $context->getParameter(ParamConverterListenerContext::PARAM_CONTROLLER); + $method = $context->getParameter(ParamConverterListenerContext::PARAM_METHOD); + + // automatically apply conversion for non-configured objects + if ($this->autoConvert) { + if (is_callable([$controller, $method])) { + $reflection = new ReflectionMethod($controller, $method); + $configurations = $this->autoConfigure($reflection, $request, $configurations); + } + } + + $this->paramConverterManager->apply($request, $configurations); + } + + /** + * @param ParamConverter[] $configurations + * + * @return ParamConverter[] + */ + private function autoConfigure( + ReflectionFunctionAbstract $reflection, + Request $request, + array $configurations + ): array { + foreach ($reflection->getParameters() as $param) { + $type = $param->getType(); + $class = $this->getParamClassByType($type); + + if ($class !== null && $request instanceof $class) { + continue; + } + + $name = $param->getName(); + + if ($type) { + if (!isset($configurations[$name])) { + $configuration = new ParamConverter([]); + $configuration->setName($name); + + $configurations[$name] = $configuration; + } + + if ($class !== null && $configurations[$name]->getClass() === null) { + $configurations[$name]->setClass($class); + } + } + + if (isset($configurations[$name])) { + $isOptional = $param->isOptional() + || $param->isDefaultValueAvailable() + || ($type && $type->allowsNull()); + + $configurations[$name]->setIsOptional($isOptional); + } + } + + return $configurations; + } + + private function getParamClassByType(?ReflectionType $type): ?string + { + return $type !== null && !$type->isBuiltin() + ? $type->getName() + : null; + } +} diff --git a/models/classes/ParamConverter/Request/ParamConverterInterface.php b/models/classes/ParamConverter/Request/ParamConverterInterface.php new file mode 100644 index 0000000000..33ff7f6974 --- /dev/null +++ b/models/classes/ParamConverter/Request/ParamConverterInterface.php @@ -0,0 +1,25 @@ +add($converter, $converter->getPriority(), $converter->getName()); + } + } + + /** + * @param ParamConverter[] $configurations + */ + public function apply(Request $request, array $configurations): void + { + foreach ($configurations as $configuration) { + $this->applyConfiguration($request, $configuration); + } + } + + /** + * Adds a parameter converter. + * + * Converters match either explicitly via $name or by iteration over all + * converters with a $priority. If you pass a $priority = null then the + * added converter will not be part of the iteration chain and can only + * be invoked explicitly. + */ + public function add(ParamConverterInterface $converter, ?int $priority = 0, string $name = null): void + { + if ($priority !== null) { + if (!isset($this->converters[$priority])) { + $this->converters[$priority] = []; + } + + $this->converters[$priority][] = $converter; + } + + if ($name !== null) { + $this->namedConverters[$name] = $converter; + } + } + + /** + * Returns all registered param converters. + * + * @return ParamConverterInterface[] + */ + public function all(): array + { + krsort($this->converters); + + $converters = []; + + foreach ($this->converters as $all) { + $converters = array_merge($converters, $all); + } + + return $converters; + } + + private function applyConfiguration(Request $request, ParamConverter $configuration): void + { + $value = $request->attributes->get($configuration->getName()); + $className = $configuration->getClass(); + + // If the value is already an instance of the class we are trying to convert it into + // we should continue as no conversion is required + if (is_object($value) && $value instanceof $className) { + return; + } + + if ($converterName = $configuration->getConverter()) { + if (!isset($this->namedConverters[$converterName])) { + throw new RuntimeException( + sprintf( + 'No converter named "%s" found for conversion of parameter "%s".', + $converterName, + $configuration->getName() + ) + ); + } + + /** @var ParamConverterInterface $converter */ + $converter = $this->namedConverters[$converterName]; + + if (!$converter->supports($configuration)) { + throw new RuntimeException( + sprintf( + 'Converter "%s" does not support conversion of parameter "%s".', + $converterName, + $configuration->getName() + ) + ); + } + + $converter->apply($request, $configuration); + + return; + } + + foreach ($this->all() as $converter) { + if ($converter->supports($configuration)) { + if ($converter->apply($request, $configuration)) { + return; + } + } + } + } +} diff --git a/models/classes/ParamConverter/Request/QueryParamConverter.php b/models/classes/ParamConverter/Request/QueryParamConverter.php new file mode 100644 index 0000000000..570f460843 --- /dev/null +++ b/models/classes/ParamConverter/Request/QueryParamConverter.php @@ -0,0 +1,65 @@ +query->all(); + $class = $configuration->getClass(); + + $constructor = new ReflectionMethod($class, '__construct'); + $constructorArgs = []; + + foreach ($constructor->getParameters() as $constructorParameter) { + $constructorParameterName = $constructorParameter->getName(); + + if (array_key_exists($constructorParameterName, $queryParameters)) { + $constructorArgs[$constructorParameterName] = $queryParameters[$constructorParameterName]; + unset($queryParameters[$constructorParameterName]); + } + } + + $instance = (new ReflectionClass($class))->newInstanceArgs($constructorArgs); + + foreach ($queryParameters as $queryParameter => $value) { + if (method_exists($instance, 'set' . $queryParameter)) { + $instance->{'set' . $queryParameter}($value); + } elseif (property_exists($instance, $queryParameter)) { + $instance->$queryParameter = $value; + } + } + + $request->attributes->set($configuration->getName(), $instance); + } catch (Throwable $exception) { + return false; + } + + return true; + } + + public function supports(ParamConverter $configuration): bool + { + return $configuration->getConverter() === $this->getName() && $configuration->getClass() !== null; + } +} diff --git a/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php b/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php new file mode 100644 index 0000000000..c5f8c22030 --- /dev/null +++ b/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php @@ -0,0 +1,56 @@ +services(); + $parameters = $configurator->parameters(); + + $paramConvertersReferences = []; + + foreach (self::PARAM_CONVERTERS as $paramConverter) { + $services + ->set($paramConverter, $paramConverter) + ->public(); + + $paramConvertersReferences[] = service($paramConverter); + } + + $services + ->set(ParamConverterManager::class, ParamConverterManager::class) + ->public() + ->args( + [ + $paramConvertersReferences, + ] + ); + + $parameters->set('autoConvert', true); + $services + ->set(ParamConverterListener::class, ParamConverterListener::class) + ->public() + ->args( + [ + service(ParamConverterManager::class), + param('autoConvert') + ] + ); + } +} diff --git a/models/classes/routing/ActionEnforcer.php b/models/classes/routing/ActionEnforcer.php index 6ab0c2758c..d68fd9856e 100644 --- a/models/classes/routing/ActionEnforcer.php +++ b/models/classes/routing/ActionEnforcer.php @@ -25,6 +25,8 @@ use Context; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\ServerRequest; +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\AnnotationRegistry; use oat\tao\model\Middleware\MiddlewareRequestHandler; use ReflectionException; use IExecutable; @@ -47,6 +49,11 @@ use oat\oatbox\log\LoggerAwareTrait; use oat\oatbox\log\TaoLoggerAwareInterface; use oat\tao\model\action\CommonModuleInterface; +use oat\tao\model\ParamConverter\Event\ParamConverterEvent; +use oat\tao\model\ParamConverter\Configuration\ParamConverter; +use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; +use oat\tao\model\ParamConverter\EventListener\ParamConverterListener; +use oat\tao\model\ParamConverter\Context\ParamConverterListenerContext; /** * @TODO ActionEnforcer class documentation. @@ -253,10 +260,45 @@ public function resolve(ServerRequestInterface $request): ResponseInterface private function resolveParameters(ServerRequestInterface $request, $controller, string $action): array { // search parameters method - $reflect = new ReflectionMethod($controller, $action); + $reflect = new ReflectionMethod($controller, $action); $parameters = $this->getParameters(); + $actionParameters = []; + + // TODO make cleaner + AnnotationRegistry::registerLoader('class_exists'); + $annotationReader = new AnnotationReader(); + $annotations = $annotationReader->getMethodAnnotations($reflect); + $paramConverterConfigurations = []; + + foreach ($annotations as $annotation) { + if ($annotation instanceof ParamConverter) { + $paramConverterConfigurations[] = $annotation; + } + } + + if (!empty($paramConverterConfigurations)) { + $symfonyRequest = (new HttpFoundationFactory())->createRequest($request); + $symfonyRequest->attributes->set( + ParamConverterListener::REQUEST_ATTRIBUTE_CONVERTERS, + $paramConverterConfigurations + ); + + $this->getEventManager()->trigger( + new ParamConverterEvent( + new ParamConverterListenerContext([ + ParamConverterListenerContext::PARAM_REQUEST => $symfonyRequest, + ParamConverterListenerContext::PARAM_CONTROLLER => $controller, + ParamConverterListenerContext::PARAM_METHOD => $action, + ]) + ) + ); + + $convertedParameters = $symfonyRequest->attributes->all(); + unset($convertedParameters[ParamConverterListener::REQUEST_ATTRIBUTE_CONVERTERS]); + + $parameters = array_merge($parameters, $convertedParameters); + } - $actionParameters = []; foreach ($reflect->getParameters() as $param) { $paramName = $param->getName(); $paramType = $param->getType(); @@ -276,6 +318,11 @@ private function resolveParameters(ServerRequestInterface $request, $controller, return $actionParameters; } + private function getEventManager(): EventManager + { + return $this->getServiceLocator()->getContainer()->get(EventManager::SERVICE_ID); + } + private function getMiddlewareRequestHandler(): MiddlewareRequestHandler { return $this->getServiceManager()->get(MiddlewareRequestHandler::SERVICE_ID); From 3cbfd86a7a1959c498446a8f07ee3c40d0cc0888 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Wed, 29 Sep 2021 13:23:56 +0000 Subject: [PATCH 02/17] refactor: remove unnecessary code --- actions/class.PropertyValues.php | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/actions/class.PropertyValues.php b/actions/class.PropertyValues.php index 46cfd1f2aa..475d51478d 100644 --- a/actions/class.PropertyValues.php +++ b/actions/class.PropertyValues.php @@ -24,28 +24,11 @@ use GuzzleHttp\Psr7\ServerRequest; use oat\tao\model\http\HttpJsonResponseTrait; -use oat\tao\model\ParamConverter\Configuration\ParamConverter; use oat\tao\model\Lists\Business\Service\ValueCollectionService; -use oat\tao\model\Lists\DataAccess\Repository\DependentPropertiesRepository; use oat\tao\model\Lists\Presentation\Web\RequestHandler\ValueCollectionSearchRequestHandler; use oat\tao\model\Lists\DataAccess\Repository\DependsOnPropertyRepository; use oat\generis\model\OntologyAwareTrait; -class TestDTO -{ - private $propertyUri; - - public function __construct(string $propertyUri) - { - $this->propertyUri = $propertyUri; - } - - public function getPropertyUri(): string - { - return $this->propertyUri; - } -} - class tao_actions_PropertyValues extends tao_actions_CommonModule { use HttpJsonResponseTrait; @@ -84,14 +67,6 @@ public function getDependOnPropertyList(): void ); } - /** - * @ParamConverter("testDTO", converter="oat.tao.param_converter.query") - */ - public function test(TestDTO $testDTO): void - { - $this->setSuccessJsonResponse([]); - } - private function getRepository(): DependsOnPropertyRepository { return $this->getServiceLocator()->get(DependsOnPropertyRepository::class); From 06a9814447483926bb48aac0eb9f7a77d2d33699 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Wed, 29 Sep 2021 13:36:41 +0000 Subject: [PATCH 03/17] refactor: replace with import --- manifest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/manifest.php b/manifest.php index bc562efdaa..3ffa7d86b3 100755 --- a/manifest.php +++ b/manifest.php @@ -68,6 +68,7 @@ use oat\tao\scripts\install\SetUpQueueTasks; use oat\tao\scripts\tools\AddRtlLocale; use oat\tao\scripts\update\Updater; +use oat\tao\model\ParamConverter\ServiceProvider\ParamConverterServiceProvider; $extpath = __DIR__ . DIRECTORY_SEPARATOR; @@ -267,6 +268,6 @@ 'structures' => $extpath . 'actions' . DIRECTORY_SEPARATOR . 'structures.xml', ], 'containerServiceProviders' => [ - \oat\tao\model\ParamConverter\ServiceProvider\ParamConverterServiceProvider::class, + ParamConverterServiceProvider::class, ] ]; From 9bb2662425e98ec5c06df6cefe892ce347f5a3e0 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Wed, 29 Sep 2021 15:54:31 +0000 Subject: [PATCH 04/17] refactor: prettify code --- actions/class.PropertyValues.php | 27 ++++++ manifest.php | 2 +- .../Configuration/AutoConfigurator.php | 66 +++++++++++++ .../Configuration/ConfigurationAnnotation.php | 32 ++++-- .../Configuration/ParamConverter.php | 6 +- .../EventListener/ParamConverterListener.php | 97 ++++++------------- .../ParamConverterManager.php | 54 ++++++----- .../Request/AbstractParamConverter.php | 69 +++++++++++++ .../Request/QueryParamConverter.php | 51 +--------- .../ParamConverterServiceProvider.php | 36 +++++-- 10 files changed, 285 insertions(+), 155 deletions(-) create mode 100644 models/classes/ParamConverter/Configuration/AutoConfigurator.php rename models/classes/ParamConverter/{Request => Manager}/ParamConverterManager.php (71%) create mode 100644 models/classes/ParamConverter/Request/AbstractParamConverter.php diff --git a/actions/class.PropertyValues.php b/actions/class.PropertyValues.php index 475d51478d..7f3ac74a88 100644 --- a/actions/class.PropertyValues.php +++ b/actions/class.PropertyValues.php @@ -24,11 +24,30 @@ use GuzzleHttp\Psr7\ServerRequest; use oat\tao\model\http\HttpJsonResponseTrait; +use oat\tao\model\ParamConverter\Configuration\ParamConverter; use oat\tao\model\Lists\Business\Service\ValueCollectionService; +use oat\tao\model\Lists\DataAccess\Repository\DependentPropertiesRepository; use oat\tao\model\Lists\Presentation\Web\RequestHandler\ValueCollectionSearchRequestHandler; use oat\tao\model\Lists\DataAccess\Repository\DependsOnPropertyRepository; use oat\generis\model\OntologyAwareTrait; +class TestDTO +{ + public const CONVERTER_ID = 'oat.tao.param_converter.query'; + + private $propertyUri; + + public function __construct(string $propertyUri) + { + $this->propertyUri = $propertyUri; + } + + public function getPropertyUri(): string + { + return $this->propertyUri; + } +} + class tao_actions_PropertyValues extends tao_actions_CommonModule { use HttpJsonResponseTrait; @@ -67,6 +86,14 @@ public function getDependOnPropertyList(): void ); } + /** + * @ParamConverter("testDTO", converter="oat.tao.param_converter.query") + */ + public function test(TestDTO $testDTO, DependentPropertiesRepository $dependentPropertiesRepository): void + { + $this->setSuccessJsonResponse([]); + } + private function getRepository(): DependsOnPropertyRepository { return $this->getServiceLocator()->get(DependsOnPropertyRepository::class); diff --git a/manifest.php b/manifest.php index 3ffa7d86b3..ee35b52451 100755 --- a/manifest.php +++ b/manifest.php @@ -269,5 +269,5 @@ ], 'containerServiceProviders' => [ ParamConverterServiceProvider::class, - ] + ], ]; diff --git a/models/classes/ParamConverter/Configuration/AutoConfigurator.php b/models/classes/ParamConverter/Configuration/AutoConfigurator.php new file mode 100644 index 0000000000..6bb61a0c2a --- /dev/null +++ b/models/classes/ParamConverter/Configuration/AutoConfigurator.php @@ -0,0 +1,66 @@ +getParameters() as $parameter) { + $type = $parameter->getType(); + $class = $this->getParamClassByType($type); + + if ($class !== null && $request instanceof $class) { + continue; + } + + $name = $parameter->getName(); + + if ($type) { + if (!isset($configurations[$name])) { + $configurations[$name] = new ParamConverter($name); + } + + if ($class !== null && $configurations[$name]->getClass() === null) { + $configurations[$name]->setClass($class); + } + + $configurationClass = $configurations[$name]->getClass(); + + if ( + $configurationClass !== null + && $configurations[$name]->getConverter() === null + && defined($configurationClass . '::CONVERTER_ID') + ) { + $configurations[$name]->setConverter( + constant($configurationClass . '::CONVERTER_ID') + ); + } + } + + if (isset($configurations[$name])) { + $isOptional = $parameter->isOptional() + || $parameter->isDefaultValueAvailable() + || ($type && $type->allowsNull()); + + $configurations[$name]->setIsOptional($isOptional); + } + } + } + + private function getParamClassByType(?ReflectionType $type): ?string + { + return $type !== null && !$type->isBuiltin() + ? $type->getName() + : null; + } +} diff --git a/models/classes/ParamConverter/Configuration/ConfigurationAnnotation.php b/models/classes/ParamConverter/Configuration/ConfigurationAnnotation.php index 0dd731d631..c7d5ace8a5 100644 --- a/models/classes/ParamConverter/Configuration/ConfigurationAnnotation.php +++ b/models/classes/ParamConverter/Configuration/ConfigurationAnnotation.php @@ -11,15 +11,7 @@ abstract class ConfigurationAnnotation public function __construct(array $values) { foreach ($values as $property => $value) { - $setter = 'set' . ucfirst($property); - - if (method_exists($this, $setter)) { - $this->$setter($value); - - continue; - } elseif (property_exists($this, $property)) { - $this->$property = $values; - + if ($this->setViaSetter($property, $value) || $this->setDirectly($property, $value)) { continue; } @@ -32,4 +24,26 @@ public function __construct(array $values) ); } } + + private function setViaSetter(string $property, $value): bool + { + $isSetterExists = method_exists($this, 'set' . $property); + + if ($isSetterExists) { + $this->{'set' . $property}($value); + } + + return $isSetterExists; + } + + private function setDirectly(string $property, $value): bool + { + $isPropertyExists = property_exists($this, $property); + + if ($isPropertyExists) { + $this->$property = $value; + } + + return $isPropertyExists; + } } diff --git a/models/classes/ParamConverter/Configuration/ParamConverter.php b/models/classes/ParamConverter/Configuration/ParamConverter.php index 1f9bedda98..b45a845f71 100644 --- a/models/classes/ParamConverter/Configuration/ParamConverter.php +++ b/models/classes/ParamConverter/Configuration/ParamConverter.php @@ -30,10 +30,10 @@ class ParamConverter extends ConfigurationAnnotation */ public function __construct( $data = [], + string $converter = null, string $class = null, array $options = [], - bool $isOptional = false, - string $converter = null + bool $isOptional = false ) { $values = []; @@ -43,10 +43,10 @@ public function __construct( $values = $data; } + $values['converter'] = $values['converter'] ?? $converter; $values['class'] = $values['class'] ?? $class; $values['options'] = $values['options'] ?? $options; $values['isOptional'] = $values['isOptional'] ?? $isOptional; - $values['converter'] = $values['converter'] ?? $converter; parent::__construct($values); } diff --git a/models/classes/ParamConverter/EventListener/ParamConverterListener.php b/models/classes/ParamConverter/EventListener/ParamConverterListener.php index 4d77355a95..3a799de609 100644 --- a/models/classes/ParamConverter/EventListener/ParamConverterListener.php +++ b/models/classes/ParamConverter/EventListener/ParamConverterListener.php @@ -4,30 +4,36 @@ namespace oat\tao\model\ParamConverter\EventListener; -use ReflectionType; use ReflectionMethod; -use ReflectionFunctionAbstract; use oat\tao\model\ParamConverter\Event\Event; use Symfony\Component\HttpFoundation\Request; use oat\tao\model\ParamConverter\Event\ParamConverterEvent; use oat\tao\model\ParamConverter\Configuration\ParamConverter; +use oat\tao\model\ParamConverter\Manager\ParamConverterManager; +use oat\tao\model\ParamConverter\Configuration\AutoConfigurator; use oat\tao\model\ParamConverter\Context\ParamConverterListenerContext; -use oat\tao\model\ParamConverter\Request\ParamConverterManager; class ParamConverterListener implements ListenerInterface { public const REQUEST_ATTRIBUTE_CONVERTERS = '_converters'; + /** @var AutoConfigurator */ + private $autoConfigurator; + /** @var ParamConverterManager */ private $paramConverterManager; /** @var bool */ private $autoConvert; - public function __construct(ParamConverterManager $paramConverterManager, bool $autoConvert = true) - { + public function __construct( + AutoConfigurator $autoConfigurator, + ParamConverterManager $paramConverterManager, + bool $autoConvert = true + ) { $this->paramConverterManager = $paramConverterManager; $this->autoConvert = $autoConvert; + $this->autoConfigurator = $autoConfigurator; } public function handleEvent(Event $event): void @@ -40,81 +46,40 @@ public function handleEvent(Event $event): void /** @var Request $request */ $request = $context->getParameter(ParamConverterListenerContext::PARAM_REQUEST); - $configurations = []; - $requestConfigurations = $request->attributes->get('_converters', []); - - if (!is_array($requestConfigurations)) { - $requestConfigurations = [$requestConfigurations]; - } - - /** @var ParamConverter $requestConfiguration */ - foreach ($requestConfigurations as $requestConfiguration) { - $configurations[$requestConfiguration->getName()] = $requestConfiguration; - } + $configurations = $this->extractConfigurations($request); $controller = $context->getParameter(ParamConverterListenerContext::PARAM_CONTROLLER); $method = $context->getParameter(ParamConverterListenerContext::PARAM_METHOD); - // automatically apply conversion for non-configured objects - if ($this->autoConvert) { - if (is_callable([$controller, $method])) { - $reflection = new ReflectionMethod($controller, $method); - $configurations = $this->autoConfigure($reflection, $request, $configurations); - } + // Automatically apply conversion for non-configured objects + if ($this->autoConvert && is_callable([$controller, $method])) { + $this->autoConfigurator->configure( + new ReflectionMethod($controller, $method), + $request, + $configurations + ); } $this->paramConverterManager->apply($request, $configurations); } /** - * @param ParamConverter[] $configurations - * * @return ParamConverter[] */ - private function autoConfigure( - ReflectionFunctionAbstract $reflection, - Request $request, - array $configurations - ): array { - foreach ($reflection->getParameters() as $param) { - $type = $param->getType(); - $class = $this->getParamClassByType($type); - - if ($class !== null && $request instanceof $class) { - continue; - } - - $name = $param->getName(); - - if ($type) { - if (!isset($configurations[$name])) { - $configuration = new ParamConverter([]); - $configuration->setName($name); - - $configurations[$name] = $configuration; - } - - if ($class !== null && $configurations[$name]->getClass() === null) { - $configurations[$name]->setClass($class); - } - } - - if (isset($configurations[$name])) { - $isOptional = $param->isOptional() - || $param->isDefaultValueAvailable() - || ($type && $type->allowsNull()); - - $configurations[$name]->setIsOptional($isOptional); - } + private function extractConfigurations(Request $request): array + { + $configurations = []; + $requestConfigurations = $request->attributes->get(self::REQUEST_ATTRIBUTE_CONVERTERS, []); + + if (!is_array($requestConfigurations)) { + $requestConfigurations = [$requestConfigurations]; } - return $configurations; - } + /** @var ParamConverter $requestConfiguration */ + foreach ($requestConfigurations as $requestConfiguration) { + $configurations[$requestConfiguration->getName()] = $requestConfiguration; + } - private function getParamClassByType(?ReflectionType $type): ?string - { - return $type !== null && !$type->isBuiltin() - ? $type->getName() - : null; + return $configurations; } } diff --git a/models/classes/ParamConverter/Request/ParamConverterManager.php b/models/classes/ParamConverter/Manager/ParamConverterManager.php similarity index 71% rename from models/classes/ParamConverter/Request/ParamConverterManager.php rename to models/classes/ParamConverter/Manager/ParamConverterManager.php index 79e63173ff..99a923d6fd 100644 --- a/models/classes/ParamConverter/Request/ParamConverterManager.php +++ b/models/classes/ParamConverter/Manager/ParamConverterManager.php @@ -2,11 +2,12 @@ declare(strict_types=1); -namespace oat\tao\model\ParamConverter\Request; +namespace oat\tao\model\ParamConverter\Manager; use RuntimeException; use Symfony\Component\HttpFoundation\Request; use oat\tao\model\ParamConverter\Configuration\ParamConverter; +use oat\tao\model\ParamConverter\Request\ParamConverterInterface; class ParamConverterManager { @@ -67,7 +68,6 @@ public function add(ParamConverterInterface $converter, ?int $priority = 0, stri public function all(): array { krsort($this->converters); - $converters = []; foreach ($this->converters as $all) { @@ -89,29 +89,11 @@ private function applyConfiguration(Request $request, ParamConverter $configurat } if ($converterName = $configuration->getConverter()) { - if (!isset($this->namedConverters[$converterName])) { - throw new RuntimeException( - sprintf( - 'No converter named "%s" found for conversion of parameter "%s".', - $converterName, - $configuration->getName() - ) - ); - } - + $this->checkProvidedConverterName($converterName, $configuration->getName()); /** @var ParamConverterInterface $converter */ $converter = $this->namedConverters[$converterName]; - if (!$converter->supports($configuration)) { - throw new RuntimeException( - sprintf( - 'Converter "%s" does not support conversion of parameter "%s".', - $converterName, - $configuration->getName() - ) - ); - } - + $this->checkConverterSupport($converter, $configuration); $converter->apply($request, $configuration); return; @@ -125,4 +107,32 @@ private function applyConfiguration(Request $request, ParamConverter $configurat } } } + + private function checkProvidedConverterName(string $converterName, string $parameter): void + { + if (!isset($this->namedConverters[$converterName])) { + throw new RuntimeException( + sprintf( + 'No converter named "%s" found for conversion of parameter "%s".', + $converterName, + $parameter + ) + ); + } + } + + private function checkConverterSupport( + ParamConverterInterface $converter, + ParamConverter $configuration + ): void { + if (!$converter->supports($configuration)) { + throw new RuntimeException( + sprintf( + 'Converter "%s" does not support conversion of parameter "%s".', + $converter->getName(), + $configuration->getName() + ) + ); + } + } } diff --git a/models/classes/ParamConverter/Request/AbstractParamConverter.php b/models/classes/ParamConverter/Request/AbstractParamConverter.php new file mode 100644 index 0000000000..dc001a3f4a --- /dev/null +++ b/models/classes/ParamConverter/Request/AbstractParamConverter.php @@ -0,0 +1,69 @@ +createObject( + $this->getData($request, $configuration->getOptions()), + $configuration->getClass() + ); + + $request->attributes->set($configuration->getName(), $object); + } catch (Throwable $exception) { + return false; + } + + return true; + } + + public function supports(ParamConverter $configuration): bool + { + return $configuration->getClass() !== null && $configuration->getConverter() === $this->getName(); + } + + abstract protected function getData(Request $request, array $options): array; + + private function createObject(array $data, string $class): object + { + $constructor = new ReflectionMethod($class, '__construct'); + $constructorArgs = []; + + foreach ($constructor->getParameters() as $constructorParameter) { + $constructorParameterName = $constructorParameter->getName(); + + if (array_key_exists($constructorParameterName, $data)) { + $constructorArgs[$constructorParameterName] = $data[$constructorParameterName]; + unset($data[$constructorParameterName]); + } + } + + $instance = (new ReflectionClass($class))->newInstanceArgs($constructorArgs); + + foreach ($data as $queryParameter => $value) { + if (method_exists($instance, 'set' . $queryParameter)) { + $instance->{'set' . $queryParameter}($value); + } elseif (property_exists($instance, $queryParameter)) { + $instance->$queryParameter = $value; + } + } + + return $instance; + } +} diff --git a/models/classes/ParamConverter/Request/QueryParamConverter.php b/models/classes/ParamConverter/Request/QueryParamConverter.php index 570f460843..700748a6c5 100644 --- a/models/classes/ParamConverter/Request/QueryParamConverter.php +++ b/models/classes/ParamConverter/Request/QueryParamConverter.php @@ -4,62 +4,17 @@ namespace oat\tao\model\ParamConverter\Request; -use Throwable; -use ReflectionClass; -use ReflectionMethod; use Symfony\Component\HttpFoundation\Request; -use oat\tao\model\ParamConverter\Configuration\ParamConverter; -class QueryParamConverter implements ParamConverterInterface +class QueryParamConverter extends AbstractParamConverter { - public function getPriority(): int - { - return 0; - } - public function getName(): string { return 'oat.tao.param_converter.query'; } - public function apply(Request $request, ParamConverter $configuration): bool - { - try { - $queryParameters = $request->query->all(); - $class = $configuration->getClass(); - - $constructor = new ReflectionMethod($class, '__construct'); - $constructorArgs = []; - - foreach ($constructor->getParameters() as $constructorParameter) { - $constructorParameterName = $constructorParameter->getName(); - - if (array_key_exists($constructorParameterName, $queryParameters)) { - $constructorArgs[$constructorParameterName] = $queryParameters[$constructorParameterName]; - unset($queryParameters[$constructorParameterName]); - } - } - - $instance = (new ReflectionClass($class))->newInstanceArgs($constructorArgs); - - foreach ($queryParameters as $queryParameter => $value) { - if (method_exists($instance, 'set' . $queryParameter)) { - $instance->{'set' . $queryParameter}($value); - } elseif (property_exists($instance, $queryParameter)) { - $instance->$queryParameter = $value; - } - } - - $request->attributes->set($configuration->getName(), $instance); - } catch (Throwable $exception) { - return false; - } - - return true; - } - - public function supports(ParamConverter $configuration): bool + protected function getData(Request $request, array $options): array { - return $configuration->getConverter() === $this->getName() && $configuration->getClass() !== null; + return $request->query->all(); } } diff --git a/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php b/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php index c5f8c22030..e23d53235f 100644 --- a/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php +++ b/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php @@ -5,12 +5,15 @@ namespace oat\tao\model\ParamConverter\ServiceProvider; use oat\tao\model\ParamConverter\Request\QueryParamConverter; -use oat\tao\model\ParamConverter\Request\ParamConverterManager; +use oat\tao\model\ParamConverter\Manager\ParamConverterManager; +use oat\tao\model\ParamConverter\Configuration\AutoConfigurator; use oat\tao\model\ParamConverter\EventListener\ParamConverterListener; use oat\generis\model\DependencyInjection\ContainerServiceProviderInterface; use function Symfony\Component\DependencyInjection\Loader\Configurator\param; use function Symfony\Component\DependencyInjection\Loader\Configurator\service; +use Symfony\Component\DependencyInjection\Loader\Configurator\ServicesConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Symfony\Component\DependencyInjection\Loader\Configurator\ParametersConfigurator; class ParamConverterServiceProvider implements ContainerServiceProviderInterface { @@ -23,24 +26,44 @@ public function __invoke(ContainerConfigurator $configurator): void $services = $configurator->services(); $parameters = $configurator->parameters(); - $paramConvertersReferences = []; + $this->provideConverters($services); + $this->provideParamConverterManager($services); + $this->provideParamConverterListener($services, $parameters); + } + private function provideConverters(ServicesConfigurator $services): void + { foreach (self::PARAM_CONVERTERS as $paramConverter) { $services ->set($paramConverter, $paramConverter) ->public(); - - $paramConvertersReferences[] = service($paramConverter); } + } + private function provideParamConverterManager(ServicesConfigurator $services): void + { $services ->set(ParamConverterManager::class, ParamConverterManager::class) ->public() ->args( [ - $paramConvertersReferences, + array_map( + static function (string $paramConverter) { + return service($paramConverter); + }, + self::PARAM_CONVERTERS + ), ] ); + } + + private function provideParamConverterListener( + ServicesConfigurator $services, + ParametersConfigurator $parameters + ): void { + $services + ->set(AutoConfigurator::class, AutoConfigurator::class) + ->public(); $parameters->set('autoConvert', true); $services @@ -48,8 +71,9 @@ public function __invoke(ContainerConfigurator $configurator): void ->public() ->args( [ + service(AutoConfigurator::class), service(ParamConverterManager::class), - param('autoConvert') + param('autoConvert'), ] ); } From c05733db2387b8ac859769b69dadb7ab210dec9c Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Wed, 29 Sep 2021 15:57:45 +0000 Subject: [PATCH 05/17] refactor: add missed DockBlocks, remove unnecessary code --- actions/class.PropertyValues.php | 27 ------------------- .../Configuration/AutoConfigurator.php | 18 +++++++++++++ .../Configuration/ConfigurationAnnotation.php | 18 +++++++++++++ .../Configuration/ParamConverter.php | 18 +++++++++++++ .../Context/ParamConverterListenerContext.php | 18 +++++++++++++ models/classes/ParamConverter/Event/Event.php | 18 +++++++++++++ .../Event/ParamConverterEvent.php | 18 +++++++++++++ .../EventListener/ListenerInterface.php | 18 +++++++++++++ .../EventListener/ParamConverterListener.php | 18 +++++++++++++ .../Manager/ParamConverterManager.php | 18 +++++++++++++ .../Request/AbstractParamConverter.php | 18 +++++++++++++ .../Request/ParamConverterInterface.php | 18 +++++++++++++ .../Request/QueryParamConverter.php | 18 +++++++++++++ .../ParamConverterServiceProvider.php | 18 +++++++++++++ 14 files changed, 234 insertions(+), 27 deletions(-) diff --git a/actions/class.PropertyValues.php b/actions/class.PropertyValues.php index 7f3ac74a88..475d51478d 100644 --- a/actions/class.PropertyValues.php +++ b/actions/class.PropertyValues.php @@ -24,30 +24,11 @@ use GuzzleHttp\Psr7\ServerRequest; use oat\tao\model\http\HttpJsonResponseTrait; -use oat\tao\model\ParamConverter\Configuration\ParamConverter; use oat\tao\model\Lists\Business\Service\ValueCollectionService; -use oat\tao\model\Lists\DataAccess\Repository\DependentPropertiesRepository; use oat\tao\model\Lists\Presentation\Web\RequestHandler\ValueCollectionSearchRequestHandler; use oat\tao\model\Lists\DataAccess\Repository\DependsOnPropertyRepository; use oat\generis\model\OntologyAwareTrait; -class TestDTO -{ - public const CONVERTER_ID = 'oat.tao.param_converter.query'; - - private $propertyUri; - - public function __construct(string $propertyUri) - { - $this->propertyUri = $propertyUri; - } - - public function getPropertyUri(): string - { - return $this->propertyUri; - } -} - class tao_actions_PropertyValues extends tao_actions_CommonModule { use HttpJsonResponseTrait; @@ -86,14 +67,6 @@ public function getDependOnPropertyList(): void ); } - /** - * @ParamConverter("testDTO", converter="oat.tao.param_converter.query") - */ - public function test(TestDTO $testDTO, DependentPropertiesRepository $dependentPropertiesRepository): void - { - $this->setSuccessJsonResponse([]); - } - private function getRepository(): DependsOnPropertyRepository { return $this->getServiceLocator()->get(DependsOnPropertyRepository::class); diff --git a/models/classes/ParamConverter/Configuration/AutoConfigurator.php b/models/classes/ParamConverter/Configuration/AutoConfigurator.php index 6bb61a0c2a..76dca9622c 100644 --- a/models/classes/ParamConverter/Configuration/AutoConfigurator.php +++ b/models/classes/ParamConverter/Configuration/AutoConfigurator.php @@ -1,5 +1,23 @@ Date: Wed, 29 Sep 2021 17:56:09 +0000 Subject: [PATCH 06/17] refactor: use interfaces, create converted object via reflection --- ...{AutoConfigurator.php => Configurator.php} | 4 +- .../Configuration/ConfiguratorInterface.php | 18 +++++++++ .../EventListener/ParamConverterListener.php | 18 ++++----- .../Manager/ParamConverterManager.php | 15 ++----- .../ParamConverterManagerInterface.php | 39 +++++++++++++++++++ .../Request/AbstractParamConverter.php | 30 ++++++++------ .../ParamConverterServiceProvider.php | 6 +-- 7 files changed, 93 insertions(+), 37 deletions(-) rename models/classes/ParamConverter/Configuration/{AutoConfigurator.php => Configurator.php} (97%) create mode 100644 models/classes/ParamConverter/Configuration/ConfiguratorInterface.php create mode 100644 models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php diff --git a/models/classes/ParamConverter/Configuration/AutoConfigurator.php b/models/classes/ParamConverter/Configuration/Configurator.php similarity index 97% rename from models/classes/ParamConverter/Configuration/AutoConfigurator.php rename to models/classes/ParamConverter/Configuration/Configurator.php index 76dca9622c..2dee984c4a 100644 --- a/models/classes/ParamConverter/Configuration/AutoConfigurator.php +++ b/models/classes/ParamConverter/Configuration/Configurator.php @@ -26,10 +26,10 @@ use ReflectionFunctionAbstract; use Symfony\Component\HttpFoundation\Request; -class AutoConfigurator +class Configurator implements ConfiguratorInterface { /** - * @param ParamConverter[] $configurations + * {@inheritdoc} */ public function configure(ReflectionFunctionAbstract $reflection, Request $request, array &$configurations): void { diff --git a/models/classes/ParamConverter/Configuration/ConfiguratorInterface.php b/models/classes/ParamConverter/Configuration/ConfiguratorInterface.php new file mode 100644 index 0000000000..cea9b85fee --- /dev/null +++ b/models/classes/ParamConverter/Configuration/ConfiguratorInterface.php @@ -0,0 +1,18 @@ +configurator = $autoConfigurator; $this->paramConverterManager = $paramConverterManager; $this->autoConvert = $autoConvert; - $this->autoConfigurator = $autoConfigurator; } public function handleEvent(Event $event): void @@ -71,7 +71,7 @@ public function handleEvent(Event $event): void // Automatically apply conversion for non-configured objects if ($this->autoConvert && is_callable([$controller, $method])) { - $this->autoConfigurator->configure( + $this->configurator->configure( new ReflectionMethod($controller, $method), $request, $configurations diff --git a/models/classes/ParamConverter/Manager/ParamConverterManager.php b/models/classes/ParamConverter/Manager/ParamConverterManager.php index c9e8e48f4b..1c67033361 100644 --- a/models/classes/ParamConverter/Manager/ParamConverterManager.php +++ b/models/classes/ParamConverter/Manager/ParamConverterManager.php @@ -27,7 +27,7 @@ use oat\tao\model\ParamConverter\Configuration\ParamConverter; use oat\tao\model\ParamConverter\Request\ParamConverterInterface; -class ParamConverterManager +class ParamConverterManager implements ParamConverterManagerInterface { /** @var array */ private $converters = []; @@ -46,7 +46,7 @@ public function __construct(array $converters = []) } /** - * @param ParamConverter[] $configurations + * {@inheritdoc} */ public function apply(Request $request, array $configurations): void { @@ -56,12 +56,7 @@ public function apply(Request $request, array $configurations): void } /** - * Adds a parameter converter. - * - * Converters match either explicitly via $name or by iteration over all - * converters with a $priority. If you pass a $priority = null then the - * added converter will not be part of the iteration chain and can only - * be invoked explicitly. + * {@inheritdoc} */ public function add(ParamConverterInterface $converter, ?int $priority = 0, string $name = null): void { @@ -79,9 +74,7 @@ public function add(ParamConverterInterface $converter, ?int $priority = 0, stri } /** - * Returns all registered param converters. - * - * @return ParamConverterInterface[] + * {@inheritdoc} */ public function all(): array { diff --git a/models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php b/models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php new file mode 100644 index 0000000000..1f5debcd3e --- /dev/null +++ b/models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php @@ -0,0 +1,39 @@ +getConstructor(); - foreach ($constructor->getParameters() as $constructorParameter) { - $constructorParameterName = $constructorParameter->getName(); + if ($constructor) { + foreach ($constructor->getParameters() as $constructorParameter) { + $constructorParameterName = $constructorParameter->getName(); - if (array_key_exists($constructorParameterName, $data)) { - $constructorArgs[$constructorParameterName] = $data[$constructorParameterName]; - unset($data[$constructorParameterName]); + if (array_key_exists($constructorParameterName, $data)) { + $constructorArgs[$constructorParameterName] = $data[$constructorParameterName]; + unset($data[$constructorParameterName]); + } } } - $instance = (new ReflectionClass($class))->newInstanceArgs($constructorArgs); + $instance = $reflectionClass->newInstanceArgs($constructorArgs); foreach ($data as $queryParameter => $value) { - if (method_exists($instance, 'set' . $queryParameter)) { - $instance->{'set' . $queryParameter}($value); - } elseif (property_exists($instance, $queryParameter)) { - $instance->$queryParameter = $value; + if ($reflectionClass->hasMethod('set' . $queryParameter)) { + $reflectionClass + ->getMethod('set' . $queryParameter) + ->invoke($instance, $value); + } elseif ($reflectionClass->hasProperty($queryParameter)) { + $reflectionClass->getProperty($queryParameter)->setValue($instance, $value); } } diff --git a/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php b/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php index 2d8e8cbded..9328086347 100644 --- a/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php +++ b/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php @@ -22,9 +22,9 @@ namespace oat\tao\model\ParamConverter\ServiceProvider; +use oat\tao\model\ParamConverter\Configuration\Configurator; use oat\tao\model\ParamConverter\Request\QueryParamConverter; use oat\tao\model\ParamConverter\Manager\ParamConverterManager; -use oat\tao\model\ParamConverter\Configuration\AutoConfigurator; use oat\tao\model\ParamConverter\EventListener\ParamConverterListener; use oat\generis\model\DependencyInjection\ContainerServiceProviderInterface; use function Symfony\Component\DependencyInjection\Loader\Configurator\param; @@ -80,7 +80,7 @@ private function provideParamConverterListener( ParametersConfigurator $parameters ): void { $services - ->set(AutoConfigurator::class, AutoConfigurator::class) + ->set(Configurator::class, Configurator::class) ->public(); $parameters->set('autoConvert', true); @@ -89,7 +89,7 @@ private function provideParamConverterListener( ->public() ->args( [ - service(AutoConfigurator::class), + service(Configurator::class), service(ParamConverterManager::class), param('autoConvert'), ] From a3b69fc8924d40f40001cc1d70ecaa829c8932a5 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Wed, 29 Sep 2021 17:58:52 +0000 Subject: [PATCH 07/17] refactor: add DockBlocks, remove unnecessary use-statements --- .../Configuration/ConfiguratorInterface.php | 18 ++++++++++++++++++ .../Manager/ParamConverterManagerInterface.php | 18 ++++++++++++++++++ .../Request/AbstractParamConverter.php | 2 -- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/models/classes/ParamConverter/Configuration/ConfiguratorInterface.php b/models/classes/ParamConverter/Configuration/ConfiguratorInterface.php index cea9b85fee..564a71279c 100644 --- a/models/classes/ParamConverter/Configuration/ConfiguratorInterface.php +++ b/models/classes/ParamConverter/Configuration/ConfiguratorInterface.php @@ -1,5 +1,23 @@ Date: Wed, 29 Sep 2021 20:16:53 +0000 Subject: [PATCH 08/17] refactor: apply dependency inversion principle, prettify code --- manifest.php | 2 + .../Factory/HttpFoundationFactory.php | 46 ++++ .../HttpFoundationFactoryInterface.php | 31 +++ .../HttpFoundation/Request/Request.php | 204 ++++++++++++++++++ .../Request/RequestInterface.php | 146 +++++++++++++ .../HttpFoundationServiceProvider.php | 39 ++++ .../Configuration/Configurator.php | 14 +- .../Configuration/ConfiguratorInterface.php | 8 +- .../Context/ParamConverterListenerContext.php | 4 +- .../EventListener/ParamConverterListener.php | 8 +- .../Manager/ParamConverterManager.php | 8 +- .../ParamConverterManagerInterface.php | 4 +- .../Request/AbstractParamConverter.php | 11 +- .../Request/ParamConverterInterface.php | 6 +- .../Request/QueryParamConverter.php | 6 +- models/classes/routing/ActionEnforcer.php | 147 ++++++++----- 16 files changed, 599 insertions(+), 85 deletions(-) create mode 100644 models/classes/HttpFoundation/Factory/HttpFoundationFactory.php create mode 100644 models/classes/HttpFoundation/Factory/HttpFoundationFactoryInterface.php create mode 100644 models/classes/HttpFoundation/Request/Request.php create mode 100644 models/classes/HttpFoundation/Request/RequestInterface.php create mode 100644 models/classes/HttpFoundation/ServiceProvider/HttpFoundationServiceProvider.php diff --git a/manifest.php b/manifest.php index ee35b52451..fca8cc8d7b 100755 --- a/manifest.php +++ b/manifest.php @@ -69,6 +69,7 @@ use oat\tao\scripts\tools\AddRtlLocale; use oat\tao\scripts\update\Updater; use oat\tao\model\ParamConverter\ServiceProvider\ParamConverterServiceProvider; +use oat\tao\model\HttpFoundation\ServiceProvider\HttpFoundationServiceProvider; $extpath = __DIR__ . DIRECTORY_SEPARATOR; @@ -268,6 +269,7 @@ 'structures' => $extpath . 'actions' . DIRECTORY_SEPARATOR . 'structures.xml', ], 'containerServiceProviders' => [ + HttpFoundationServiceProvider::class, ParamConverterServiceProvider::class, ], ]; diff --git a/models/classes/HttpFoundation/Factory/HttpFoundationFactory.php b/models/classes/HttpFoundation/Factory/HttpFoundationFactory.php new file mode 100644 index 0000000000..a4ba9a5199 --- /dev/null +++ b/models/classes/HttpFoundation/Factory/HttpFoundationFactory.php @@ -0,0 +1,46 @@ +httpFoundationFactory = new SymfonyHttpFoundationFactory(); + } + + public function createRequest(ServerRequestInterface $psrRequest, bool $streamed = false): RequestInterface + { + $request = $this->httpFoundationFactory->createRequest($psrRequest, $streamed); + + return new Request($request); + } +} diff --git a/models/classes/HttpFoundation/Factory/HttpFoundationFactoryInterface.php b/models/classes/HttpFoundation/Factory/HttpFoundationFactoryInterface.php new file mode 100644 index 0000000000..382700a201 --- /dev/null +++ b/models/classes/HttpFoundation/Factory/HttpFoundationFactoryInterface.php @@ -0,0 +1,31 @@ +request = $request; + } + + public function request(): SymfonyRequest + { + return $this->request; + } + + /** + * {@inheritdoc} + */ + public function get(string $key, $default = null) + { + return $this->request()->get($key, $default); + } + + public function getAttributes(): array + { + return $this->request()->attributes->all(); + } + + /** + * {@inheritdoc} + */ + public function getAttribute(string $key, $default = null) + { + return $this->request()->attributes->get($key, $default); + } + + /** + * {@inheritdoc} + */ + public function setAttribute(string $key, $value): void + { + $this->request()->attributes->set($key, $value); + } + + public function getRequestParameters(): array + { + return $this->request()->request->all(); + } + + /** + * {@inheritdoc} + */ + public function getRequestParameter(string $key, $default = null) + { + return $this->request()->request->get($key, $default); + } + + /** + * {@inheritdoc} + */ + public function setRequestParameter(string $key, $value): void + { + $this->request()->request->set($key, $value); + } + + public function getQueryParameters(): array + { + return $this->request()->query->all(); + } + + /** + * {@inheritdoc} + */ + public function getQueryParameter(string $key, $default = null) + { + return $this->request()->query->get($key, $default); + } + + /** + * {@inheritdoc} + */ + public function setQueryParameter(string $key, $value): void + { + $this->request()->query->set($key, $value); + } + + public function getServerParameters(): array + { + return $this->request()->server->all(); + } + + /** + * {@inheritdoc} + */ + public function getServerParameter(string $key, $default = null) + { + return $this->request()->server->get($key, $default); + } + + /** + * {@inheritdoc} + */ + public function setServerParameter(string $key, $value): void + { + $this->request()->server->set($key, $value); + } + + public function getFiles(): array + { + return $this->request()->files->all(); + } + + /** + * {@inheritdoc} + */ + public function getFile(string $key, $default = null) + { + return $this->request()->files->get($key, $default); + } + + /** + * {@inheritdoc} + */ + public function setFile(string $key, $value): void + { + $this->request()->files->set($key, $value); + } + + public function getCookies(): array + { + return $this->request()->cookies->all(); + } + + /** + * {@inheritdoc} + */ + public function getCookie(string $key, $default = null) + { + return $this->request()->cookies->get($key, $default); + } + + /** + * {@inheritdoc} + */ + public function setCookie(string $key, $value): void + { + $this->request()->cookies->set($key, $value); + } + + public function getHeaders(): array + { + return $this->request()->headers->all(); + } + + /** + * {@inheritdoc} + */ + public function getHeader(string $key, string $default = null): ?string + { + return $this->request()->headers->get($key, $default); + } + + /** + * {@inheritdoc} + */ + public function setHeader(string $key, $values, bool $replace = true): void + { + $this->request()->headers->set($key, $values, $replace); + } + + /** + * {@inheritdoc} + */ + public function getContent(bool $asResource = false) + { + return $this->request()->getContent($asResource); + } +} diff --git a/models/classes/HttpFoundation/Request/RequestInterface.php b/models/classes/HttpFoundation/Request/RequestInterface.php new file mode 100644 index 0000000000..48298e610b --- /dev/null +++ b/models/classes/HttpFoundation/Request/RequestInterface.php @@ -0,0 +1,146 @@ +services(); + + $services + ->set(HttpFoundationFactory::class, HttpFoundationFactory::class) + ->public(); + } +} diff --git a/models/classes/ParamConverter/Configuration/Configurator.php b/models/classes/ParamConverter/Configuration/Configurator.php index 2dee984c4a..093bc29869 100644 --- a/models/classes/ParamConverter/Configuration/Configurator.php +++ b/models/classes/ParamConverter/Configuration/Configurator.php @@ -24,20 +24,26 @@ use ReflectionType; use ReflectionFunctionAbstract; -use Symfony\Component\HttpFoundation\Request; +use oat\tao\model\HttpFoundation\Request\RequestInterface; class Configurator implements ConfiguratorInterface { /** * {@inheritdoc} */ - public function configure(ReflectionFunctionAbstract $reflection, Request $request, array &$configurations): void - { + public function configure( + ReflectionFunctionAbstract $reflection, + RequestInterface $request, + array &$configurations + ): void { foreach ($reflection->getParameters() as $parameter) { $type = $parameter->getType(); $class = $this->getParamClassByType($type); - if ($class !== null && $request instanceof $class) { + if ( + $class !== null + && ($request instanceof $class || $request->request() instanceof $class) + ) { continue; } diff --git a/models/classes/ParamConverter/Configuration/ConfiguratorInterface.php b/models/classes/ParamConverter/Configuration/ConfiguratorInterface.php index 564a71279c..e8c42fca5b 100644 --- a/models/classes/ParamConverter/Configuration/ConfiguratorInterface.php +++ b/models/classes/ParamConverter/Configuration/ConfiguratorInterface.php @@ -23,7 +23,7 @@ namespace oat\tao\model\ParamConverter\Configuration; use ReflectionFunctionAbstract; -use Symfony\Component\HttpFoundation\Request; +use oat\tao\model\HttpFoundation\Request\RequestInterface; interface ConfiguratorInterface { @@ -32,5 +32,9 @@ interface ConfiguratorInterface * * @param ParamConverter[] $configurations */ - public function configure(ReflectionFunctionAbstract $reflection, Request $request, array &$configurations): void; + public function configure( + ReflectionFunctionAbstract $reflection, + RequestInterface $request, + array &$configurations + ): void; } diff --git a/models/classes/ParamConverter/Context/ParamConverterListenerContext.php b/models/classes/ParamConverter/Context/ParamConverterListenerContext.php index 0c91b0e89f..d151f71b65 100644 --- a/models/classes/ParamConverter/Context/ParamConverterListenerContext.php +++ b/models/classes/ParamConverter/Context/ParamConverterListenerContext.php @@ -24,7 +24,7 @@ use InvalidArgumentException; use oat\tao\model\Context\AbstractContext; -use Symfony\Component\HttpFoundation\Request; +use oat\tao\model\HttpFoundation\Request\RequestInterface; class ParamConverterListenerContext extends AbstractContext { @@ -59,7 +59,7 @@ protected function getSupportedParameters(): array protected function validateParameter(string $parameter, $parameterValue): void { - if ($parameter === self::PARAM_REQUEST && $parameterValue instanceof Request) { + if ($parameter === self::PARAM_REQUEST && $parameterValue instanceof RequestInterface) { return; } diff --git a/models/classes/ParamConverter/EventListener/ParamConverterListener.php b/models/classes/ParamConverter/EventListener/ParamConverterListener.php index 74dfb44ac4..d2c18d2f80 100644 --- a/models/classes/ParamConverter/EventListener/ParamConverterListener.php +++ b/models/classes/ParamConverter/EventListener/ParamConverterListener.php @@ -24,7 +24,7 @@ use ReflectionMethod; use oat\tao\model\ParamConverter\Event\Event; -use Symfony\Component\HttpFoundation\Request; +use oat\tao\model\HttpFoundation\Request\RequestInterface; use oat\tao\model\ParamConverter\Event\ParamConverterEvent; use oat\tao\model\ParamConverter\Configuration\ParamConverter; use oat\tao\model\ParamConverter\Configuration\ConfiguratorInterface; @@ -61,7 +61,7 @@ public function handleEvent(Event $event): void } $context = $event->getContext(); - /** @var Request $request */ + /** @var RequestInterface $request */ $request = $context->getParameter(ParamConverterListenerContext::PARAM_REQUEST); $configurations = $this->extractConfigurations($request); @@ -84,10 +84,10 @@ public function handleEvent(Event $event): void /** * @return ParamConverter[] */ - private function extractConfigurations(Request $request): array + private function extractConfigurations(RequestInterface $request): array { $configurations = []; - $requestConfigurations = $request->attributes->get(self::REQUEST_ATTRIBUTE_CONVERTERS, []); + $requestConfigurations = $request->getAttribute(self::REQUEST_ATTRIBUTE_CONVERTERS, []); if (!is_array($requestConfigurations)) { $requestConfigurations = [$requestConfigurations]; diff --git a/models/classes/ParamConverter/Manager/ParamConverterManager.php b/models/classes/ParamConverter/Manager/ParamConverterManager.php index 1c67033361..a657cde9fe 100644 --- a/models/classes/ParamConverter/Manager/ParamConverterManager.php +++ b/models/classes/ParamConverter/Manager/ParamConverterManager.php @@ -23,7 +23,7 @@ namespace oat\tao\model\ParamConverter\Manager; use RuntimeException; -use Symfony\Component\HttpFoundation\Request; +use oat\tao\model\HttpFoundation\Request\RequestInterface; use oat\tao\model\ParamConverter\Configuration\ParamConverter; use oat\tao\model\ParamConverter\Request\ParamConverterInterface; @@ -48,7 +48,7 @@ public function __construct(array $converters = []) /** * {@inheritdoc} */ - public function apply(Request $request, array $configurations): void + public function apply(RequestInterface $request, array $configurations): void { foreach ($configurations as $configuration) { $this->applyConfiguration($request, $configuration); @@ -88,9 +88,9 @@ public function all(): array return $converters; } - private function applyConfiguration(Request $request, ParamConverter $configuration): void + private function applyConfiguration(RequestInterface $request, ParamConverter $configuration): void { - $value = $request->attributes->get($configuration->getName()); + $value = $request->getAttribute($configuration->getName()); $className = $configuration->getClass(); // If the value is already an instance of the class we are trying to convert it into diff --git a/models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php b/models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php index dadbe99db4..4073eb91f6 100644 --- a/models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php +++ b/models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php @@ -22,7 +22,7 @@ namespace oat\tao\model\ParamConverter\Manager; -use Symfony\Component\HttpFoundation\Request; +use oat\tao\model\HttpFoundation\Request\RequestInterface; use oat\tao\model\ParamConverter\Configuration\ParamConverter; use oat\tao\model\ParamConverter\Request\ParamConverterInterface; @@ -34,7 +34,7 @@ interface ParamConverterManagerInterface * * @param ParamConverter[] $configurations */ - public function apply(Request $request, array $configurations): void; + public function apply(RequestInterface $request, array $configurations): void; /** * Adds a parameter converter. diff --git a/models/classes/ParamConverter/Request/AbstractParamConverter.php b/models/classes/ParamConverter/Request/AbstractParamConverter.php index 89c283069c..f077417c23 100644 --- a/models/classes/ParamConverter/Request/AbstractParamConverter.php +++ b/models/classes/ParamConverter/Request/AbstractParamConverter.php @@ -24,7 +24,7 @@ use Throwable; use ReflectionClass; -use Symfony\Component\HttpFoundation\Request; +use oat\tao\model\HttpFoundation\Request\RequestInterface; use oat\tao\model\ParamConverter\Configuration\ParamConverter; abstract class AbstractParamConverter implements ParamConverterInterface @@ -34,7 +34,7 @@ public function getPriority(): int return 0; } - public function apply(Request $request, ParamConverter $configuration): bool + public function apply(RequestInterface $request, ParamConverter $configuration): bool { try { $object = $this->createObject( @@ -42,7 +42,10 @@ public function apply(Request $request, ParamConverter $configuration): bool $configuration->getClass() ); - $request->attributes->set($configuration->getName(), $object); + $converted = $request->getAttribute(self::ATTRIBUTE_CONVERTED, []); + $converted[$configuration->getName()] = $object; + + $request->setAttribute(self::ATTRIBUTE_CONVERTED, $converted); } catch (Throwable $exception) { return false; } @@ -55,7 +58,7 @@ public function supports(ParamConverter $configuration): bool return $configuration->getClass() !== null && $configuration->getConverter() === $this->getName(); } - abstract protected function getData(Request $request, array $options): array; + abstract protected function getData(RequestInterface $request, array $options): array; private function createObject(array $data, string $class): object { diff --git a/models/classes/ParamConverter/Request/ParamConverterInterface.php b/models/classes/ParamConverter/Request/ParamConverterInterface.php index 1f3af1f362..dbb107a222 100644 --- a/models/classes/ParamConverter/Request/ParamConverterInterface.php +++ b/models/classes/ParamConverter/Request/ParamConverterInterface.php @@ -22,11 +22,13 @@ namespace oat\tao\model\ParamConverter\Request; -use Symfony\Component\HttpFoundation\Request; +use oat\tao\model\HttpFoundation\Request\RequestInterface; use oat\tao\model\ParamConverter\Configuration\ParamConverter; interface ParamConverterInterface { + public const ATTRIBUTE_CONVERTED = 'converted'; + public function getPriority(): int; public function getName(): string; @@ -34,7 +36,7 @@ public function getName(): string; /** * Stores the object in the request. */ - public function apply(Request $request, ParamConverter $configuration): bool; + public function apply(RequestInterface $request, ParamConverter $configuration): bool; /** * Checks if the object is supported. diff --git a/models/classes/ParamConverter/Request/QueryParamConverter.php b/models/classes/ParamConverter/Request/QueryParamConverter.php index e9c68a49b5..9996fb9cda 100644 --- a/models/classes/ParamConverter/Request/QueryParamConverter.php +++ b/models/classes/ParamConverter/Request/QueryParamConverter.php @@ -22,7 +22,7 @@ namespace oat\tao\model\ParamConverter\Request; -use Symfony\Component\HttpFoundation\Request; +use oat\tao\model\HttpFoundation\Request\RequestInterface; class QueryParamConverter extends AbstractParamConverter { @@ -31,8 +31,8 @@ public function getName(): string return 'oat.tao.param_converter.query'; } - protected function getData(Request $request, array $options): array + protected function getData(RequestInterface $request, array $options): array { - return $request->query->all(); + return $request->getQueryParameters(); } } diff --git a/models/classes/routing/ActionEnforcer.php b/models/classes/routing/ActionEnforcer.php index d68fd9856e..03acfda411 100644 --- a/models/classes/routing/ActionEnforcer.php +++ b/models/classes/routing/ActionEnforcer.php @@ -16,44 +16,47 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Copyright (c) 2014-2021 (original work) Open Assessment Technologies SA; - * - * */ +declare(strict_types=1); + namespace oat\tao\model\routing; use Context; -use GuzzleHttp\Psr7\Response; -use GuzzleHttp\Psr7\ServerRequest; -use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\AnnotationRegistry; -use oat\tao\model\Middleware\MiddlewareRequestHandler; -use ReflectionException; use IExecutable; +use ReflectionMethod; +use ReflectionException; use ActionEnforcingException; -use oat\tao\model\http\ResponseEmitter; -use oat\oatbox\service\ServiceManagerAwareInterface; -use oat\oatbox\service\ServiceManagerAwareTrait; +use GuzzleHttp\Psr7\Response; +use common_session_SessionManager; +use GuzzleHttp\Psr7\ServerRequest; +use oat\oatbox\event\EventManager; use oat\tao\model\http\Controller; +use oat\oatbox\log\LoggerAwareTrait; +use oat\tao\model\event\BeforeAction; +use oat\tao\model\http\ResponseEmitter; use Psr\Http\Message\ResponseInterface; +use oat\tao\model\accessControl\AclProxy; +use oat\oatbox\log\TaoLoggerAwareInterface; use Psr\Http\Message\ServerRequestInterface; -use ReflectionMethod; -use common_session_SessionManager; use tao_models_classes_AccessDeniedException; -use oat\tao\model\accessControl\AclProxy; +use oat\tao\model\action\CommonModuleInterface; +use oat\oatbox\service\ServiceManagerAwareTrait; +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\AnnotationRegistry; +use oat\oatbox\service\ServiceManagerAwareInterface; +use oat\tao\model\Middleware\MiddlewareRequestHandler; use oat\tao\model\accessControl\data\DataAccessControl; use oat\tao\model\accessControl\data\PermissionException; +use oat\tao\model\HttpFoundation\Request\RequestInterface; use oat\tao\model\accessControl\func\AclProxy as FuncProxy; -use oat\oatbox\event\EventManager; -use oat\tao\model\event\BeforeAction; -use oat\oatbox\log\LoggerAwareTrait; -use oat\oatbox\log\TaoLoggerAwareInterface; -use oat\tao\model\action\CommonModuleInterface; use oat\tao\model\ParamConverter\Event\ParamConverterEvent; use oat\tao\model\ParamConverter\Configuration\ParamConverter; -use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; +use oat\tao\model\HttpFoundation\Factory\HttpFoundationFactory; +use oat\tao\model\ParamConverter\Request\ParamConverterInterface; use oat\tao\model\ParamConverter\EventListener\ParamConverterListener; use oat\tao\model\ParamConverter\Context\ParamConverterListenerContext; +use oat\tao\model\HttpFoundation\Factory\HttpFoundationFactoryInterface; /** * @TODO ActionEnforcer class documentation. @@ -255,49 +258,18 @@ public function resolve(ServerRequestInterface $request): ResponseInterface } /** + * @param mixed $controller + * * @throws ReflectionException */ private function resolveParameters(ServerRequestInterface $request, $controller, string $action): array { - // search parameters method + // Search parameters method $reflect = new ReflectionMethod($controller, $action); $parameters = $this->getParameters(); $actionParameters = []; - // TODO make cleaner - AnnotationRegistry::registerLoader('class_exists'); - $annotationReader = new AnnotationReader(); - $annotations = $annotationReader->getMethodAnnotations($reflect); - $paramConverterConfigurations = []; - - foreach ($annotations as $annotation) { - if ($annotation instanceof ParamConverter) { - $paramConverterConfigurations[] = $annotation; - } - } - - if (!empty($paramConverterConfigurations)) { - $symfonyRequest = (new HttpFoundationFactory())->createRequest($request); - $symfonyRequest->attributes->set( - ParamConverterListener::REQUEST_ATTRIBUTE_CONVERTERS, - $paramConverterConfigurations - ); - - $this->getEventManager()->trigger( - new ParamConverterEvent( - new ParamConverterListenerContext([ - ParamConverterListenerContext::PARAM_REQUEST => $symfonyRequest, - ParamConverterListenerContext::PARAM_CONTROLLER => $controller, - ParamConverterListenerContext::PARAM_METHOD => $action, - ]) - ) - ); - - $convertedParameters = $symfonyRequest->attributes->all(); - unset($convertedParameters[ParamConverterListener::REQUEST_ATTRIBUTE_CONVERTERS]); - - $parameters = array_merge($parameters, $convertedParameters); - } + $this->applyParamConverters($parameters, $request, $reflect); foreach ($reflect->getParameters() as $param) { $paramName = $param->getName(); @@ -306,18 +278,77 @@ private function resolveParameters(ServerRequestInterface $request, $controller, if (isset($parameters[$paramName])) { $actionParameters[$paramName] = $parameters[$paramName]; - } elseif($paramTypeName === ServerRequest::class) { + } elseif ($paramTypeName === ServerRequest::class) { $actionParameters[$paramName] = $request; - } elseif (class_exists($paramTypeName) || interface_exists($paramTypeName)) { + } elseif ($paramTypeName !== null && (class_exists($paramTypeName) || interface_exists($paramTypeName))) { $actionParameters[$paramName] = $this->getClassInstance($paramTypeName); } elseif (!$param->isDefaultValueAvailable()) { - $this->logWarning('Missing parameter ' . $paramName . ' for ' . $this->getControllerClass() . '@' . $action); + $this->logWarning( + sprintf( + 'Missing parameter %s for %s@%s', + $paramName, + $this->getControllerClass(), + $action + ) + ); } } return $actionParameters; } + private function applyParamConverters( + array &$parameters, + ServerRequestInterface $request, + ReflectionMethod $reflectionMethod + ): void { + $configurations = $this->getParamConverters($reflectionMethod); + + if (empty($configurations)) { + return; + } + + $request = $this->getHttpFoundationFactory()->createRequest($request); + $request->setAttribute(ParamConverterListener::REQUEST_ATTRIBUTE_CONVERTERS, $configurations); + + $this->getEventManager()->trigger( + new ParamConverterEvent( + $this->createParamConverterListenerContext($request, $reflectionMethod) + ) + ); + + $parameters = array_merge( + $parameters, + $request->getAttribute(ParamConverterInterface::ATTRIBUTE_CONVERTED, []) + ); + } + + private function getParamConverters(ReflectionMethod $reflectionMethod): array + { + AnnotationRegistry::registerLoader('class_exists'); + $annotations = (new AnnotationReader())->getMethodAnnotations($reflectionMethod); + + return array_filter($annotations, static function ($annotation) { + return $annotation instanceof ParamConverter; + }); + } + + private function createParamConverterListenerContext( + RequestInterface $request, + ReflectionMethod $reflectionMethod + ): ParamConverterListenerContext { + return new ParamConverterListenerContext([ + ParamConverterListenerContext::PARAM_REQUEST => $request, + ParamConverterListenerContext::PARAM_CONTROLLER => $reflectionMethod->class, + ParamConverterListenerContext::PARAM_METHOD => $reflectionMethod->getName(), + ]); + } + + private function getHttpFoundationFactory(): HttpFoundationFactoryInterface + { + return $this->getServiceLocator()->getContainer()->get(HttpFoundationFactory::class); + } + private function getEventManager(): EventManager { return $this->getServiceLocator()->getContainer()->get(EventManager::SERVICE_ID); From 65e48a898258c8aaeab0b757c870b9d47e352199 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Wed, 29 Sep 2021 20:18:35 +0000 Subject: [PATCH 09/17] refactor: apply php-cs-fixer on manifest --- manifest.php | 82 +++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/manifest.php b/manifest.php index fca8cc8d7b..047e6e400c 100755 --- a/manifest.php +++ b/manifest.php @@ -19,57 +19,55 @@ * 2008-2010 (update and modification) Deutsche Institut für Internationale Pädagogische Forschung (under the project TAO-TRANSFER); * 2009-2012 (update and modification) Public Research Centre Henri Tudor (under the project TAO-SUSTAIN & TAO-DEV); * 2013- (update and modification) Open Assessment Technologies SA; - * */ +use oat\tao\model\user\TaoRoles; use oat\tao\controller\api\Users; -use oat\tao\install\services\SetupSettingsStorage; -use oat\tao\model\accessControl\func\AccessRule; use oat\tao\model\routing\ApiRoute; -use oat\tao\model\routing\LegacyRoute; -use oat\tao\model\user\TaoRoles; -use oat\tao\scripts\install\AddArchiveService; +use oat\tao\scripts\update\Updater; use oat\tao\scripts\install\AddLogFs; +use oat\tao\model\routing\LegacyRoute; +use oat\tao\scripts\install\RegisterEvents; +use oat\tao\scripts\install\SetServiceState; +use oat\tao\scripts\install\SetUpQueueTasks; use oat\tao\scripts\install\AddTmpFsHandlers; +use oat\tao\scripts\install\AddArchiveService; use oat\tao\scripts\install\CreateRdsListStore; -use oat\tao\scripts\install\CreateWebhookEventLogTable; -use oat\tao\scripts\install\InstallNotificationTable; -use oat\tao\scripts\install\RegisterActionService; -use oat\tao\scripts\install\RegisterActionAccessControl; -use oat\tao\scripts\install\RegisterClassMetadataServices; -use oat\tao\scripts\install\RegisterClassPropertiesChangedEvent; -use oat\tao\scripts\install\RegisterClassPropertiesChangedEventListener; -use oat\tao\scripts\install\RegisterClassPropertyRemovedEvent; -use oat\tao\scripts\install\RegisterClassPropertyRemovedListener; -use oat\tao\scripts\install\RegisterDataAccessControlChangedEvent; -use oat\tao\scripts\install\RegisterDataAccessControlChangedListener; -use oat\tao\scripts\install\RegisterEvents; -use oat\tao\scripts\install\RegisterResourceEvents; -use oat\tao\scripts\install\RegisterResourceRelationService; -use oat\tao\scripts\install\RegisterResourceWatcherService; use oat\tao\scripts\install\RegisterRtlLocales; -use oat\tao\scripts\install\RegisterSearchServices; -use oat\tao\scripts\install\RegisterSessionCookieService; -use oat\tao\scripts\install\RegisterSignatureGenerator; -use oat\tao\scripts\install\RegisterTaoUpdateEventListener; -use oat\tao\scripts\install\RegisterTaskQueueServices; -use oat\tao\scripts\install\RegisterUserLockoutsEventListeners; +use oat\tao\model\accessControl\func\AccessRule; use oat\tao\scripts\install\RegisterUserService; -use oat\tao\scripts\install\RegisterValidationRules; -use oat\tao\scripts\install\RegisterValueCollectionServices; -use oat\tao\scripts\install\SetClientLoggerConfig; use oat\tao\scripts\install\SetContainerService; use oat\tao\scripts\install\SetDefaultCSPHeader; +use oat\tao\install\services\SetupSettingsStorage; +use oat\tao\scripts\install\RegisterActionService; +use oat\tao\scripts\install\SetClientLoggerConfig; +use oat\tao\scripts\install\SetServiceFileStorage; +use oat\tao\scripts\install\RegisterResourceEvents; +use oat\tao\scripts\install\RegisterSearchServices; use oat\tao\scripts\install\SetImageAligmentConfig; use oat\tao\scripts\install\SetLocaleNumbersConfig; -use oat\tao\scripts\install\SetServiceFileStorage; -use oat\tao\scripts\install\SetServiceState; +use oat\tao\scripts\install\RegisterValidationRules; use oat\tao\scripts\install\SetupMaintenanceService; -use oat\tao\scripts\install\SetUpQueueTasks; -use oat\tao\scripts\tools\AddRtlLocale; -use oat\tao\scripts\update\Updater; -use oat\tao\model\ParamConverter\ServiceProvider\ParamConverterServiceProvider; +use oat\tao\scripts\install\InstallNotificationTable; +use oat\tao\scripts\install\RegisterTaskQueueServices; +use oat\tao\scripts\install\CreateWebhookEventLogTable; +use oat\tao\scripts\install\RegisterSignatureGenerator; +use oat\tao\scripts\install\RegisterActionAccessControl; +use oat\tao\scripts\install\RegisterSessionCookieService; +use oat\tao\scripts\install\RegisterClassMetadataServices; +use oat\tao\scripts\install\RegisterResourceWatcherService; +use oat\tao\scripts\install\RegisterTaoUpdateEventListener; +use oat\tao\scripts\install\RegisterResourceRelationService; +use oat\tao\scripts\install\RegisterValueCollectionServices; +use oat\tao\scripts\install\RegisterClassPropertyRemovedEvent; +use oat\tao\scripts\install\RegisterUserLockoutsEventListeners; +use oat\tao\scripts\install\RegisterClassPropertiesChangedEvent; +use oat\tao\scripts\install\RegisterClassPropertyRemovedListener; +use oat\tao\scripts\install\RegisterDataAccessControlChangedEvent; +use oat\tao\scripts\install\RegisterDataAccessControlChangedListener; +use oat\tao\scripts\install\RegisterClassPropertiesChangedEventListener; use oat\tao\model\HttpFoundation\ServiceProvider\HttpFoundationServiceProvider; +use oat\tao\model\ParamConverter\ServiceProvider\ParamConverterServiceProvider; $extpath = __DIR__ . DIRECTORY_SEPARATOR; @@ -81,7 +79,7 @@ 'author' => 'Open Assessment Technologies, CRP Henri Tudor', 'models' => [ 'http://www.tao.lu/Ontologies/TAO.rdf', - 'http://www.tao.lu/middleware/wfEngine.rdf' + 'http://www.tao.lu/middleware/wfEngine.rdf', ], 'install' => [ 'rdf' => [ @@ -95,7 +93,7 @@ __DIR__ . '/models/ontology/widgetdefinitions.rdf', __DIR__ . '/models/ontology/requiredaction.rdf', __DIR__ . '/models/ontology/auth/basicauth.rdf', - __DIR__ . '/models/ontology/userlocks.rdf' + __DIR__ . '/models/ontology/userlocks.rdf', ], 'checks' => [ ['type' => 'CheckPHPRuntime', 'value' => ['id' => 'tao_php_runtime', 'min' => '5.4']], @@ -115,8 +113,8 @@ ['type' => 'CheckPHPINIValue', 'value' => ['id' => 'tao_ini_suhosin_request_max_varname_length', 'name' => 'suhosin.request.max_varname_length', 'value' => '128', 'dependsOn' => ['tao_extension_suhosin']]], ['type' => 'CheckFileSystemComponent', 'value' => ['id' => 'fs_generis_common_conf', 'location' => 'config', 'rights' => 'rw', 'recursive' => true]], ['type' => 'CheckFileSystemComponent', 'value' => ['id' => 'fs_tao_client_locales', 'location' => 'tao/views/locales', 'rights' => 'rw']], - ['type' => 'CheckCustom', 'value' => ['id' => 'tao_custom_not_nginx', 'name' => 'not_nginx', 'extension' => 'tao', "optional" => true, 'dependsOn' => ['tao_extension_curl']]], - ['type' => 'CheckCustom', 'value' => ['id' => 'tao_custom_allowoverride', 'name' => 'allow_override', 'extension' => 'tao', "optional" => true, 'dependsOn' => ['tao_custom_not_nginx']]], + ['type' => 'CheckCustom', 'value' => ['id' => 'tao_custom_not_nginx', 'name' => 'not_nginx', 'extension' => 'tao', 'optional' => true, 'dependsOn' => ['tao_extension_curl']]], + ['type' => 'CheckCustom', 'value' => ['id' => 'tao_custom_allowoverride', 'name' => 'allow_override', 'extension' => 'tao', 'optional' => true, 'dependsOn' => ['tao_custom_not_nginx']]], ['type' => 'CheckCustom', 'value' => ['id' => 'tao_custom_mod_rewrite', 'name' => 'mod_rewrite', 'extension' => 'tao', 'dependsOn' => ['tao_custom_allowoverride']]], ['type' => 'CheckCustom', 'value' => ['id' => 'tao_custom_database_drivers', 'name' => 'database_drivers', 'extension' => 'tao']], ], @@ -163,13 +161,13 @@ RegisterActionAccessControl::class, RegisterRtlLocales::class, RegisterSearchServices::class, - SetImageAligmentConfig::class + SetImageAligmentConfig::class, ], ], 'update' => Updater::class, 'optimizableClasses' => [ 'http://www.tao.lu/Ontologies/TAO.rdf#Languages', - 'http://www.tao.lu/Ontologies/TAO.rdf#LanguageUsages' + 'http://www.tao.lu/Ontologies/TAO.rdf#LanguageUsages', ], 'managementRole' => TaoRoles::TAO_MANAGER, 'acl' => [ From 0961acbe2f56fa896fe3a759f3161f24f58536b2 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Wed, 29 Sep 2021 20:22:13 +0000 Subject: [PATCH 10/17] refactor: remove commented lines --- .../classes/HttpFoundation/Request/RequestInterface.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/models/classes/HttpFoundation/Request/RequestInterface.php b/models/classes/HttpFoundation/Request/RequestInterface.php index 48298e610b..ac0a4d130e 100644 --- a/models/classes/HttpFoundation/Request/RequestInterface.php +++ b/models/classes/HttpFoundation/Request/RequestInterface.php @@ -26,14 +26,6 @@ interface RequestInterface { -// public $attributes; -// public $request; -// public $query; -// public $server; -// public $files; -// public $cookies; -// public $headers; - public function request(): Request; /** From 9046d662bca4c652f7a6d8b124a829c0cda59c3f Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Wed, 29 Sep 2021 20:28:38 +0000 Subject: [PATCH 11/17] refactor: group use statements and separate with a line --- .../ServiceProvider/ParamConverterServiceProvider.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php b/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php index 9328086347..41c5c5f503 100644 --- a/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php +++ b/models/classes/ParamConverter/ServiceProvider/ParamConverterServiceProvider.php @@ -27,12 +27,13 @@ use oat\tao\model\ParamConverter\Manager\ParamConverterManager; use oat\tao\model\ParamConverter\EventListener\ParamConverterListener; use oat\generis\model\DependencyInjection\ContainerServiceProviderInterface; -use function Symfony\Component\DependencyInjection\Loader\Configurator\param; -use function Symfony\Component\DependencyInjection\Loader\Configurator\service; use Symfony\Component\DependencyInjection\Loader\Configurator\ServicesConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\ParametersConfigurator; +use function Symfony\Component\DependencyInjection\Loader\Configurator\param; +use function Symfony\Component\DependencyInjection\Loader\Configurator\service; + class ParamConverterServiceProvider implements ContainerServiceProviderInterface { private const PARAM_CONVERTERS = [ From acf7b8e0fe3eb2c75b9ce30f9e4a3ec507c800bc Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Thu, 30 Sep 2021 10:45:22 +0000 Subject: [PATCH 12/17] refactor: register event, ignore 'requiresRight' annotation --- migrations/Version202109300806032234_tao.php | 56 ++++++++++++++++++++ models/classes/routing/ActionEnforcer.php | 11 +++- scripts/install/RegisterEvents.php | 32 ++++++----- 3 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 migrations/Version202109300806032234_tao.php diff --git a/migrations/Version202109300806032234_tao.php b/migrations/Version202109300806032234_tao.php new file mode 100644 index 0000000000..881353a6fc --- /dev/null +++ b/migrations/Version202109300806032234_tao.php @@ -0,0 +1,56 @@ +getEventManager(); + $eventManager->attach(ParamConverterEvent::class, [ParamConverterListener::class, 'handleEvent']); + $this->getServiceLocator()->register(EventManager::SERVICE_ID, $eventManager); + } + + public function down(Schema $schema): void + { + $eventManager = $this->getEventManager(); + $eventManager->detach(ParamConverterEvent::class, [ParamConverterListener::class, 'handleEvent']); + $this->getServiceLocator()->register(EventManager::SERVICE_ID, $eventManager); + } + + private function getEventManager(): EventManager + { + return $this->getServiceLocator()->getContainer()->get(EventManager::SERVICE_ID); + } +} diff --git a/models/classes/routing/ActionEnforcer.php b/models/classes/routing/ActionEnforcer.php index 03acfda411..ec82ddd9da 100644 --- a/models/classes/routing/ActionEnforcer.php +++ b/models/classes/routing/ActionEnforcer.php @@ -43,7 +43,6 @@ use oat\tao\model\action\CommonModuleInterface; use oat\oatbox\service\ServiceManagerAwareTrait; use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\AnnotationRegistry; use oat\oatbox\service\ServiceManagerAwareInterface; use oat\tao\model\Middleware\MiddlewareRequestHandler; use oat\tao\model\accessControl\data\DataAccessControl; @@ -325,7 +324,15 @@ private function applyParamConverters( private function getParamConverters(ReflectionMethod $reflectionMethod): array { - AnnotationRegistry::registerLoader('class_exists'); + /** + * Ignore 'requiresRight' annotation as we don't have such annotation class in TAO. + * + * TODO Create annotation class and use it in all places instead of non-existing `requiresRight` + */ + AnnotationReader::addGlobalIgnoredName('requiresRight'); + + // Autoload 'ParamConverter' annotation. + class_exists(ParamConverter::class); $annotations = (new AnnotationReader())->getMethodAnnotations($reflectionMethod); return array_filter($annotations, static function ($annotation) { diff --git a/scripts/install/RegisterEvents.php b/scripts/install/RegisterEvents.php index 9c617671ac..7d9835a1a2 100644 --- a/scripts/install/RegisterEvents.php +++ b/scripts/install/RegisterEvents.php @@ -15,34 +15,38 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * Copyright (c) 2020 (original work) Open Assessment Technologies SA; - * + * Copyright (c) 2020-2021 (original work) Open Assessment Technologies SA; */ declare(strict_types = 1); namespace oat\tao\scripts\install; +use oat\oatbox\reporting\Report; use oat\oatbox\event\EventManager; use oat\oatbox\extension\InstallAction; -use oat\generis\model\OntologyAwareTrait; +use common_ext_event_ExtensionInstalled; use oat\tao\model\migrations\MigrationsService; +use oat\tao\model\ParamConverter\Event\ParamConverterEvent; +use oat\tao\model\ParamConverter\EventListener\ParamConverterListener; -/** - * Class RegisterEvents - * @package oat\tao\scripts\install - */ class RegisterEvents extends InstallAction { - use OntologyAwareTrait; - public function __invoke($params) { - /** @var EventManager $eventManager */ - $eventManager = $this->getServiceManager()->get(EventManager::SERVICE_ID); - $eventManager->attach(\common_ext_event_ExtensionInstalled::class, [MigrationsService::SERVICE_ID, 'extensionInstalled']); - $this->getServiceManager()->register(EventManager::SERVICE_ID, $eventManager); + $eventManager = $this->getEventManager(); + $eventManager->attach( + common_ext_event_ExtensionInstalled::class, + [MigrationsService::SERVICE_ID, 'extensionInstalled'] + ); + $eventManager->attach(ParamConverterEvent::class, [ParamConverterListener::class, 'handleEvent']); + $this->getServiceLocator()->register(EventManager::SERVICE_ID, $eventManager); - return new \common_report_Report(\common_report_Report::TYPE_SUCCESS, 'Events registered'); + return Report::createSuccess('Events registered'); + } + + private function getEventManager(): EventManager + { + return $this->getServiceLocator()->getContainer()->get(EventManager::SERVICE_ID); } } From 09f10f24d886c341e0bc7ca5d19b8e7a90d645ba Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Wed, 13 Oct 2021 16:49:45 +0000 Subject: [PATCH 13/17] feat: implement serializer, use serializer to deserialize param converter objects --- composer.json | 6 +- manifest.php | 4 +- .../Configuration/ParamConverter.php | 5 ++ .../Context/ObjectFactoryContext.php | 70 ++++++++++++++++++ .../Context/ObjectFactoryContextInterface.php | 16 +++++ .../Context/ParamConverterListenerContext.php | 25 +++++-- ...ParamConverterListenerContextInterface.php | 16 +++++ models/classes/ParamConverter/Event/Event.php | 4 +- .../Event/ParamConverterEvent.php | 8 +-- .../EventListener/ParamConverterListener.php | 8 +-- .../ParamConverter/Factory/ObjectFactory.php | 71 +++++++++++++++++++ .../Factory/ObjectFactoryInterface.php | 14 ++++ .../ParamConverterServiceProvider.php | 23 ++++-- .../Request/AbstractParamConverter.php | 59 +++++++-------- models/classes/Serializer/Serializer.php | 34 +++++++++ .../Serializer/SerializerInterface.php | 26 +++++++ .../Serializer/SerializerServiceProvider.php | 46 ++++++++++++ 17 files changed, 377 insertions(+), 58 deletions(-) create mode 100644 models/classes/ParamConverter/Context/ObjectFactoryContext.php create mode 100644 models/classes/ParamConverter/Context/ObjectFactoryContextInterface.php create mode 100644 models/classes/ParamConverter/Context/ParamConverterListenerContextInterface.php create mode 100644 models/classes/ParamConverter/Factory/ObjectFactory.php create mode 100644 models/classes/ParamConverter/Factory/ObjectFactoryInterface.php rename models/classes/ParamConverter/{ServiceProvider => }/ParamConverterServiceProvider.php (86%) create mode 100644 models/classes/Serializer/Serializer.php create mode 100644 models/classes/Serializer/SerializerInterface.php create mode 100644 models/classes/Serializer/SerializerServiceProvider.php diff --git a/composer.json b/composer.json index 0741155a71..0d2ea3b14e 100644 --- a/composer.json +++ b/composer.json @@ -81,7 +81,11 @@ "flow/jsonpath": "~0.5", "jtl-software/opsgenie-client": "2.0.2", "league/openapi-psr7-validator": "0.9", - "relay/relay": "~2.0" + "relay/relay": "~2.0", + "symfony/serializer": "~5.2.0", + "symfony/http-foundation": "^5.3", + "symfony/psr-http-message-bridge": "^2.1", + "symfony/property-access": "~5.0.0" }, "autoload": { "psr-4": { diff --git a/manifest.php b/manifest.php index 047e6e400c..2160033981 100755 --- a/manifest.php +++ b/manifest.php @@ -50,6 +50,7 @@ use oat\tao\scripts\install\SetupMaintenanceService; use oat\tao\scripts\install\InstallNotificationTable; use oat\tao\scripts\install\RegisterTaskQueueServices; +use oat\tao\model\Serializer\SerializerServiceProvider; use oat\tao\scripts\install\CreateWebhookEventLogTable; use oat\tao\scripts\install\RegisterSignatureGenerator; use oat\tao\scripts\install\RegisterActionAccessControl; @@ -60,6 +61,7 @@ use oat\tao\scripts\install\RegisterResourceRelationService; use oat\tao\scripts\install\RegisterValueCollectionServices; use oat\tao\scripts\install\RegisterClassPropertyRemovedEvent; +use oat\tao\model\ParamConverter\ParamConverterServiceProvider; use oat\tao\scripts\install\RegisterUserLockoutsEventListeners; use oat\tao\scripts\install\RegisterClassPropertiesChangedEvent; use oat\tao\scripts\install\RegisterClassPropertyRemovedListener; @@ -67,7 +69,6 @@ use oat\tao\scripts\install\RegisterDataAccessControlChangedListener; use oat\tao\scripts\install\RegisterClassPropertiesChangedEventListener; use oat\tao\model\HttpFoundation\ServiceProvider\HttpFoundationServiceProvider; -use oat\tao\model\ParamConverter\ServiceProvider\ParamConverterServiceProvider; $extpath = __DIR__ . DIRECTORY_SEPARATOR; @@ -267,6 +268,7 @@ 'structures' => $extpath . 'actions' . DIRECTORY_SEPARATOR . 'structures.xml', ], 'containerServiceProviders' => [ + SerializerServiceProvider::class, HttpFoundationServiceProvider::class, ParamConverterServiceProvider::class, ], diff --git a/models/classes/ParamConverter/Configuration/ParamConverter.php b/models/classes/ParamConverter/Configuration/ParamConverter.php index 441016be89..e5d7c25d5b 100644 --- a/models/classes/ParamConverter/Configuration/ParamConverter.php +++ b/models/classes/ParamConverter/Configuration/ParamConverter.php @@ -28,6 +28,11 @@ */ class ParamConverter extends ConfigurationAnnotation { + public const OPTION_CREATION_RULE = 'creationRule'; + + public const RULE_CREATE = 'create'; + public const RULE_DESERIALIZE = 'deserialize'; + /** @var string */ private $name; diff --git a/models/classes/ParamConverter/Context/ObjectFactoryContext.php b/models/classes/ParamConverter/Context/ObjectFactoryContext.php new file mode 100644 index 0000000000..7b31cf9bca --- /dev/null +++ b/models/classes/ParamConverter/Context/ObjectFactoryContext.php @@ -0,0 +1,70 @@ +getParameter(self::PARAM_CLASS); + } + + public function getData(): array + { + return $this->getParameter(self::PARAM_DATA); + } + + public function getFormat(): string + { + return $this->getParameter(self::PARAM_FORMAT, 'json'); + } + + public function getContext(): array + { + return $this->getParameter(self::PARAM_CONTEXT, []); + } + + protected function getSupportedParameters(): array + { + return [ + self::PARAM_CLASS, + self::PARAM_DATA, + self::PARAM_FORMAT, + self::PARAM_CONTEXT, + ]; + } + + protected function validateParameter(string $parameter, $parameterValue): void + { + if ( + in_array($parameter, [self::PARAM_CLASS, self::PARAM_FORMAT], true) + && is_string($parameterValue) + ) { + return; + } + + if ( + in_array($parameter, [self::PARAM_DATA, self::PARAM_CONTEXT], true) + && is_array($parameterValue) + ) { + return; + } + + throw new InvalidArgumentException( + sprintf( + 'Context parameter %s is not valid.', + $parameter + ) + ); + } +} diff --git a/models/classes/ParamConverter/Context/ObjectFactoryContextInterface.php b/models/classes/ParamConverter/Context/ObjectFactoryContextInterface.php new file mode 100644 index 0000000000..7844d7abd5 --- /dev/null +++ b/models/classes/ParamConverter/Context/ObjectFactoryContextInterface.php @@ -0,0 +1,16 @@ +getParameter(self::PARAM_REQUEST); + } + + public function getController(): string + { + return $this->getParameter(self::PARAM_CONTROLLER); + } + + public function getMethod(): string + { + return $this->getParameter(self::PARAM_METHOD); + } + protected function getRequiredParameters(): array { return [ @@ -64,16 +79,12 @@ protected function validateParameter(string $parameter, $parameterValue): void } if ( - $parameter === self::PARAM_CONTROLLER - && (is_string($parameterValue) || is_object($parameterValue)) + in_array($parameter, [self::PARAM_CONTROLLER, self::PARAM_METHOD], true) + && is_string($parameterValue) ) { return; } - if ($parameter === self::PARAM_METHOD && is_string($parameterValue)) { - return; - } - throw new InvalidArgumentException( sprintf( 'Context parameter %s is not valid.', diff --git a/models/classes/ParamConverter/Context/ParamConverterListenerContextInterface.php b/models/classes/ParamConverter/Context/ParamConverterListenerContextInterface.php new file mode 100644 index 0000000000..d71aa0ce1a --- /dev/null +++ b/models/classes/ParamConverter/Context/ParamConverterListenerContextInterface.php @@ -0,0 +1,16 @@ +context = $context; } - public function getContext(): ContextInterface + public function getContext(): ParamConverterListenerContextInterface { return $this->context; } diff --git a/models/classes/ParamConverter/EventListener/ParamConverterListener.php b/models/classes/ParamConverter/EventListener/ParamConverterListener.php index d2c18d2f80..fe253e4b23 100644 --- a/models/classes/ParamConverter/EventListener/ParamConverterListener.php +++ b/models/classes/ParamConverter/EventListener/ParamConverterListener.php @@ -28,7 +28,6 @@ use oat\tao\model\ParamConverter\Event\ParamConverterEvent; use oat\tao\model\ParamConverter\Configuration\ParamConverter; use oat\tao\model\ParamConverter\Configuration\ConfiguratorInterface; -use oat\tao\model\ParamConverter\Context\ParamConverterListenerContext; use oat\tao\model\ParamConverter\Manager\ParamConverterManagerInterface; class ParamConverterListener implements ListenerInterface @@ -61,13 +60,12 @@ public function handleEvent(Event $event): void } $context = $event->getContext(); - /** @var RequestInterface $request */ - $request = $context->getParameter(ParamConverterListenerContext::PARAM_REQUEST); + $request = $context->getRequest(); $configurations = $this->extractConfigurations($request); - $controller = $context->getParameter(ParamConverterListenerContext::PARAM_CONTROLLER); - $method = $context->getParameter(ParamConverterListenerContext::PARAM_METHOD); + $controller = $context->getController(); + $method = $context->getMethod(); // Automatically apply conversion for non-configured objects if ($this->autoConvert && is_callable([$controller, $method])) { diff --git a/models/classes/ParamConverter/Factory/ObjectFactory.php b/models/classes/ParamConverter/Factory/ObjectFactory.php new file mode 100644 index 0000000000..fa42f5570d --- /dev/null +++ b/models/classes/ParamConverter/Factory/ObjectFactory.php @@ -0,0 +1,71 @@ +serializer = $serializer; + } + + public function create(ObjectFactoryContextInterface $context): object + { + $constructorArgs = []; + $data = $context->getData(); + + $reflectionClass = new ReflectionClass($context->getClass()); + $constructor = $reflectionClass->getConstructor(); + + if ($constructor) { + foreach ($constructor->getParameters() as $constructorParameter) { + $constructorParameterName = $constructorParameter->getName(); + + if (array_key_exists($constructorParameterName, $data)) { + $constructorArgs[$constructorParameterName] = $data[$constructorParameterName]; + unset($data[$constructorParameterName]); + } + } + } + + $instance = $reflectionClass->newInstanceArgs($constructorArgs); + + foreach ($data as $queryParameter => $value) { + if ($reflectionClass->hasMethod('set' . $queryParameter)) { + $reflectionClass + ->getMethod('set' . $queryParameter) + ->invoke($instance, $value); + } elseif ($reflectionClass->hasProperty($queryParameter)) { + $reflectionClass->getProperty($queryParameter)->setValue($instance, $value); + } + } + + return $instance; + } + + public function deserialize(ObjectFactoryContextInterface $context): object + { + $format = $context->getFormat(); + + if ($format !== 'json') { + throw new InvalidArgumentException('Currently, only the "json" format is supported.'); + } + + return $this->serializer->deserialize( + json_encode($context->getData()), + $context->getClass(), + $format, + $context->getContext() + ); + } +} diff --git a/models/classes/ParamConverter/Factory/ObjectFactoryInterface.php b/models/classes/ParamConverter/Factory/ObjectFactoryInterface.php new file mode 100644 index 0000000000..642d5aab6d --- /dev/null +++ b/models/classes/ParamConverter/Factory/ObjectFactoryInterface.php @@ -0,0 +1,14 @@ +set(ObjectFactory::class, ObjectFactory::class) + ->args( + [ + service(Serializer::class), + ] + ); + foreach (self::PARAM_CONVERTERS as $paramConverter) { $services ->set($paramConverter, $paramConverter) - ->public(); + ->args( + [ + service(ObjectFactory::class), + ] + ); } } @@ -63,7 +77,6 @@ private function provideParamConverterManager(ServicesConfigurator $services): v { $services ->set(ParamConverterManager::class, ParamConverterManager::class) - ->public() ->args( [ array_map( @@ -80,9 +93,7 @@ private function provideParamConverterListener( ServicesConfigurator $services, ParametersConfigurator $parameters ): void { - $services - ->set(Configurator::class, Configurator::class) - ->public(); + $services->set(Configurator::class, Configurator::class); $parameters->set('autoConvert', true); $services diff --git a/models/classes/ParamConverter/Request/AbstractParamConverter.php b/models/classes/ParamConverter/Request/AbstractParamConverter.php index f077417c23..2e68940910 100644 --- a/models/classes/ParamConverter/Request/AbstractParamConverter.php +++ b/models/classes/ParamConverter/Request/AbstractParamConverter.php @@ -23,12 +23,21 @@ namespace oat\tao\model\ParamConverter\Request; use Throwable; -use ReflectionClass; use oat\tao\model\HttpFoundation\Request\RequestInterface; use oat\tao\model\ParamConverter\Configuration\ParamConverter; +use oat\tao\model\ParamConverter\Context\ObjectFactoryContext; +use oat\tao\model\ParamConverter\Factory\ObjectFactoryInterface; abstract class AbstractParamConverter implements ParamConverterInterface { + /** @var ObjectFactoryInterface */ + private $objectFactory; + + public function __construct(ObjectFactoryInterface $objectFactory) + { + $this->objectFactory = $objectFactory; + } + public function getPriority(): int { return 0; @@ -37,10 +46,9 @@ public function getPriority(): int public function apply(RequestInterface $request, ParamConverter $configuration): bool { try { - $object = $this->createObject( - $this->getData($request, $configuration->getOptions()), - $configuration->getClass() - ); + $options = $configuration->getOptions(); + $data = $this->getData($request, $options); + $object = $this->createObject($data, $configuration->getClass(), $options); $converted = $request->getAttribute(self::ATTRIBUTE_CONVERTED, []); $converted[$configuration->getName()] = $object; @@ -60,35 +68,22 @@ public function supports(ParamConverter $configuration): bool abstract protected function getData(RequestInterface $request, array $options): array; - private function createObject(array $data, string $class): object + private function createObject(array $data, string $class, array $options): object { - $constructorArgs = []; - $reflectionClass = new ReflectionClass($class); - $constructor = $reflectionClass->getConstructor(); - - if ($constructor) { - foreach ($constructor->getParameters() as $constructorParameter) { - $constructorParameterName = $constructorParameter->getName(); - - if (array_key_exists($constructorParameterName, $data)) { - $constructorArgs[$constructorParameterName] = $data[$constructorParameterName]; - unset($data[$constructorParameterName]); - } - } - } - - $instance = $reflectionClass->newInstanceArgs($constructorArgs); - - foreach ($data as $queryParameter => $value) { - if ($reflectionClass->hasMethod('set' . $queryParameter)) { - $reflectionClass - ->getMethod('set' . $queryParameter) - ->invoke($instance, $value); - } elseif ($reflectionClass->hasProperty($queryParameter)) { - $reflectionClass->getProperty($queryParameter)->setValue($instance, $value); - } + $rule = $options[ParamConverter::OPTION_CREATION_RULE] ?? null; + $context = new ObjectFactoryContext( + [ + ObjectFactoryContext::PARAM_CLASS => $class, + ObjectFactoryContext::PARAM_DATA => $data, + ] + ); + + if ($rule === ParamConverter::RULE_CREATE) { + $object = $this->objectFactory->create($context); + } else { + $object = $this->objectFactory->deserialize($context); } - return $instance; + return $object; } } diff --git a/models/classes/Serializer/Serializer.php b/models/classes/Serializer/Serializer.php new file mode 100644 index 0000000000..6ada52b275 --- /dev/null +++ b/models/classes/Serializer/Serializer.php @@ -0,0 +1,34 @@ +serializer = $serializer; + } + + /** + * {@inheritdoc} + */ + public function serialize($data, string $format, array $context = []): string + { + return $this->serializer->serialize($data, $format, $context); + } + + /** + * {@inheritdoc} + */ + public function deserialize($data, string $type, string $format, array $context = []) + { + return $this->serializer->deserialize($data, $type, $format, $context); + } +} diff --git a/models/classes/Serializer/SerializerInterface.php b/models/classes/Serializer/SerializerInterface.php new file mode 100644 index 0000000000..527d9adb05 --- /dev/null +++ b/models/classes/Serializer/SerializerInterface.php @@ -0,0 +1,26 @@ +services(); + + $services->set(ObjectNormalizer::class, ObjectNormalizer::class); + $services->set(JsonEncoder::class, JsonEncoder::class); + + $services + ->set(SymfonySerializerInterface::class, SymfonySerializerAlias::class) + ->args( + [ + [ + service(ObjectNormalizer::class), + ], + [ + service(JsonEncoder::class), + ], + ] + ); + + $services + ->set(Serializer::class, Serializer::class) + ->args( + [ + service(SymfonySerializerInterface::class), + ] + ); + } +} From d4138c0c1e0aa2aa6d81960814c4d2e1059f5c80 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Wed, 13 Oct 2021 17:03:46 +0000 Subject: [PATCH 14/17] refactor: add missed dockblocks --- .../Context/ObjectFactoryContext.php | 18 ++++++++++++++++++ .../Context/ObjectFactoryContextInterface.php | 18 ++++++++++++++++++ .../ParamConverterListenerContextInterface.php | 18 ++++++++++++++++++ .../ParamConverter/Factory/ObjectFactory.php | 18 ++++++++++++++++++ .../Factory/ObjectFactoryInterface.php | 18 ++++++++++++++++++ models/classes/Serializer/Serializer.php | 18 ++++++++++++++++++ .../classes/Serializer/SerializerInterface.php | 18 ++++++++++++++++++ .../Serializer/SerializerServiceProvider.php | 18 ++++++++++++++++++ 8 files changed, 144 insertions(+) diff --git a/models/classes/ParamConverter/Context/ObjectFactoryContext.php b/models/classes/ParamConverter/Context/ObjectFactoryContext.php index 7b31cf9bca..d4988593ff 100644 --- a/models/classes/ParamConverter/Context/ObjectFactoryContext.php +++ b/models/classes/ParamConverter/Context/ObjectFactoryContext.php @@ -1,5 +1,23 @@ Date: Mon, 15 Nov 2021 11:47:51 +0000 Subject: [PATCH 15/17] tests: add classes and test action for perfomance testing --- actions/class.TestAction.php | 58 +++++++++++++++++++ models/classes/TestParamConverter/Query.php | 44 ++++++++++++++ .../classes/TestParamConverter/SubQuery.php | 29 ++++++++++ 3 files changed, 131 insertions(+) create mode 100644 actions/class.TestAction.php create mode 100644 models/classes/TestParamConverter/Query.php create mode 100644 models/classes/TestParamConverter/SubQuery.php diff --git a/actions/class.TestAction.php b/actions/class.TestAction.php new file mode 100644 index 0000000000..c3da3599b9 --- /dev/null +++ b/actions/class.TestAction.php @@ -0,0 +1,58 @@ + + */ + +declare(strict_types=1); + +use oat\tao\model\TestParamConverter\Query; +use oat\tao\model\http\HttpJsonResponseTrait; +use oat\tao\model\ParamConverter\Configuration\ParamConverter; + +class tao_actions_TestAction extends tao_actions_CommonModule +{ + use HttpJsonResponseTrait; + + public function withoutParamConverter(): void + { + $uri = $this->getGetParameter('uri'); + $listOfUris = $this->getGetParameter('listOfUris'); + $subQuery = $this->getGetParameter('subQuery'); + + $subQueryUri = $subQuery['uri'] ?? ''; + $subQueryValue = $subQuery['value'] ?? 0; + + $this->setSuccessJsonResponse(__METHOD__); + } + + /** + * @ParamConverter("query", converter="oat.tao.param_converter.query") + */ + public function withParamConverter(Query $query): void + { + $uri = $query->getUri(); + $listOfUris = $query->getListOfUris(); + + $subQueryUri = $query->getSubQuery()->getUri(); + $subQueryValue = $query->getSubQuery()->getValue(); + + $this->setSuccessJsonResponse(__METHOD__); + } +} diff --git a/models/classes/TestParamConverter/Query.php b/models/classes/TestParamConverter/Query.php new file mode 100644 index 0000000000..225eadaf3b --- /dev/null +++ b/models/classes/TestParamConverter/Query.php @@ -0,0 +1,44 @@ +uri = $uri; + $this->listOfUris = $listOfUris; + $this->subQuery = new SubQuery(); + } + + public function getUri(): string + { + return $this->uri; + } + + public function getListOfUris(): array + { + return $this->listOfUris; + } + + public function getSubQuery(): SubQuery + { + return $this->subQuery; + } + + public function setSubQuery(SubQuery $subQuery): void + { + $this->subQuery = $subQuery; + } +} diff --git a/models/classes/TestParamConverter/SubQuery.php b/models/classes/TestParamConverter/SubQuery.php new file mode 100644 index 0000000000..1b84c8ba77 --- /dev/null +++ b/models/classes/TestParamConverter/SubQuery.php @@ -0,0 +1,29 @@ +uri; + } + + public function getValue(): int + { + return $this->value; + } + + public function setValue(int $value): void + { + $this->value = $value; + } +} From eeb886d90df516d8d0ea846de20c8b495ae68d5d Mon Sep 17 00:00:00 2001 From: Andrei Shapiro <59471572+shpran@users.noreply.github.com> Date: Sat, 20 Nov 2021 16:03:41 +0300 Subject: [PATCH 16/17] refactor: remove author and change year --- actions/class.TestAction.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/actions/class.TestAction.php b/actions/class.TestAction.php index c3da3599b9..e89ec5de3d 100644 --- a/actions/class.TestAction.php +++ b/actions/class.TestAction.php @@ -15,9 +15,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * Copyright (c) 2020-2021 (original work) Open Assessment Technologies SA; - * - * @author Sergei Mikhailov + * Copyright (c) 2021 (original work) Open Assessment Technologies SA; */ declare(strict_types=1); From 43c14bb7955707d93f18707ddbac61f221eb9d32 Mon Sep 17 00:00:00 2001 From: Andrei Shapiro Date: Fri, 3 Dec 2021 14:33:02 +0000 Subject: [PATCH 17/17] refactor: use method instead of passing ParamConverters through the constructor. Do not use converter name inside ParamConverters --- .../Manager/ParamConverterManager.php | 26 +++------ .../ParamConverterManagerInterface.php | 4 +- .../ParamConverterServiceProvider.php | 55 +++++++++---------- .../Request/AbstractParamConverter.php | 7 +-- .../Request/ParamConverterInterface.php | 4 -- .../Request/QueryParamConverter.php | 5 -- 6 files changed, 35 insertions(+), 66 deletions(-) diff --git a/models/classes/ParamConverter/Manager/ParamConverterManager.php b/models/classes/ParamConverter/Manager/ParamConverterManager.php index a657cde9fe..21e7a98359 100644 --- a/models/classes/ParamConverter/Manager/ParamConverterManager.php +++ b/models/classes/ParamConverter/Manager/ParamConverterManager.php @@ -29,22 +29,12 @@ class ParamConverterManager implements ParamConverterManagerInterface { - /** @var array */ + /** @var array */ private $converters = []; - /** @var array */ + /** @var array */ private $namedConverters = []; - /** - * @param ParamConverterInterface[] $converters - */ - public function __construct(array $converters = []) - { - foreach ($converters as $converter) { - $this->add($converter, $converter->getPriority(), $converter->getName()); - } - } - /** * {@inheritdoc} */ @@ -58,7 +48,7 @@ public function apply(RequestInterface $request, array $configurations): void /** * {@inheritdoc} */ - public function add(ParamConverterInterface $converter, ?int $priority = 0, string $name = null): void + public function add(ParamConverterInterface $converter, string $name = null, ?int $priority = 0): void { if ($priority !== null) { if (!isset($this->converters[$priority])) { @@ -95,7 +85,7 @@ private function applyConfiguration(RequestInterface $request, ParamConverter $c // If the value is already an instance of the class we are trying to convert it into // we should continue as no conversion is required - if (is_object($value) && $value instanceof $className) { + if ($value instanceof $className) { return; } @@ -132,15 +122,13 @@ private function checkProvidedConverterName(string $converterName, string $param } } - private function checkConverterSupport( - ParamConverterInterface $converter, - ParamConverter $configuration - ): void { + private function checkConverterSupport(ParamConverterInterface $converter, ParamConverter $configuration): void + { if (!$converter->supports($configuration)) { throw new RuntimeException( sprintf( 'Converter "%s" does not support conversion of parameter "%s".', - $converter->getName(), + $configuration->getConverter(), $configuration->getName() ) ); diff --git a/models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php b/models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php index 4073eb91f6..90751ca350 100644 --- a/models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php +++ b/models/classes/ParamConverter/Manager/ParamConverterManagerInterface.php @@ -43,10 +43,8 @@ public function apply(RequestInterface $request, array $configurations): void; * converters with a $priority. If you pass a $priority = null then the * added converter will not be part of the iteration chain and can only * be invoked explicitly. - * - * @param int|null $priority the priority (between -10 and 10) */ - public function add(ParamConverterInterface $converter, ?int $priority = 0, string $name = null): void; + public function add(ParamConverterInterface $converter, string $name = null, ?int $priority = 0): void; /** * Returns all registered param converters. diff --git a/models/classes/ParamConverter/ParamConverterServiceProvider.php b/models/classes/ParamConverter/ParamConverterServiceProvider.php index 78fbd4f112..0859a3d5ce 100644 --- a/models/classes/ParamConverter/ParamConverterServiceProvider.php +++ b/models/classes/ParamConverter/ParamConverterServiceProvider.php @@ -31,30 +31,30 @@ use oat\generis\model\DependencyInjection\ContainerServiceProviderInterface; use Symfony\Component\DependencyInjection\Loader\Configurator\ServicesConfigurator; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symfony\Component\DependencyInjection\Loader\Configurator\ParametersConfigurator; -use function Symfony\Component\DependencyInjection\Loader\Configurator\param; use function Symfony\Component\DependencyInjection\Loader\Configurator\service; class ParamConverterServiceProvider implements ContainerServiceProviderInterface { private const PARAM_CONVERTERS = [ - QueryParamConverter::class, + 'oat.tao.param_converter.query' => QueryParamConverter::class, ]; + /** @var ServicesConfigurator */ + private $services; + public function __invoke(ContainerConfigurator $configurator): void { - $services = $configurator->services(); - $parameters = $configurator->parameters(); + $this->services = $configurator->services(); - $this->provideConverters($services); - $this->provideParamConverterManager($services); - $this->provideParamConverterListener($services, $parameters); + $this->provideConverters(); + $this->provideParamConverterManager(); + $this->provideParamConverterListener(); } - private function provideConverters(ServicesConfigurator $services): void + private function provideConverters(): void { - $services + $this->services ->set(ObjectFactory::class, ObjectFactory::class) ->args( [ @@ -63,7 +63,7 @@ private function provideConverters(ServicesConfigurator $services): void ); foreach (self::PARAM_CONVERTERS as $paramConverter) { - $services + $this->services ->set($paramConverter, $paramConverter) ->args( [ @@ -73,37 +73,34 @@ private function provideConverters(ServicesConfigurator $services): void } } - private function provideParamConverterManager(ServicesConfigurator $services): void + private function provideParamConverterManager(): void { - $services - ->set(ParamConverterManager::class, ParamConverterManager::class) - ->args( + $this->services->set(ParamConverterManager::class, ParamConverterManager::class); + $paramConverterManager = $this->services->get(ParamConverterManager::class); + + foreach (self::PARAM_CONVERTERS as $name => $paramConverterId) { + $paramConverterManager->call( + 'add', [ - array_map( - static function (string $paramConverter) { - return service($paramConverter); - }, - self::PARAM_CONVERTERS - ), + service($paramConverterId), + $name, ] ); + } } - private function provideParamConverterListener( - ServicesConfigurator $services, - ParametersConfigurator $parameters - ): void { - $services->set(Configurator::class, Configurator::class); + private function provideParamConverterListener(): void + { + $this->services->set(Configurator::class, Configurator::class); - $parameters->set('autoConvert', true); - $services + $this->services ->set(ParamConverterListener::class, ParamConverterListener::class) ->public() ->args( [ service(Configurator::class), service(ParamConverterManager::class), - param('autoConvert'), + true, ] ); } diff --git a/models/classes/ParamConverter/Request/AbstractParamConverter.php b/models/classes/ParamConverter/Request/AbstractParamConverter.php index 2e68940910..eced57e117 100644 --- a/models/classes/ParamConverter/Request/AbstractParamConverter.php +++ b/models/classes/ParamConverter/Request/AbstractParamConverter.php @@ -38,11 +38,6 @@ public function __construct(ObjectFactoryInterface $objectFactory) $this->objectFactory = $objectFactory; } - public function getPriority(): int - { - return 0; - } - public function apply(RequestInterface $request, ParamConverter $configuration): bool { try { @@ -63,7 +58,7 @@ public function apply(RequestInterface $request, ParamConverter $configuration): public function supports(ParamConverter $configuration): bool { - return $configuration->getClass() !== null && $configuration->getConverter() === $this->getName(); + return $configuration->getClass() !== null; } abstract protected function getData(RequestInterface $request, array $options): array; diff --git a/models/classes/ParamConverter/Request/ParamConverterInterface.php b/models/classes/ParamConverter/Request/ParamConverterInterface.php index dbb107a222..f3258a0f63 100644 --- a/models/classes/ParamConverter/Request/ParamConverterInterface.php +++ b/models/classes/ParamConverter/Request/ParamConverterInterface.php @@ -29,10 +29,6 @@ interface ParamConverterInterface { public const ATTRIBUTE_CONVERTED = 'converted'; - public function getPriority(): int; - - public function getName(): string; - /** * Stores the object in the request. */ diff --git a/models/classes/ParamConverter/Request/QueryParamConverter.php b/models/classes/ParamConverter/Request/QueryParamConverter.php index 9996fb9cda..a59c5c5ed1 100644 --- a/models/classes/ParamConverter/Request/QueryParamConverter.php +++ b/models/classes/ParamConverter/Request/QueryParamConverter.php @@ -26,11 +26,6 @@ class QueryParamConverter extends AbstractParamConverter { - public function getName(): string - { - return 'oat.tao.param_converter.query'; - } - protected function getData(RequestInterface $request, array $options): array { return $request->getQueryParameters();