diff --git a/.docker-http-client/http-client/Dockerfile b/.docker-http-client/http-client/Dockerfile index ea16689..bfb0d29 100644 --- a/.docker-http-client/http-client/Dockerfile +++ b/.docker-http-client/http-client/Dockerfile @@ -1,5 +1,5 @@ -FROM php:8.0-fpm-alpine +FROM php:8.1-fpm-alpine COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer -WORKDIR /http-client \ No newline at end of file +WORKDIR /http-client diff --git a/composer.json b/composer.json index 24fd710..fa2bb6e 100644 --- a/composer.json +++ b/composer.json @@ -14,26 +14,26 @@ } ], "require": { - "php": ">=7.1", - "composer/ca-bundle": "^1.1", - "psr/log": "^1.0", - "psr/http-message": "^1.0", + "php": ">=8.1", + "composer/ca-bundle": "^1.4", + "psr/log": "^3.0", + "psr/http-message": "^1.0 | ^2.0", "psr/http-client": "^1.0", - "guzzlehttp/psr7": "^1.4 | ^2.0", + "guzzlehttp/psr7": "^1.4 |^2.6", "ext-json": "*", "ext-curl": "*" }, "require-dev": { - "nette/tester": ">=2.3", + "nette/tester": "^2.5", "guzzlehttp/guzzle": "^6.0 | ^7.0", - "tracy/tracy": ">=2.7", - "nette/di": "^3.0", - "phpstan/phpstan": "^0.12.85", - "phpstan/phpstan-nette": "^0.12.17", - "react/http": "^1.3", - "react/child-process": "^0.6.2", - "phpstan/phpstan-strict-rules": "^0.12.9", - "orisai/coding-standard": "^1.1" + "tracy/tracy": "^2.10", + "nette/di": "^3.1", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-nette": "^1.2", + "react/http": "^1.9", + "react/child-process": "^0.6.5", + "phpstan/phpstan-strict-rules": "^1.5", + "orisai/coding-standard": "^3.10" }, "suggest": { "ext-curl": "to use class CurlHttpClient", diff --git a/src/Fapi/HttpClient/BaseLoggingFormatter.php b/src/Fapi/HttpClient/BaseLoggingFormatter.php index b015c28..9511fbe 100644 --- a/src/Fapi/HttpClient/BaseLoggingFormatter.php +++ b/src/Fapi/HttpClient/BaseLoggingFormatter.php @@ -2,118 +2,7 @@ namespace Fapi\HttpClient; -use Fapi\HttpClient\Utils\Json; -use Fapi\HttpClient\Utils\JsonException; -use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; -use Throwable; -use function base64_encode; -use function extension_loaded; -use function function_exists; -use function get_class; -use function iconv_substr; -use function mb_substr; -use function serialize; -use function sprintf; -use function strlen; -use function substr; -use const JSON_UNESCAPED_UNICODE; - -final class BaseLoggingFormatter implements ILoggingFormatter +final class BaseLoggingFormatter extends \Fapi\HttpClient\LoggingFormatters\BaseLoggingFormatter implements ILoggingFormatter { - /** @var int */ - private $maxBodyLength; - - public function __construct(int $maxBodyLength = 40000) - { - $this->maxBodyLength = $maxBodyLength; - } - - public function formatSuccessful(RequestInterface $request, ResponseInterface $response, float $elapsedTime): string - { - return 'Fapi\HttpClient: an HTTP request has been sent.' - . $this->dumpHttpRequest($request) - . $this->dumpHttpResponse($response) - . $this->dumpElapsedTime($elapsedTime); - } - - public function formatFailed(RequestInterface $request, Throwable $exception, float $elapsedTime): string - { - return 'Fapi\HttpClient: an HTTP request failed.' - . $this->dumpHttpRequest($request) - . $this->dumpException($exception) - . $this->dumpElapsedTime($elapsedTime); - } - - private function dumpHttpRequest(RequestInterface $request): string - { - $body = $this->processBody((string) $request->getBody()); - - return ' Request URL: ' . $this->dumpValue((string) $request->getUri()) - . ' Request method: ' . $this->dumpValue($request->getMethod()) - . ' Request headers: ' . $this->dumpValue($request->getHeaders()) - . ' Request body: ' . $this->dumpValue($body); - } - - private function dumpHttpResponse(ResponseInterface $response): string - { - $body = $this->processBody((string) $response->getBody()); - - return ' Response status code: ' . $this->dumpValue($response->getStatusCode()) - . ' Response headers: ' . $this->dumpValue($response->getHeaders()) - . ' Response body: ' . $this->dumpValue($body); - } - - private function dumpException(Throwable $exception): string - { - $dump = ' Exception type: ' . $this->dumpValue(get_class($exception)) - . ' Exception message: ' . $this->dumpValue($exception->getMessage()); - - if ($exception->getPrevious() !== null) { - $previousException = $exception->getPrevious(); - - $dump .= ' Previous exception type: ' . $this->dumpValue(get_class($previousException)) - . ' Previous exception message: ' . $this->dumpValue($previousException->getMessage()); - } - - return $dump; - } - - private function dumpElapsedTime(float $elapsedTime): string - { - $elapsedTime *= 1000; - - return ' Elapsed time: ' . sprintf('%0.2f', $elapsedTime) . ' ms'; - } - - /** - * @param mixed $value - */ - private function dumpValue($value): string - { - try { - return Json::encode($value, JSON_UNESCAPED_UNICODE); - } catch (JsonException $e) { - return '(serialized) ' . base64_encode(serialize($value)); - } - } - - private function processBody(string $body): string - { - if (strlen($body) <= $this->maxBodyLength) { - return $body; - } - - if (function_exists('mb_substr')) { - return mb_substr($body, 0, $this->maxBodyLength, 'UTF-8'); - } - - if (!extension_loaded('iconv')) { - return (string) iconv_substr($body, 0, $this->maxBodyLength, 'UTF-8'); - } - - return substr($body, 0, $this->maxBodyLength); - } - } diff --git a/src/Fapi/HttpClient/Bridges/NetteDI/HttpClientExtension.php b/src/Fapi/HttpClient/Bridges/NetteDI/HttpClientExtension.php index 76533e8..dd7621e 100644 --- a/src/Fapi/HttpClient/Bridges/NetteDI/HttpClientExtension.php +++ b/src/Fapi/HttpClient/Bridges/NetteDI/HttpClientExtension.php @@ -1,6 +1,6 @@ */ - public $defaults = [ + public array $defaults = [ 'type' => 'guzzle', 'logging' => false, 'bar' => false, ]; /** @var array */ - private $typeClasses = [ + private array $typeClasses = [ 'curl' => CurlHttpClient::class, 'guzzle' => GuzzleHttpClient::class, ]; diff --git a/src/Fapi/HttpClient/Bridges/Tracy/BarHttpClient.php b/src/Fapi/HttpClient/Bridges/Tracy/BarHttpClient.php index a18570a..b1b8f24 100644 --- a/src/Fapi/HttpClient/Bridges/Tracy/BarHttpClient.php +++ b/src/Fapi/HttpClient/Bridges/Tracy/BarHttpClient.php @@ -16,24 +16,17 @@ final class BarHttpClient implements IHttpClient, IBarPanel { - /** @var int */ - private $maxRequests = 100; - - /** @var IHttpClient */ - private $httpClient; + private int $maxRequests = 100; /** @var array */ - private $requests = []; + private array $requests = []; - /** @var int */ - private $count = 0; + private int $count = 0; - /** @var float */ - private $totalTime = 0.0; + private float $totalTime = 0.0; - public function __construct(IHttpClient $httpClient) + public function __construct(private IHttpClient $httpClient) { - $this->httpClient = $httpClient; Debugger::getBar()->addPanel($this); } diff --git a/src/Fapi/HttpClient/Bridges/Tracy/TracyToPsrLogger.php b/src/Fapi/HttpClient/Bridges/Tracy/TracyToPsrLogger.php index 6ebf0f4..1bfd15a 100644 --- a/src/Fapi/HttpClient/Bridges/Tracy/TracyToPsrLogger.php +++ b/src/Fapi/HttpClient/Bridges/Tracy/TracyToPsrLogger.php @@ -4,6 +4,7 @@ use Psr\Log\AbstractLogger; use Psr\Log\LogLevel; +use Stringable; use Throwable; use Tracy\ILogger; @@ -21,18 +22,16 @@ final class TracyToPsrLogger extends AbstractLogger LogLevel::DEBUG => ILogger::DEBUG, ]; - /** @var ILogger */ - private $tracyLogger; - - public function __construct(ILogger $tracyLogger) + public function __construct(private ILogger $tracyLogger) { - $this->tracyLogger = $tracyLogger; } /** + * @param array $context + * * @inheritdoc */ - public function log($level, $message, array $context = []) + public function log($level, string|Stringable $message, array $context = []): void { $priority = self::PRIORITY_MAP[$level] ?? ILogger::ERROR; diff --git a/src/Fapi/HttpClient/CapturingHttpClient.php b/src/Fapi/HttpClient/CapturingHttpClient.php index 5bc9b06..ee5c928 100644 --- a/src/Fapi/HttpClient/CapturingHttpClient.php +++ b/src/Fapi/HttpClient/CapturingHttpClient.php @@ -15,40 +15,29 @@ class CapturingHttpClient implements IHttpClient { - /** @var IHttpClient */ - private $httpClient; - /** @var array */ - private $httpRequests = []; + private array $httpRequests = []; /** @var array */ - private $httpResponses = []; - - /** @var string */ - private $file; - - /** @var string */ - private $className; + private array $httpResponses = []; - public function __construct(IHttpClient $httpClient, string $file, string $className) + public function __construct(private IHttpClient $httpClient, private string $file, private string $className) { if (!class_exists('Tester\Dumper')) { throw new InvalidStateException('Capturing HTTP client requires Nette Tester.'); } - $this->httpClient = $httpClient; - if (is_file($file)) { require_once $file; spl_autoload($className); if (class_exists($className)) { - $this->httpClient = new $className(); + /** @var IHttpClient $httpClient */ + $httpClient = new $className(); + + $this->httpClient = $httpClient; } } - - $this->file = $file; - $this->className = $className; } public function sendRequest(RequestInterface $request): ResponseInterface @@ -82,7 +71,7 @@ private function writeToPhpFile(string $fileName, string $className): void $code = 'exportValue((string) $httpRequest->getUri(), "\t\t\t\t") . ",\n"; $code .= "\t\t\t\t" . $this->exportValue($httpRequest->getHeaders(), "\t\t\t\t") . ",\n"; $code .= "\t\t\t\t" . $this->exportValue((string) $httpRequest->getBody(), "\t\t\t\t") . ",\n"; - $code .= "\t\t\t\t" . $this->exportValue($httpRequest->getProtocolVersion(), "\t\t\t\t") . "\n"; + $code .= "\t\t\t\t" . $this->exportValue($httpRequest->getProtocolVersion(), "\t\t\t\t") . ",\n"; $code .= "\t\t\t" . '),' . "\n"; $code .= "\t\t\t" . 'new HttpResponse(' . "\n"; $code .= "\t\t\t\t" . $this->exportValue($httpResponse->getStatusCode(), "\t\t\t\t") . ",\n"; $code .= "\t\t\t\t" . $this->exportValue($httpResponse->getHeaders(), "\t\t\t\t") . ",\n"; - $code .= "\t\t\t\t" . $this->exportValue((string) $httpResponse->getBody(), "\t\t\t\t") . "\n"; - $code .= "\t\t\t" . ')' . "\n"; + $code .= "\t\t\t\t" . $this->exportValue((string) $httpResponse->getBody(), "\t\t\t\t") . ",\n"; + $code .= "\t\t\t" . ')' . ",\n"; $code .= "\t\t" . ');' . "\n"; } @@ -123,10 +112,7 @@ private function writeToPhpFile(string $fileName, string $className): void file_put_contents($fileName, $code); } - /** - * @param mixed $value - */ - private function exportValue($value, string $indent = ''): string + private function exportValue(mixed $value, string $indent = ''): string { $s = Dumper::toPhp($value); $s = str_replace("\n", "\n" . $indent, $s); diff --git a/src/Fapi/HttpClient/CurlHttpClient.php b/src/Fapi/HttpClient/CurlHttpClient.php index 7129e93..1c9792b 100644 --- a/src/Fapi/HttpClient/CurlHttpClient.php +++ b/src/Fapi/HttpClient/CurlHttpClient.php @@ -3,6 +3,7 @@ namespace Fapi\HttpClient; use Composer\CaBundle\CaBundle; +use CurlHandle; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use function assert; @@ -93,12 +94,8 @@ public function sendRequest(RequestInterface $request): ResponseInterface return $httpResponse; } - /** - * @return resource - */ - private function initializeCurl(RequestInterface $httpRequest) + private function initializeCurl(RequestInterface $httpRequest): CurlHandle { - /** @var resource $handle */ $handle = curl_init(); curl_setopt_array($handle, [ @@ -112,10 +109,7 @@ private function initializeCurl(RequestInterface $httpRequest) return $handle; } - /** - * @param resource $handle - */ - private function processOptions(RequestInterface $request, $handle): void + private function processOptions(RequestInterface $request, CurlHandle $handle): void { if ($request->hasHeader('timeout')) { curl_setopt($handle, CURLOPT_TIMEOUT, (int) $request->getHeaderLine('timeout')); @@ -128,7 +122,7 @@ private function processOptions(RequestInterface $request, $handle): void if ($request->hasHeader('cert')) { curl_setopt($handle, CURLOPT_SSLCERT, $request->getHeader('cert')[0]); - if ($request->getHeader('cert')[1] ?? false) { + if ((bool) ($request->getHeader('cert')[1] ?? false)) { curl_setopt($handle, CURLOPT_SSLCERTPASSWD, $request->getHeader('cert')[1]); } } @@ -136,7 +130,7 @@ private function processOptions(RequestInterface $request, $handle): void if ($request->hasHeader('ssl_key')) { curl_setopt($handle, CURLOPT_SSLKEY, $request->getHeader('ssl_key')[0]); - if ($request->getHeader('ssl_key')[1] ?? false) { + if ((bool) ($request->getHeader('ssl_key')[1] ?? false)) { curl_setopt($handle, CURLOPT_SSLKEYPASSWD, $request->getHeader('ssl_key')[1]); } } @@ -144,10 +138,7 @@ private function processOptions(RequestInterface $request, $handle): void $this->processVerifyOption($request->getHeaderLine('verify'), $handle); } - /** - * @param resource $handle - */ - private function processVerifyOption(string $verify, $handle): void + private function processVerifyOption(string $verify, CurlHandle $handle): void { if ((bool) $verify) { $caPathOrFile = CaBundle::getSystemCaRootBundlePath(); @@ -165,10 +156,7 @@ private function processVerifyOption(string $verify, $handle): void curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false); } - /** - * @param resource $handle - */ - private function processHeaders(RequestInterface $request, $handle): RequestInterface + private function processHeaders(RequestInterface $request, CurlHandle $handle): RequestInterface { $request = $request->withoutHeader('timeout') ->withoutHeader('connect_timeout') @@ -213,7 +201,7 @@ private function parseHeaders(string $header): array $line = trim($line); preg_match('#^([A-Za-z\-]+): (.*)\z#', $line, $match); - if (!$match) { + if (!(bool) $match) { continue; } diff --git a/src/Fapi/HttpClient/GuzzleHttpClient.php b/src/Fapi/HttpClient/GuzzleHttpClient.php index c8a827d..62f1dde 100644 --- a/src/Fapi/HttpClient/GuzzleHttpClient.php +++ b/src/Fapi/HttpClient/GuzzleHttpClient.php @@ -19,8 +19,7 @@ class GuzzleHttpClient implements IHttpClient { - /** @var Client */ - private $client; + private Client $client; public function __construct() { @@ -48,7 +47,7 @@ public function sendRequest(RequestInterface $request): ResponseInterface $response->getHeaders(), $response->getBody(), $response->getProtocolVersion(), - $response->getReasonPhrase() + $response->getReasonPhrase(), ); } catch (TransferException $e) { if ($this->isTimeoutException($e)) { @@ -96,11 +95,13 @@ private function processOptions(RequestInterface $request): array $options = []; if ($request->hasHeader('timeout')) { - $options['timeout'] = (int) ($request->getHeaderLine('timeout') ?? 5); + $headerLine = $request->getHeaderLine('timeout'); + $options['timeout'] = (int) ($headerLine !== '' ? $headerLine : 5); } if ($request->hasHeader('connect_timeout')) { - $options['connect_timeout'] = (int) ($request->getHeaderLine('connect_timeout') ?? 5); + $headerLine = $request->getHeaderLine('connect_timeout'); + $options['connect_timeout'] = (int) ($headerLine !== '' ? $headerLine : 5); } if ($request->hasHeader('verify') && !(bool) $request->getHeaderLine('verify')) { diff --git a/src/Fapi/HttpClient/HttpRequest.php b/src/Fapi/HttpClient/HttpRequest.php index b44d75e..810116c 100644 --- a/src/Fapi/HttpClient/HttpRequest.php +++ b/src/Fapi/HttpClient/HttpRequest.php @@ -22,14 +22,18 @@ class HttpRequest extends Request { /** @var array */ - private static $defaults = ['verify' => true]; + private static array $defaults = ['verify' => true]; /** - * @param UriInterface|string $uri * @param array $headers - * @param StreamInterface|string|null $body */ - public function __construct(string $method, $uri, array $headers = [], $body = null, string $version = '1.1') + public function __construct( + string $method, + UriInterface|string $uri, + array $headers = [], + StreamInterface|string|null $body = null, + string $version = '1.1', + ) { if (!HttpMethod::isValid($method)) { throw new InvalidArgumentException('Parameter method must be an HTTP method.'); @@ -39,95 +43,90 @@ public function __construct(string $method, $uri, array $headers = [], $body = n } /** - * @param UriInterface|string $uri * @param array $options */ - public static function from($uri, string $method = HttpMethod::GET, array $options = []): HttpRequest + public static function from(UriInterface|string $uri, string $method = HttpMethod::GET, array $options = []): self { - $body = null; - $options = static::preProcessHeaders($options, $body); + $body = ''; + $options = self::preProcessHeaders($options, $body); return new self($method, $uri, $options, $body); } /** * @param array $options - * @param StreamInterface|string $body * @return array */ - private static function preProcessHeaders(array $options, &$body): array + private static function preProcessHeaders(array $options, StreamInterface|string &$body): array { $data = self::$defaults; if (isset($options['form_params'])) { $value = $options['form_params']; - static::validateFormParamsOption($value); + self::validateFormParamsOption($value); $body = http_build_query($value, '', '&'); $data['Content-Type'] = 'application/x-www-form-urlencoded'; } if (isset($options['headers'])) { $value = $options['headers']; - static::validateHeadersOption($value); + self::validateHeadersOption($value); $data += $value; } if (isset($options['auth'])) { $value = $options['auth']; - static::validateAuthOption($value); + self::validateAuthOption($value); $data['Authorization'] = 'Basic ' . base64_encode($value[0] . ':' . $value[1]); } if (isset($options['body'])) { $value = $options['body']; - static::validateBodyOption($value); + self::validateBodyOption($value); $body = $value; } if (isset($options['json'])) { $value = $options['json']; - static::validateJsonOption($value); + self::validateJsonOption($value); $body = Json::encode($value); $data['Content-Type'] = 'application/json'; } if (isset($options['timeout'])) { $value = $options['timeout']; - static::validateTimeoutOption($value); + self::validateTimeoutOption($value); $data['timeout'] = $value; } if (isset($options['connect_timeout'])) { $value = $options['connect_timeout']; - static::validateConnectTimeoutOption($value); + self::validateConnectTimeoutOption($value); $data['connect_timeout'] = $value; } if (isset($options['verify'])) { $value = $options['verify']; - static::validateVerify($value); + self::validateVerify($value); $data['verify'] = (bool) $value; } if (isset($options['cert'])) { $value = $options['cert']; - static::validateCertOption($value); + self::validateCertOption($value); $data['cert'] = $value; } if (isset($options['ssl_key'])) { $value = $options['ssl_key']; - static::validateSslKeyOption($value); + self::validateSslKeyOption($value); $data['ssl_key'] = $value; } return $data; } - /** - * @param mixed $formParams - */ - private static function validateFormParamsOption($formParams): void + private static function validateFormParamsOption(mixed $formParams): void { if (!is_array($formParams)) { throw new InvalidArgumentException('Form params must be an array.'); @@ -140,10 +139,7 @@ private static function validateFormParamsOption($formParams): void } } - /** - * @param mixed $headers - */ - private static function validateHeadersOption($headers): void + private static function validateHeadersOption(mixed $headers): void { if (!is_array($headers)) { throw new InvalidArgumentException('Headers must be an array.'); @@ -162,10 +158,7 @@ private static function validateHeadersOption($headers): void } } - /** - * @param mixed $auth - */ - private static function validateAuthOption($auth): void + private static function validateAuthOption(mixed $auth): void { if (!is_array($auth)) { throw new InvalidArgumentException('Parameter auth must be an array.'); @@ -173,7 +166,7 @@ private static function validateAuthOption($auth): void if (count($auth) !== 2 || !isset($auth[0], $auth[1])) { throw new InvalidArgumentException( - 'Parameter auth must be an array of two elements (username and password).' + 'Parameter auth must be an array of two elements (username and password).', ); } @@ -186,20 +179,14 @@ private static function validateAuthOption($auth): void } } - /** - * @param mixed $body - */ - private static function validateBodyOption($body): void + private static function validateBodyOption(mixed $body): void { if (!is_string($body)) { throw new InvalidArgumentException('Body must be a string.'); } } - /** - * @param mixed $json - */ - private static function validateJsonOption($json): void + private static function validateJsonOption(mixed $json): void { try { Json::encode($json); @@ -208,40 +195,28 @@ private static function validateJsonOption($json): void } } - /** - * @param mixed $timeout - */ - private static function validateTimeoutOption($timeout): void + private static function validateTimeoutOption(mixed $timeout): void { if ($timeout !== null && !is_int($timeout)) { throw new InvalidArgumentException('Option timeout must be an integer or null.'); } } - /** - * @param mixed $connectTimeout - */ - private static function validateConnectTimeoutOption($connectTimeout): void + private static function validateConnectTimeoutOption(mixed $connectTimeout): void { if ($connectTimeout !== null && !is_int($connectTimeout)) { throw new InvalidArgumentException('Option connectTimeout must be an integer or null.'); } } - /** - * @param mixed $verify - */ - private static function validateVerify($verify): void + private static function validateVerify(mixed $verify): void { if (!is_bool($verify)) { throw new InvalidArgumentException('Option verify must be an bool.'); } } - /** - * @param mixed $value - */ - private static function validateCertOption($value): void + private static function validateCertOption(mixed $value): void { if (!is_string($value) && !is_array($value)) { throw new InvalidArgumentException('Option cert must be a string.'); @@ -251,14 +226,16 @@ private static function validateCertOption($value): void if (!isset($value[0]) || !isset($value[1])) { throw new InvalidArgumentException( 'Option cert must be an array of two elements (cert and key). Provided array: ' . json_encode( - $value - ) + $value, + ), ); } if (!is_string($value[0]) || !is_string($value[1])) { throw new InvalidArgumentException( - 'Option cert must be an array of two strings (cert and key). Provided array: ' . json_encode($value) + 'Option cert must be an array of two strings (cert and key). Provided array: ' . json_encode( + $value, + ), ); } @@ -274,10 +251,7 @@ private static function validateCertOption($value): void } } - /** - * @param mixed $value - */ - private static function validateSslKeyOption($value): void + private static function validateSslKeyOption(mixed $value): void { if (!is_string($value) && !is_array($value)) { throw new InvalidArgumentException('Option ssl_key must be a string.'); @@ -287,16 +261,16 @@ private static function validateSslKeyOption($value): void if (!isset($value[0]) || !isset($value[1])) { throw new InvalidArgumentException( 'Option ssl_key must be an array of two elements (key and password). Provided array: ' . json_encode( - $value - ) . '.' + $value, + ) . '.', ); } if (!is_string($value[0]) || !is_string($value[1])) { throw new InvalidArgumentException( 'Option ssl_key must be an array of two strings (key and password). Provided array: ' . json_encode( - $value - ) . '.' + $value, + ) . '.', ); } diff --git a/src/Fapi/HttpClient/HttpResponse.php b/src/Fapi/HttpClient/HttpResponse.php index a836df9..5fcc901 100644 --- a/src/Fapi/HttpClient/HttpResponse.php +++ b/src/Fapi/HttpClient/HttpResponse.php @@ -12,21 +12,20 @@ class HttpResponse extends Response /** * @param array $headers - * @param StreamInterface|string|null $body */ public function __construct( int $status = 200, array $headers = [], - $body = null, + StreamInterface|string|null $body = null, string $version = '1.1', - ?string $reason = null + string|null $reason = null, ) { if (!HttpStatusCode::isValid($status)) { throw new InvalidArgumentException('Parameter statusCode must be an HTTP status code.'); } - static::validateHeaders($headers); + self::validateHeaders($headers); parent::__construct($status, $headers, $body, $version, $reason); } diff --git a/src/Fapi/HttpClient/ILoggingFormatter.php b/src/Fapi/HttpClient/ILoggingFormatter.php index c5bab9c..aadee73 100644 --- a/src/Fapi/HttpClient/ILoggingFormatter.php +++ b/src/Fapi/HttpClient/ILoggingFormatter.php @@ -12,7 +12,7 @@ interface ILoggingFormatter public function formatSuccessful( RequestInterface $request, ResponseInterface $response, - float $elapsedTime + float $elapsedTime, ): string; public function formatFailed(RequestInterface $request, Throwable $exception, float $elapsedTime): string; diff --git a/src/Fapi/HttpClient/LoggingFormatters/BaseLoggingFormatter.php b/src/Fapi/HttpClient/LoggingFormatters/BaseLoggingFormatter.php new file mode 100644 index 0000000..1f5d6dc --- /dev/null +++ b/src/Fapi/HttpClient/LoggingFormatters/BaseLoggingFormatter.php @@ -0,0 +1,112 @@ +dumpHttpRequest($request) + . $this->dumpHttpResponse($response) + . $this->dumpElapsedTime($elapsedTime); + } + + public function formatFailed(RequestInterface $request, Throwable $exception, float $elapsedTime): string + { + return 'Fapi\HttpClient: an HTTP request failed.' + . $this->dumpHttpRequest($request) + . $this->dumpException($exception) + . $this->dumpElapsedTime($elapsedTime); + } + + private function dumpHttpRequest(RequestInterface $request): string + { + $body = $this->processBody((string) $request->getBody()); + + return ' Request URL: ' . $this->dumpValue((string) $request->getUri()) + . ' Request method: ' . $this->dumpValue($request->getMethod()) + . ' Request headers: ' . $this->dumpValue($request->getHeaders()) + . ' Request body: ' . $this->dumpValue($body); + } + + private function dumpHttpResponse(ResponseInterface $response): string + { + $body = $this->processBody((string) $response->getBody()); + + return ' Response status code: ' . $this->dumpValue($response->getStatusCode()) + . ' Response headers: ' . $this->dumpValue($response->getHeaders()) + . ' Response body: ' . $this->dumpValue($body); + } + + private function dumpException(Throwable $exception): string + { + $dump = ' Exception type: ' . $this->dumpValue($exception::class) + . ' Exception message: ' . $this->dumpValue($exception->getMessage()); + + if ($exception->getPrevious() !== null) { + $previousException = $exception->getPrevious(); + + $dump .= ' Previous exception type: ' . $this->dumpValue($previousException::class) + . ' Previous exception message: ' . $this->dumpValue($previousException->getMessage()); + } + + return $dump; + } + + private function dumpElapsedTime(float $elapsedTime): string + { + $elapsedTime *= 1000; + + return ' Elapsed time: ' . sprintf('%0.2f', $elapsedTime) . ' ms'; + } + + private function dumpValue(mixed $value): string + { + try { + return Json::encode($value, JSON_UNESCAPED_UNICODE); + } catch (JsonException) { + return '(serialized) ' . base64_encode(serialize($value)); + } + } + + private function processBody(string $body): string + { + if (strlen($body) <= $this->maxBodyLength) { + return $body; + } + + if (function_exists('mb_substr')) { + return mb_substr($body, 0, $this->maxBodyLength, 'UTF-8'); + } + + if (!extension_loaded('iconv')) { + return (string) iconv_substr($body, 0, $this->maxBodyLength, 'UTF-8'); + } + + return substr($body, 0, $this->maxBodyLength); + } + +} diff --git a/src/Fapi/HttpClient/LoggingFormatters/JsonLoggingFormatter.php b/src/Fapi/HttpClient/LoggingFormatters/JsonLoggingFormatter.php new file mode 100644 index 0000000..a68334d --- /dev/null +++ b/src/Fapi/HttpClient/LoggingFormatters/JsonLoggingFormatter.php @@ -0,0 +1,91 @@ + 'Fapi\HttpClient', + 'description' => 'an HTTP request has been sent.', + 'request' => [ + 'url' => (string) $request->getUri(), + 'method' => $request->getMethod(), + 'headers' => $request->getHeaders(), + 'body' => $this->processBody((string) $request->getBody()), + ], + 'response' => [ + 'statusCode' => $response->getStatusCode(), + 'headers' => $response->getHeaders(), + 'body' => $this->processBody((string) $response->getBody()), + ], + 'elapsedTime' => $this->formatElapsedTime($elapsedTime), + ]); + } + + public function formatFailed(RequestInterface $request, Throwable $exception, float $elapsedTime): string + { + return Json::encode([ + 'class' => 'Fapi\HttpClient', + 'description' => 'an HTTP request failed.', + 'request' => [ + 'url' => (string) $request->getUri(), + 'method' => $request->getMethod(), + 'headers' => $request->getHeaders(), + 'body' => $this->processBody((string) $request->getBody()), + ], + 'exception' => [ + 'type' => $exception::class, + 'message' => $exception->getMessage(), + 'previous' => $exception->getPrevious() !== null ? [ + 'type' => get_class($exception->getPrevious()), + 'message' => $exception->getPrevious()->getMessage(), + ] : null, + ], + 'elapsedTime' => $this->formatElapsedTime($elapsedTime), + ]); + } + + private function processBody(string $body): string + { + if (strlen($body) <= $this->maxBodyLength) { + return $body; + } + + if (function_exists('mb_substr')) { + return mb_substr($body, 0, $this->maxBodyLength, 'UTF-8'); + } + + if (!extension_loaded('iconv')) { + return (string) iconv_substr($body, 0, $this->maxBodyLength, 'UTF-8'); + } + + return substr($body, 0, $this->maxBodyLength); + } + + private function formatElapsedTime(float $elapsedTime): string + { + return sprintf('%0.2f', $elapsedTime * 1000); + } + +} diff --git a/src/Fapi/HttpClient/LoggingHttpClient.php b/src/Fapi/HttpClient/LoggingHttpClient.php index ee7121c..0154c05 100644 --- a/src/Fapi/HttpClient/LoggingHttpClient.php +++ b/src/Fapi/HttpClient/LoggingHttpClient.php @@ -11,20 +11,14 @@ class LoggingHttpClient implements IHttpClient { - /** @var IHttpClient */ - private $httpClient; + private ILoggingFormatter $formatter; - /** @var LoggerInterface */ - private $logger; - - /** @var ILoggingFormatter */ - private $formatter; - - public function __construct(IHttpClient $httpClient, LoggerInterface $logger, ?ILoggingFormatter $formatter = null) + public function __construct( + private IHttpClient $httpClient, + private LoggerInterface $logger, + ILoggingFormatter|null $formatter = null, + ) { - $this->httpClient = $httpClient; - $this->logger = $logger; - if ($formatter === null) { $formatter = new BaseLoggingFormatter(); } @@ -52,24 +46,24 @@ public function sendRequest(RequestInterface $request): ResponseInterface private function logSuccessfulRequest( RequestInterface $request, ResponseInterface $response, - float $elapsedTime + float $elapsedTime, ): void { $this->log( $this->formatter->formatSuccessful($request, $response, $elapsedTime), - LogLevel::INFO + LogLevel::INFO, ); } private function logFailedRequest( RequestInterface $request, HttpClientException $exception, - float $elapsedTime + float $elapsedTime, ): void { $this->log( $this->formatter->formatFailed($request, $exception, $elapsedTime), - LogLevel::WARNING + LogLevel::WARNING, ); } diff --git a/src/Fapi/HttpClient/MockHttpClient.php b/src/Fapi/HttpClient/MockHttpClient.php index 1e502d1..51dc7ad 100644 --- a/src/Fapi/HttpClient/MockHttpClient.php +++ b/src/Fapi/HttpClient/MockHttpClient.php @@ -15,10 +15,10 @@ class MockHttpClient implements IHttpClient { /** @var array */ - private $requests = []; + private array $requests = []; /** @var array */ - private $responses = []; + private array $responses = []; public function add(RequestInterface $request, ResponseInterface $response): void { @@ -61,7 +61,7 @@ private function assertHttpRequestUrl(RequestInterface $expected, RequestInterfa throw new InvalidArgumentException( 'Invalid HTTP request. Url not matched. Expected "' - . $expectedUrl . '" got "' . $actualUrl . '".' + . $expectedUrl . '" got "' . $actualUrl . '".', ); } @@ -73,7 +73,7 @@ private function assertHttpRequestMethod(RequestInterface $expected, RequestInte throw new InvalidArgumentException( 'Invalid HTTP request. Method not matched. Expected "' - . $expected->getMethod() . '" got "' . $actual->getMethod() . '".' + . $expected->getMethod() . '" got "' . $actual->getMethod() . '".', ); } @@ -88,7 +88,7 @@ private function assertHttpRequestOptions(RequestInterface $expected, RequestInt . Json::encode($expected->getHeaders()) . '", got: "' . Json::encode($actual->getHeaders()) - . '".' + . '".', ); } @@ -103,7 +103,7 @@ private function assertHttpRequestBody(RequestInterface $expected, RequestInterf . $expected->getBody() . '", got: "' . $actual->getBody() - . '".' + . '".', ); } diff --git a/src/Fapi/HttpClient/RedirectHelper.php b/src/Fapi/HttpClient/RedirectHelper.php index 8dc3dfb..e0ff863 100644 --- a/src/Fapi/HttpClient/RedirectHelper.php +++ b/src/Fapi/HttpClient/RedirectHelper.php @@ -19,11 +19,11 @@ public static function followRedirects( IHttpClient $httpClient, ResponseInterface $response, RequestInterface $request, - int $limit = 5 + int $limit = 5, ): ResponseInterface { for ($count = 0; $count < $limit; $count++) { - $redirectUrl = static::getRedirectUrl($response, $request->getUri()); + $redirectUrl = self::getRedirectUrl($response, $request->getUri()); if ($redirectUrl === null) { return $response; @@ -36,13 +36,13 @@ public static function followRedirects( throw new TooManyRedirectsException('Maximum number of redirections exceeded.'); } - private static function getRedirectUrl(ResponseInterface $httpResponse, UriInterface $requestUri): ?string + private static function getRedirectUrl(ResponseInterface $httpResponse, UriInterface $requestUri): string|null { - if (!static::isRedirectionStatusCode($httpResponse->getStatusCode())) { + if (!self::isRedirectionStatusCode($httpResponse->getStatusCode())) { return null; } - $url = static::getLocationHeader($httpResponse->getHeaders()); + $url = self::getLocationHeader($httpResponse->getHeaders()); if ($url === null) { return null; @@ -52,7 +52,7 @@ private static function getRedirectUrl(ResponseInterface $httpResponse, UriInter $url = $requestUri->getScheme() . '://' . $requestUri->getHost() . $url; } - if (!static::isValidRedirectUrl($url)) { + if (!self::isValidRedirectUrl($url)) { return null; } @@ -67,7 +67,7 @@ private static function isRedirectionStatusCode(int $code): bool /** * @param array> $headers */ - private static function getLocationHeader(array $headers): ?string + private static function getLocationHeader(array $headers): string|null { $headers = array_change_key_case($headers, CASE_LOWER); diff --git a/src/Fapi/HttpClient/Rest/RestClient.php b/src/Fapi/HttpClient/Rest/RestClient.php index 5feb6e8..203e1ad 100644 --- a/src/Fapi/HttpClient/Rest/RestClient.php +++ b/src/Fapi/HttpClient/Rest/RestClient.php @@ -19,24 +19,16 @@ class RestClient { - /** @var string */ - private $username; - - /** @var string */ - private $password; - - /** @var string */ - private $apiUrl; - - /** @var IHttpClient */ - private $httpClient; - - public function __construct(string $username, string $password, string $apiUrl, IHttpClient $httpClient) + private string $apiUrl; + + public function __construct( + private string $username, + private string $password, + string $apiUrl, + private IHttpClient $httpClient, + ) { - $this->username = $username; - $this->password = $password; $this->apiUrl = rtrim($apiUrl, '/'); - $this->httpClient = $httpClient; } /** @@ -56,7 +48,7 @@ public function getResources(string $path, array $parameters = []): array } throw new InvalidStatusCodeException( - 'Expected return status code ' . HttpStatusCode::S200_OK . ', got ' . $httpResponse->getStatusCode() . '.' + 'Expected return status code ' . HttpStatusCode::S200_OK . ', got ' . $httpResponse->getStatusCode() . '.', ); } @@ -64,7 +56,7 @@ public function getResources(string $path, array $parameters = []): array * @param array $parameters * @return array|null */ - public function getResource(string $path, int $id, array $parameters = []): ?array + public function getResource(string $path, int $id, array $parameters = []): array|null { $path .= '/' . $id; @@ -83,7 +75,7 @@ public function getResource(string $path, int $id, array $parameters = []): ?arr } throw new InvalidStatusCodeException( - 'Expected return status code ' . HttpStatusCode::S200_OK . ', got ' . $httpResponse->getStatusCode() . '.' + 'Expected return status code ' . HttpStatusCode::S200_OK . ', got ' . $httpResponse->getStatusCode() . '.', ); } @@ -104,7 +96,7 @@ public function getSingularResource(string $path, array $parameters = []): array } throw new InvalidStatusCodeException( - 'Expected return status code ' . HttpStatusCode::S200_OK . ', got ' . $httpResponse->getStatusCode() . '.' + 'Expected return status code ' . HttpStatusCode::S200_OK . ', got ' . $httpResponse->getStatusCode() . '.', ); } @@ -121,7 +113,7 @@ public function createResource(string $path, array $data): array } throw new InvalidStatusCodeException( - 'Expected return status code ' . HttpStatusCode::S201_CREATED . ', got ' . $httpResponse->getStatusCode() . '.' + 'Expected return status code ' . HttpStatusCode::S201_CREATED . ', got ' . $httpResponse->getStatusCode() . '.', ); } @@ -138,7 +130,7 @@ public function updateResource(string $path, int $id, array $data): array } throw new InvalidStatusCodeException( - 'Expected return status code ' . HttpStatusCode::S200_OK . ', got ' . $httpResponse->getStatusCode() . '.' + 'Expected return status code ' . HttpStatusCode::S200_OK . ', got ' . $httpResponse->getStatusCode() . '.', ); } @@ -149,7 +141,7 @@ public function deleteResource(string $path, int $id): void $validStatusCode = !in_array( $httpResponse->getStatusCode(), [HttpStatusCode::S200_OK, HttpStatusCode::S204_NO_CONTENT], - true + true, ); if ($validStatusCode) { @@ -157,7 +149,7 @@ public function deleteResource(string $path, int $id): void 'Expected return status code [' . implode(', ', [ HttpStatusCode::S200_OK, HttpStatusCode::S204_NO_CONTENT, - ]) . '], got ' . $httpResponse->getStatusCode() . '.' + ]) . '], got ' . $httpResponse->getStatusCode() . '.', ); } } @@ -165,7 +157,7 @@ public function deleteResource(string $path, int $id): void /** * @param array|null $data */ - private function sendHttpRequest(string $method, string $path, ?array $data = null): ResponseInterface + private function sendHttpRequest(string $method, string $path, array|null $data = null): ResponseInterface { $url = $this->apiUrl . $path; @@ -228,10 +220,7 @@ private function getArrayResponseData(ResponseInterface $httpResponse): array return $responseData; } - /** - * @return mixed - */ - private function getResponseData(ResponseInterface $httpResponse) + private function getResponseData(ResponseInterface $httpResponse): mixed { try { return Json::decode((string) $httpResponse->getBody(), Json::FORCE_ARRAY); diff --git a/src/Fapi/HttpClient/Utils/Callback.php b/src/Fapi/HttpClient/Utils/Callback.php index 4a6c1b3..ad1e584 100644 --- a/src/Fapi/HttpClient/Utils/Callback.php +++ b/src/Fapi/HttpClient/Utils/Callback.php @@ -21,10 +21,9 @@ class Callback * * @param array $args * @param callable $onError function($message, $severity) - * @return mixed * @throws Throwable */ - public static function invokeSafe(callable $function, array $args, callable $onError) + public static function invokeSafe(callable $function, array $args, callable $onError): mixed { /** @noinspection PhpUnusedLocalVariableInspection */ $prev = set_error_handler(static function ($severity, $message, $file) use ($onError, &$prev) { diff --git a/src/Fapi/HttpClient/Utils/Json.php b/src/Fapi/HttpClient/Utils/Json.php index 54573f6..55c36ea 100644 --- a/src/Fapi/HttpClient/Utils/Json.php +++ b/src/Fapi/HttpClient/Utils/Json.php @@ -35,7 +35,7 @@ class Json public const PRETTY = 2; /** @var array */ - private static $messages = [ + private static array $messages = [ JSON_ERROR_DEPTH => 'The maximum stack depth has been exceeded', JSON_ERROR_STATE_MISMATCH => 'Syntax error, malformed JSON', JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', @@ -46,11 +46,10 @@ class Json /** * Returns the JSON representation of a value. * - * @param mixed $value * @throws JsonException * @throws Throwable */ - public static function encode($value, int $options = 0): string + public static function encode(mixed $value, int $options = 0): string { $flags = PHP_VERSION_ID >= 50400 ? (JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | @@ -69,7 +68,7 @@ public static function encode($value, int $options = 0): string static function ($message): void { // needed to receive 'recursion detected' error throw new JsonException($message); - } + }, ); } else { $json = json_encode($value, $flags); @@ -78,7 +77,7 @@ static function ($message): void { $error = json_last_error(); if ((bool) $error) { - $message = static::$messages[$error] ?? (PHP_VERSION_ID >= 50500 ? json_last_error_msg() : 'Unknown error'); + $message = self::$messages[$error] ?? (PHP_VERSION_ID >= 50500 ? json_last_error_msg() : 'Unknown error'); throw new JsonException($message, $error); } @@ -91,10 +90,9 @@ static function ($message): void { /** * Decodes a JSON string. * - * @return mixed * @throws JsonException */ - public static function decode(string $json, int $options = 0) + public static function decode(string $json, int $options = 0): mixed { if (!(bool) preg_match('##u', $json)) { throw new JsonException('Invalid UTF-8 sequence', 5); // workaround for PHP < 5.3.3 & PECL JSON-C @@ -106,7 +104,7 @@ public static function decode(string $json, int $options = 0) !$forceArray && (bool) preg_match('#(?<=[^\\\\]")\\\\u0000(?:[^"\\\\]|\\\\.)*+"\s*+:#', $json) ) { // workaround for json_decode fatal error when object key starts with \u0000 - throw new JsonException(static::$messages[JSON_ERROR_CTRL_CHAR]); + throw new JsonException(self::$messages[JSON_ERROR_CTRL_CHAR]); } $args = [$json, $forceArray, 512]; @@ -130,7 +128,7 @@ public static function decode(string $json, int $options = 0) ) { // '' is not clearing json_last_error $error = json_last_error(); - throw new JsonException(static::$messages[$error] ?? 'Unknown error', $error); + throw new JsonException(self::$messages[$error] ?? 'Unknown error', $error); } return $value; diff --git a/tests/Fapi/HttpClientTests/BaseHttpClient.php b/tests/Fapi/HttpClientTests/BaseHttpClient.php index 40ea74e..200b479 100644 --- a/tests/Fapi/HttpClientTests/BaseHttpClient.php +++ b/tests/Fapi/HttpClientTests/BaseHttpClient.php @@ -16,8 +16,7 @@ abstract class BaseHttpClient extends TestCase { - /** @var IHttpClient */ - protected $httpClient; + protected IHttpClient $httpClient; protected function setUp(): void { @@ -29,8 +28,9 @@ protected function setUp(): void abstract protected function createHttpClient(): IHttpClient; /** - * @dataProvider getSampleHttpRequests * @param array $options + * + * @dataProvider getSampleHttpRequests */ public function testSendHttpRequest(string $url, string $method, array $options, string $expectedBody): void { @@ -194,7 +194,7 @@ public function testVerify(): void 'auth' => ['admin', 'xxx'], 'body' => '{"foo":"bar"}', 'verify' => false, - ] + ], ); $httpResponse = $httpClient->sendRequest($httpRequest); diff --git a/tests/Fapi/HttpClientTests/BaseLoggingFormatterTest.phpt b/tests/Fapi/HttpClientTests/BaseLoggingFormatterTest.phpt index 5839979..1c8f2b6 100644 --- a/tests/Fapi/HttpClientTests/BaseLoggingFormatterTest.phpt +++ b/tests/Fapi/HttpClientTests/BaseLoggingFormatterTest.phpt @@ -23,12 +23,13 @@ final class BaseLoggingFormatterTest extends TestCase $formatted = $formatter->formatFailed( new HttpRequest('GET', 'test.cz'), new Exception('test'), - 0.10013794898987 + 0.10013794898987, ); Assert::equal( - 'Fapi\HttpClient: an HTTP request failed. Request URL: "test.cz" Request method: "GET" Request headers: [] Request body: "" Exception type: "Exception" Exception message: "test" Elapsed time: 100.14 ms', - $formatted + 'Fapi\HttpClient: an HTTP request failed. Request URL: "test.cz" Request method: "GET" Request headers: [] ' + . 'Request body: "" Exception type: "Exception" Exception message: "test" Elapsed time: 100.14 ms', + $formatted, ); } @@ -39,12 +40,14 @@ final class BaseLoggingFormatterTest extends TestCase $formatted = $formatter->formatSuccessful( new HttpRequest('GET', 'test.cz'), new HttpResponse(200, [], '{"test": "test"}'), - 0.10113794898987 + 0.10113794898987, ); Assert::equal( - 'Fapi\HttpClient: an HTTP request has been sent. Request URL: "test.cz" Request method: "GET" Request headers: [] Request body: "" Response status code: 200 Response headers: [] Response body: "{\"test\": \"test\"}" Elapsed time: 101.14 ms', - $formatted + 'Fapi\HttpClient: an HTTP request has been sent. Request URL: "test.cz" Request method: "GET" ' + . 'Request headers: [] Request body: "" Response status code: 200 Response headers: [] ' + . 'Response body: "{\"test\": \"test\"}" Elapsed time: 101.14 ms', + $formatted, ); } @@ -56,7 +59,7 @@ final class BaseLoggingFormatterTest extends TestCase $formatted = $formatter->formatSuccessful( new HttpRequest('GET', 'test.cz'), new HttpResponse(200, [], $body), - 0.10113794898987 + 0.10113794898987, ); Assert::same(40218, strlen($formatted)); diff --git a/tests/Fapi/HttpClientTests/CapturingHttpClientTest.phpt b/tests/Fapi/HttpClientTests/CapturingHttpClientTest.phpt index a0a8dec..51b00e2 100644 --- a/tests/Fapi/HttpClientTests/CapturingHttpClientTest.phpt +++ b/tests/Fapi/HttpClientTests/CapturingHttpClientTest.phpt @@ -20,20 +20,17 @@ use Tester\Assert; use Tester\FileMock; use Tester\TestCase; use function file_get_contents; -use function get_class; require __DIR__ . '/../../bootstrap.php'; class CapturingHttpClientTest extends TestCase { - /** @var string */ - private $file; + private string $file; public function setUp(): void { $this->file = __DIR__ . '/MockHttpClients/SampleMockHttpClient2.php'; - } public function testWriteToMockPhpFile(): void @@ -45,7 +42,7 @@ class CapturingHttpClientTest extends TestCase 'headers' => [ 'User-Agent' => 'Nette Tester', ], - ] + ], ); $mockHttpResponse = new HttpResponse( @@ -55,7 +52,7 @@ class CapturingHttpClientTest extends TestCase 'text/plain', ], ], - "It works!\n" + "It works!\n", ); $mockHttpClient = new MockHttpClient(); @@ -65,7 +62,7 @@ class CapturingHttpClientTest extends TestCase $capturingHttpClient = new CapturingHttpClient( $mockHttpClient, $fileName, - 'Fapi\\HttpClientTests\\MockHttpClients\\SampleMockHttpClient' + 'Fapi\\HttpClientTests\\MockHttpClients\\SampleMockHttpClient', ); $capturingHttpClient->sendRequest($mockHttpRequest); @@ -85,7 +82,7 @@ class CapturingHttpClientTest extends TestCase 'headers' => [ 'User-Agent' => 'Nette Tester', ], - ] + ], ); $mockHttpResponse = new HttpResponse( @@ -95,7 +92,7 @@ class CapturingHttpClientTest extends TestCase 'text/plain', ], ], - "It works!\n" + "It works!\n", ); $mockHttpClient = new MockHttpClient(); @@ -104,13 +101,13 @@ class CapturingHttpClientTest extends TestCase $capturingHttpClient = new CapturingHttpClient( $mockHttpClient, $this->file, - 'Fapi\\HttpClientTests\\MockHttpClients\\SampleMockHttpClient2' + 'Fapi\\HttpClientTests\\MockHttpClients\\SampleMockHttpClient2', ); $capturingHttpClient->sendRequest($mockHttpRequest); $capturingHttpClient->close(); - $reflectionClass = new ReflectionClass(get_class($capturingHttpClient)); + $reflectionClass = new ReflectionClass($capturingHttpClient::class); $reflectionProperty = $reflectionClass->getProperty('httpClient'); $reflectionProperty->setAccessible(true); $httpClient = $reflectionProperty->getValue($capturingHttpClient); @@ -120,13 +117,13 @@ class CapturingHttpClientTest extends TestCase $capturingHttpClient = new CapturingHttpClient( $mockHttpClient, $this->file, - 'Fapi\\HttpClientTests\\MockHttpClients\\SampleMockHttpClient2' + 'Fapi\\HttpClientTests\\MockHttpClients\\SampleMockHttpClient2', ); $capturingHttpClient->sendRequest($mockHttpRequest); $capturingHttpClient->close(); - $reflectionClass = new ReflectionClass(get_class($capturingHttpClient)); + $reflectionClass = new ReflectionClass($capturingHttpClient::class); $reflectionProperty = $reflectionClass->getProperty('httpClient'); $reflectionProperty->setAccessible(true); $httpClient = $reflectionProperty->getValue($capturingHttpClient); diff --git a/tests/Fapi/HttpClientTests/HttpRequestTest.phpt b/tests/Fapi/HttpClientTests/HttpRequestTest.phpt index d04b039..6f4a8b2 100644 --- a/tests/Fapi/HttpClientTests/HttpRequestTest.phpt +++ b/tests/Fapi/HttpClientTests/HttpRequestTest.phpt @@ -28,8 +28,9 @@ final class HttpRequestTest extends TestCase } /** - * @dataProvider getValidHeadersData * @param array $headers + * + * @dataProvider getValidHeadersData */ public function testValidHeaders(array $headers = []): void { @@ -39,8 +40,9 @@ final class HttpRequestTest extends TestCase } /** - * @dataProvider getInvalidHeadersData * @param array $headers + * + * @dataProvider getInvalidHeadersData */ public function testInvalidHeaders(array $headers = []): void { diff --git a/tests/Fapi/HttpClientTests/LoggingHttpClientTest.phpt b/tests/Fapi/HttpClientTests/LoggingHttpClientTest.phpt index 76b2d3f..8fbdde5 100644 --- a/tests/Fapi/HttpClientTests/LoggingHttpClientTest.phpt +++ b/tests/Fapi/HttpClientTests/LoggingHttpClientTest.phpt @@ -26,13 +26,13 @@ final class LoggingHttpClientTest extends TestCase Debugger::enable(false, __DIR__); $loggingClient = new LoggingHttpClient( $mockClient, - new TracyToPsrLogger(Debugger::getLogger()) + new TracyToPsrLogger(Debugger::getLogger()), ); $loggingClient->sendRequest(new HttpRequest( 'GET', 'http://localhost/1', - ['Host' => ['localhost'], 'User-Agent' => ['Nette Tester']] + ['Host' => ['localhost'], 'User-Agent' => ['Nette Tester']], )); Assert::true(is_file(__DIR__ . '/info.log')); @@ -45,13 +45,13 @@ final class LoggingHttpClientTest extends TestCase new HttpRequest( 'GET', 'http://localhost/1', - ['Host' => ['localhost'], 'User-Agent' => ['Nette Tester']] + ['Host' => ['localhost'], 'User-Agent' => ['Nette Tester']], ), new HttpResponse( 200, ['Content-Type' => ['text/plain']], - "It works!\n" - ) + "It works!\n", + ), ); return $mockClient; diff --git a/tests/Fapi/HttpClientTests/MockHttpClientTest.phpt b/tests/Fapi/HttpClientTests/MockHttpClientTest.phpt index 6937c7a..88b9cc4 100644 --- a/tests/Fapi/HttpClientTests/MockHttpClientTest.phpt +++ b/tests/Fapi/HttpClientTests/MockHttpClientTest.phpt @@ -23,7 +23,7 @@ final class MockHttpClientTest extends TestCase $mockClient->sendRequest(new HttpRequest('GET', 'not.match.com/1')); }, InvalidArgumentException::class, - 'Invalid HTTP request. No more requests found.' + 'Invalid HTTP request. No more requests found.', ); } @@ -37,7 +37,7 @@ final class MockHttpClientTest extends TestCase $mockClient->sendRequest(new HttpRequest('GET', 'not.match.com/1')); }, InvalidArgumentException::class, - 'Invalid HTTP request. Url not matched. Expected "not.match.com" got "not.match.com/1".' + 'Invalid HTTP request. Url not matched. Expected "not.match.com" got "not.match.com/1".', ); } @@ -51,7 +51,7 @@ final class MockHttpClientTest extends TestCase $mockClient->sendRequest(new HttpRequest('POST', 'not.match.com')); }, InvalidArgumentException::class, - 'Invalid HTTP request. Method not matched. Expected "GET" got "POST".' + 'Invalid HTTP request. Method not matched. Expected "GET" got "POST".', ); } @@ -67,7 +67,7 @@ final class MockHttpClientTest extends TestCase ])); }, InvalidArgumentException::class, - 'Invalid HTTP request. Options not matched. Expected: "[]", got: "{"headers":[""]}".' + 'Invalid HTTP request. Options not matched. Expected: "[]", got: "{"headers":[""]}".', ); } @@ -81,7 +81,7 @@ final class MockHttpClientTest extends TestCase $mockClient->sendRequest(new HttpRequest('GET', 'not.match.com', [])); }, InvalidArgumentException::class, - 'Invalid HTTP request. Body not matched. Expected: "test", got: "".' + 'Invalid HTTP request. Body not matched. Expected: "test", got: "".', ); } diff --git a/tests/Fapi/HttpClientTests/MockHttpClients/SampleMockHttpClient.php b/tests/Fapi/HttpClientTests/MockHttpClients/SampleMockHttpClient.php index 65f3efd..3650120 100644 --- a/tests/Fapi/HttpClientTests/MockHttpClients/SampleMockHttpClient.php +++ b/tests/Fapi/HttpClientTests/MockHttpClients/SampleMockHttpClient.php @@ -21,13 +21,13 @@ public function __construct() 'User-Agent' => ['Nette Tester'], ], '', - '1.1' + '1.1', ), new HttpResponse( 200, ['Content-Type' => ['text/plain']], - "It works!\n" - ) + "It works!\n", + ), ); } diff --git a/tests/Fapi/HttpClientTests/MockHttpServer/LoginRequestHandler.php b/tests/Fapi/HttpClientTests/MockHttpServer/LoginRequestHandler.php index 3ddadcb..ee6c406 100644 --- a/tests/Fapi/HttpClientTests/MockHttpServer/LoginRequestHandler.php +++ b/tests/Fapi/HttpClientTests/MockHttpServer/LoginRequestHandler.php @@ -10,7 +10,7 @@ class LoginRequestHandler { public function handleRequest( - ServerRequestInterface $request + ServerRequestInterface $request, ): ResponseInterface { $method = $request->getMethod(); diff --git a/tests/Fapi/HttpClientTests/MockHttpServer/MockHttpServer.php b/tests/Fapi/HttpClientTests/MockHttpServer/MockHttpServer.php index 3837273..94298c4 100644 --- a/tests/Fapi/HttpClientTests/MockHttpServer/MockHttpServer.php +++ b/tests/Fapi/HttpClientTests/MockHttpServer/MockHttpServer.php @@ -13,32 +13,23 @@ class MockHttpServer { - /** @var React\EventLoop\LoopInterface */ - private $eventLoop; + private React\EventLoop\LoopInterface $eventLoop; - /** @var React\Socket\Server */ - private $socketServer; + private React\Socket\Server $socketServer; - /** @var React\Http\Server */ - private $httpServer; + private React\Http\Server $httpServer; - /** @var ApiRequestHandler */ - private $apiRequestHandler; + private ApiRequestHandler $apiRequestHandler; - /** @var AssignCookieRequestHandler */ - private $assignCookieRequestHandler; + private AssignCookieRequestHandler $assignCookieRequestHandler; - /** @var CheckCookieRequestHandler */ - private $checkCookieRequestHandler; + private CheckCookieRequestHandler $checkCookieRequestHandler; - /** @var DelayedRequestHandler */ - private $delayedRequestHandler; + private DelayedRequestHandler $delayedRequestHandler; - /** @var EmptyRequestHandler */ - private $emptyRequestHandler; + private EmptyRequestHandler $emptyRequestHandler; - /** @var LoginRequestHandler */ - private $loginRequestHandler; + private LoginRequestHandler $loginRequestHandler; public function run(): void { @@ -76,7 +67,7 @@ public function handleRequest(ServerRequestInterface $request): ResponseInterfac } private function processRequest( - ServerRequestInterface $request + ServerRequestInterface $request, ): ResponseInterface { $path = $request->getUri()->getPath(); diff --git a/tests/Fapi/HttpClientTests/MockHttpServer/MockHttpServerRunner.php b/tests/Fapi/HttpClientTests/MockHttpServer/MockHttpServerRunner.php index 1dd9af2..a504a7c 100644 --- a/tests/Fapi/HttpClientTests/MockHttpServer/MockHttpServerRunner.php +++ b/tests/Fapi/HttpClientTests/MockHttpServer/MockHttpServerRunner.php @@ -13,19 +13,15 @@ class MockHttpServerRunner { /** @var array function (MockHttpServerRunner $sender); Occurs when the mock HTTP server is started */ - public $onStarted = []; + public array $onStarted = []; - /** @var string */ - private $serverRunningMessage = "Server running at http://127.0.0.1:1337/\n"; + private string $serverRunningMessage = "Server running at http://127.0.0.1:1337/\n"; - /** @var React\EventLoop\LoopInterface */ - private $eventLoop; + private React\EventLoop\LoopInterface $eventLoop; - /** @var React\ChildProcess\Process */ - private $process; + private React\ChildProcess\Process $process; - /** @var string */ - private $stdoutBuffer; + private string $stdoutBuffer; public function run(): void { diff --git a/tests/Fapi/HttpClientTests/RedirectHelperTest.phpt b/tests/Fapi/HttpClientTests/RedirectHelperTest.phpt index c8a3a0f..5bedd21 100644 --- a/tests/Fapi/HttpClientTests/RedirectHelperTest.phpt +++ b/tests/Fapi/HttpClientTests/RedirectHelperTest.phpt @@ -87,8 +87,8 @@ class RedirectHelperTest extends TestCase new HttpResponse( HttpStatusCode::S301_MOVED_PERMANENTLY, ['Location' => ['http://example.com/b']], - '' - ) + '', + ), ); $client->add( @@ -96,8 +96,8 @@ class RedirectHelperTest extends TestCase new HttpResponse( HttpStatusCode::S302_FOUND, ['Location' => ['https://example.com/c']], - '' - ) + '', + ), ); $client->add( @@ -105,8 +105,8 @@ class RedirectHelperTest extends TestCase new HttpResponse( HttpStatusCode::S200_OK, ['Content-Type' => ['text/plain']], - 'OK' - ) + 'OK', + ), ); return $client; @@ -121,8 +121,8 @@ class RedirectHelperTest extends TestCase new HttpResponse( HttpStatusCode::S301_MOVED_PERMANENTLY, ['Location' => ['http://example.com/a2']], - '' - ) + '', + ), ); $client->add( @@ -130,8 +130,8 @@ class RedirectHelperTest extends TestCase new HttpResponse( HttpStatusCode::S301_MOVED_PERMANENTLY, ['Location' => ['invalid']], - '' - ) + '', + ), ); return $client; @@ -146,8 +146,8 @@ class RedirectHelperTest extends TestCase new HttpResponse( HttpStatusCode::S301_MOVED_PERMANENTLY, [], - '' - ) + '', + ), ); return $client; diff --git a/tests/Fapi/HttpClientTests/Rest/RestClientTest.phpt b/tests/Fapi/HttpClientTests/Rest/RestClientTest.phpt index ac482aa..be7e696 100644 --- a/tests/Fapi/HttpClientTests/Rest/RestClientTest.phpt +++ b/tests/Fapi/HttpClientTests/Rest/RestClientTest.phpt @@ -29,7 +29,7 @@ class RestClientTest extends TestCase HttpMethod::GET, [], HttpStatusCode::S200_OK, - '[{"id":1},{"id":2},{"id":3}]' + '[{"id":1},{"id":2},{"id":3}]', ); $restClient = new RestClient('admin', 'xxx', 'https://example.com/', $httpClient); @@ -45,7 +45,7 @@ class RestClientTest extends TestCase HttpMethod::GET, [], HttpStatusCode::S200_OK, - '[{"id":1},{"id":2}]' + '[{"id":1},{"id":2}]', ); $restClient = new RestClient('admin', 'xxx', 'https://example.com/', $httpClient); @@ -61,7 +61,7 @@ class RestClientTest extends TestCase HttpMethod::GET, [], HttpStatusCode::S200_OK, - '{"id":2}' + '{"id":2}', ); $restClient = new RestClient('admin', 'xxx', 'https://example.com/', $httpClient); @@ -77,7 +77,7 @@ class RestClientTest extends TestCase HttpMethod::GET, [], HttpStatusCode::S404_NOT_FOUND, - '{"status":"error","error":{"message":"Not Found"}}' + '{"status":"error","error":{"message":"Not Found"}}', ); $restClient = new RestClient('admin', 'xxx', 'https://example.com/', $httpClient); @@ -93,7 +93,7 @@ class RestClientTest extends TestCase HttpMethod::GET, [], HttpStatusCode::S200_OK, - '{"foo":true}' + '{"foo":true}', ); $restClient = new RestClient('admin', 'xxx', 'https://example.com/', $httpClient); @@ -109,7 +109,7 @@ class RestClientTest extends TestCase HttpMethod::POST, ['json' => ['foo' => 'bar']], HttpStatusCode::S201_CREATED, - '{"id":7,"foo":"bar"}' + '{"id":7,"foo":"bar"}', ); $restClient = new RestClient('admin', 'xxx', 'https://example.com/', $httpClient); @@ -125,7 +125,7 @@ class RestClientTest extends TestCase HttpMethod::PUT, ['json' => ['bar' => 'baz']], HttpStatusCode::S200_OK, - '{"id":7,"foo":"bar","bar":"baz"}' + '{"id":7,"foo":"bar","bar":"baz"}', ); $restClient = new RestClient('admin', 'xxx', 'https://example.com/', $httpClient); @@ -141,7 +141,7 @@ class RestClientTest extends TestCase HttpMethod::DELETE, [], HttpStatusCode::S200_OK, - 'null' + 'null', ); $restClient = new RestClient('admin', 'xxx', 'https://example.com/', $httpClient); @@ -156,7 +156,7 @@ class RestClientTest extends TestCase HttpMethod::DELETE, [], HttpStatusCode::S204_NO_CONTENT, - '' + '', ); $restClient = new RestClient('admin', 'xxx', 'https://example.com/', $httpClient); @@ -172,7 +172,7 @@ class RestClientTest extends TestCase string $method, array $options, int $statusCode, - string $responseBody + string $responseBody, ): MockHttpClient { $commonOptions = [ @@ -188,7 +188,7 @@ class RestClientTest extends TestCase $httpResponse = new HttpResponse( $statusCode, ['Content-Type' => ['application/json']], - $responseBody + $responseBody, ); $httpClient = new MockHttpClient(); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index c6c5427..bf08835 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,5 +1,7 @@ --> - + + + + + + + + + + + + + + - - - - - - - - - - diff --git a/tools/phpstan/phpstan.neon b/tools/phpstan/phpstan.neon index 083848f..774d8bd 100644 --- a/tools/phpstan/phpstan.neon +++ b/tools/phpstan/phpstan.neon @@ -1,8 +1,5 @@ parameters: excludes_analyse: - ignoreErrors: - - '#Constant LOCKS_DIR not found\.#' - - '#Used constant LOCKS_DIR not found\.#' includes: - ../../vendor/phpstan/phpstan-nette/extension.neon