diff --git a/phpstan.neon b/phpstan.neon index 30e9572..9cae086 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,18 +1,7 @@ parameters: - level: 6 + level: 10 paths: - src - tests ignoreErrors: - - '#type has no value type specified in iterable type#' - - '#has parameter .* with no value type specified in iterable type#' - - '#has no value type specified in iterable type array#' - - '#configureOptions\(\) has no return type specified.#' - - '#configure\(\) has no return type specified#' - - '#process\(\) has no return type specified#' - - '#should return Iterator but returns Traversable#' - - '#Negated boolean expression is always false#' - checkGenericClassInNonGenericObjectType: false - reportUnmatchedIgnoredErrors: false - inferPrivatePropertyTypeFromConstructor: true - treatPhpDocTypesAsCertain: false + - identifier: parameter.defaultValue diff --git a/src/Client/Client.php b/src/Client/Client.php index 1532ad9..94d586f 100644 --- a/src/Client/Client.php +++ b/src/Client/Client.php @@ -19,6 +19,9 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; +/** + * @phpstan-import-type RequestOptions from \CleverAge\RestProcessBundle\Task\RequestTask + */ class Client implements ClientInterface { public function __construct( @@ -50,6 +53,8 @@ public function setUri(string $uri): void } /** + * @param RequestOptions $options + * * @throws RestRequestException */ public function call(array $options = []): ResponseInterface @@ -70,7 +75,7 @@ public function call(array $options = []): ResponseInterface $this->getRequestUri($options), $this->getRequestOptions($options), ); - } catch (\Exception|\Throwable $e) { + } catch (\Throwable $e) { $this->logger->error( 'Rest request failed', [ @@ -110,19 +115,40 @@ protected function configureOptions(OptionsResolver $resolver): void $resolver->setAllowedTypes('data', ['array', 'string', 'null']); } + /** + * @param RequestOptions $options + * + * @return RequestOptions + */ protected function getOptions(array $options = []): array { $resolver = new OptionsResolver(); $this->configureOptions($resolver); - return $resolver->resolve($options); + /** @var RequestOptions $resolved */ + $resolved = $resolver->resolve($options); + + return $resolved; } + /** + * @param RequestOptions $options + */ protected function getRequestUri(array $options = []): string { return $this->replaceParametersInUri($this->constructUri($options), $options); } + /** + * @param RequestOptions $options + * + * @return array{ + * 'headers': array<mixed>, + * 'json'?: array<mixed>|string|null, + * 'query'?: array<mixed>|string|null, + * 'body'?: array<mixed>|string|null + * } + */ protected function getRequestOptions(array $options = []): array { $requestOptions = []; @@ -144,6 +170,9 @@ protected function getRequestOptions(array $options = []): array return $requestOptions; } + /** + * @param RequestOptions $options + */ protected function constructUri(array $options): string { $uri = ltrim((string) $options['url'], '/'); @@ -156,9 +185,13 @@ protected function getApiUrl(): string return $this->geUri(); } + /** + * @param RequestOptions $options + */ protected function replaceParametersInUri(string $uri, array $options = []): string { - if (\array_key_exists('url_parameters', $options) && $options['url_parameters']) { + if ($options['url_parameters']) { + /** @var array<string> $search */ $search = array_keys($options['url_parameters']); array_walk( $search, @@ -166,6 +199,7 @@ static function (&$item, $key) { $item = '{'.$item.'}'; } ); + /** @var array<string> $replace */ $replace = array_values($options['url_parameters']); array_walk( $replace, diff --git a/src/Client/ClientInterface.php b/src/Client/ClientInterface.php index 7e066e1..b08bf2a 100644 --- a/src/Client/ClientInterface.php +++ b/src/Client/ClientInterface.php @@ -15,6 +15,9 @@ use Symfony\Contracts\HttpClient\ResponseInterface; +/** + * @phpstan-import-type RequestOptions from \CleverAge\RestProcessBundle\Task\RequestTask + */ interface ClientInterface { /** @@ -26,5 +29,8 @@ public function geUri(): string; public function setUri(string $uri): void; + /** + * @param RequestOptions $options + */ public function call(array $options = []): ResponseInterface; } diff --git a/src/Task/RequestTask.php b/src/Task/RequestTask.php index 0abc302..d8b4b9b 100644 --- a/src/Task/RequestTask.php +++ b/src/Task/RequestTask.php @@ -22,7 +22,34 @@ use Symfony\Component\OptionsResolver\Exception\AccessException; use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; use Symfony\Component\OptionsResolver\OptionsResolver; - +use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; + +/** + * @phpstan-type Options array{ + * 'client': string, + * 'url': string, + * 'method': string, + * 'headers': array<mixed>, + * 'url_parameters': array<mixed>, + * 'data': array<mixed>|string|null, + * 'sends': string, + * 'expects': string, + * 'valid_response_code': array<int>, + * 'log_response': bool, + * } + * @phpstan-type RequestOptions array{ + * 'url': string, + * 'method': string, + * 'headers': array<mixed>, + * 'url_parameters': array<mixed>, + * 'sends': string, + * 'expects': string, + * 'data': array<mixed>|string|null + * } + */ class RequestTask extends AbstractConfigurableTask { public function __construct(protected LoggerInterface $logger, protected ClientRegistry $registry) @@ -31,9 +58,15 @@ public function __construct(protected LoggerInterface $logger, protected ClientR /** * @throws MissingClientException + * @throws ClientExceptionInterface + * @throws RedirectionExceptionInterface + * @throws ServerExceptionInterface + * @throws TransportExceptionInterface + * @throws \Throwable */ public function execute(ProcessState $state): void { + /** @var Options $options */ $options = $this->getOptions($state); $requestOptions = $this->getRequestOptions($state); @@ -68,7 +101,7 @@ public function execute(ProcessState $state): void } $state->setOutput($response->getContent()); - } catch (\Exception|\Throwable $e) { + } catch (\Throwable $e) { $this->logger->error( 'REST request failed', [ @@ -116,8 +149,12 @@ protected function configureOptions(OptionsResolver $resolver): void $resolver->setAllowedTypes('log_response', ['bool']); } + /** + * @return RequestOptions + */ protected function getRequestOptions(ProcessState $state): array { + /** @var Options $options */ $options = $this->getOptions($state); $requestOptions = [ @@ -130,8 +167,12 @@ protected function getRequestOptions(ProcessState $state): array 'data' => $options['data'], ]; + /** @var array<mixed> $input */ $input = $state->getInput() ?: []; - return array_merge($requestOptions, $input); + /** @var RequestOptions $mergedOptions */ + $mergedOptions = array_merge($requestOptions, $input); + + return $mergedOptions; } }