From 098640c021ab4994cde065b7c9ca1574e8548ee1 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:09:47 +0100 Subject: [PATCH 01/49] Update composer.json --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3a6b2da..5e66a80 100644 --- a/composer.json +++ b/composer.json @@ -42,6 +42,7 @@ }, "autoload": { "files": [ + "functions/header_accept_parse.php", "functions/server_request_files.php", "functions/server_request_headers.php", "functions/server_request_method.php", @@ -60,7 +61,7 @@ "scripts": { "test": [ "phpcs", - "psalm", + "psalm --no-cache", "XDEBUG_MODE=coverage phpunit --coverage-text --colors=always" ], "build": [ From 3da6616c448228bcb4e2b05dba61ff1bd13e7e09 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:10:42 +0100 Subject: [PATCH 02/49] New function to parse the Accept header --- functions/header_accept_parse.php | 129 ++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 functions/header_accept_parse.php diff --git a/functions/header_accept_parse.php b/functions/header_accept_parse.php new file mode 100644 index 0000000..66f6f85 --- /dev/null +++ b/functions/header_accept_parse.php @@ -0,0 +1,129 @@ + + * @copyright Copyright (c) 2018, Anatoly Nekhay + * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE + * @link https://github.com/sunrise-php/http-message + */ + +namespace Sunrise\Http\Message; + +/** + * Import functions + */ +use function uasort; + +/** + * Parses the given Accept header + * + * @param string $header + * + * @return array> + */ +function header_accept_parse(string $header): array +{ + static $cache = []; + + if (isset($cache[$header])) { + return $cache[$header]; + } + + $cursor = -1; + $cursorInValue = true; + $cursorInParameter = false; + $cursorInParameterName = false; + $cursorInParameterValue = false; + $cursorInQuotedParameterValue = false; + + $data = []; + $valueIndex = 0; + $parameterIndex = 0; + + while (true) { + $char = $header[++$cursor] ?? null; + $prev = $header[$cursor-1] ?? null; + $next = $header[$cursor+1] ?? null; + + if ($char === null) { + break; + } + + if ($char === ' ' && !$cursorInQuotedParameterValue) { + continue; + } + + if ($char === ',' && !$cursorInQuotedParameterValue) { + $cursorInValue = true; + $cursorInParameter = false; + $cursorInParameterName = false; + $cursorInParameterValue = false; + $cursorInQuotedParameterValue = false; + $parameterIndex = 0; + $valueIndex++; + continue; + } + if ($char === ';' && !$cursorInQuotedParameterValue) { + $cursorInValue = false; + $cursorInParameter = true; + $cursorInParameterName = true; + $cursorInParameterValue = false; + $cursorInQuotedParameterValue = false; + $parameterIndex++; + continue; + } + if ($char === '=' && !$cursorInQuotedParameterValue && $cursorInParameterName) { + $cursorInValue = false; + $cursorInParameter = true; + $cursorInParameterName = false; + $cursorInParameterValue = true; + $cursorInQuotedParameterValue = false; + continue; + } + + if ($char === '"' && !$cursorInQuotedParameterValue && $cursorInParameterValue) { + $cursorInQuotedParameterValue = true; + continue; + } + if ($char === '\\' && $next === '"' && $cursorInQuotedParameterValue) { + continue; + } + if ($char === '"' && $prev !== '\\' && $cursorInQuotedParameterValue) { + $cursorInParameterValue = false; + $cursorInQuotedParameterValue = false; + continue; + } + + if ($cursorInValue) { + $data[$valueIndex][0] ??= ''; + $data[$valueIndex][0] .= $char; + continue; + } + if ($cursorInParameterName && isset($data[$valueIndex][0])) { + $data[$valueIndex][1][$parameterIndex][0] ??= ''; + $data[$valueIndex][1][$parameterIndex][0] .= $char; + continue; + } + if ($cursorInParameterValue && isset($data[$valueIndex][1][$parameterIndex][0])) { + $data[$valueIndex][1][$parameterIndex][1] ??= ''; + $data[$valueIndex][1][$parameterIndex][1] .= $char; + continue; + } + } + + $result = []; + foreach ($data as $item) { + $result[$item[0]] = []; + if (isset($item[1])) { + foreach ($item[1] as $param) { + $result[$item[0]][$param[0]] = $param[1] ?? ''; + } + } + } + + uasort($result, fn(array $a, array $b): int => ($b['q'] ?? 1) <=> ($a['q'] ?? 1)); + + return $cache[$header] = $result; +} From e6054bf4bf6db5af316e8f809549489198d2599d Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:12:22 +0100 Subject: [PATCH 03/49] Deleted --- .../FailedStreamOperationException.php | 19 ------------- .../FailedUploadedFileOperationException.php | 19 ------------- src/Exception/InvalidHeaderNameException.php | 19 ------------- src/Exception/InvalidHeaderValueException.php | 19 ------------- .../InvalidHeaderValueParameterException.php | 19 ------------- src/Exception/InvalidStreamException.php | 27 ------------------- .../InvalidStreamOperationException.php | 19 ------------- .../InvalidUploadedFileException.php | 19 ------------- .../InvalidUploadedFileOperationException.php | 19 ------------- 9 files changed, 179 deletions(-) delete mode 100644 src/Exception/FailedStreamOperationException.php delete mode 100644 src/Exception/FailedUploadedFileOperationException.php delete mode 100644 src/Exception/InvalidHeaderNameException.php delete mode 100644 src/Exception/InvalidHeaderValueException.php delete mode 100644 src/Exception/InvalidHeaderValueParameterException.php delete mode 100644 src/Exception/InvalidStreamException.php delete mode 100644 src/Exception/InvalidStreamOperationException.php delete mode 100644 src/Exception/InvalidUploadedFileException.php delete mode 100644 src/Exception/InvalidUploadedFileOperationException.php diff --git a/src/Exception/FailedStreamOperationException.php b/src/Exception/FailedStreamOperationException.php deleted file mode 100644 index 30e6369..0000000 --- a/src/Exception/FailedStreamOperationException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * FailedStreamOperationException - */ -class FailedStreamOperationException extends RuntimeException -{ -} diff --git a/src/Exception/FailedUploadedFileOperationException.php b/src/Exception/FailedUploadedFileOperationException.php deleted file mode 100644 index 9046ded..0000000 --- a/src/Exception/FailedUploadedFileOperationException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * FailedUploadedFileOperationException - */ -class FailedUploadedFileOperationException extends RuntimeException -{ -} diff --git a/src/Exception/InvalidHeaderNameException.php b/src/Exception/InvalidHeaderNameException.php deleted file mode 100644 index 8e3b545..0000000 --- a/src/Exception/InvalidHeaderNameException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * InvalidHeaderNameException - */ -class InvalidHeaderNameException extends InvalidHeaderException -{ -} diff --git a/src/Exception/InvalidHeaderValueException.php b/src/Exception/InvalidHeaderValueException.php deleted file mode 100644 index 571182d..0000000 --- a/src/Exception/InvalidHeaderValueException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * InvalidHeaderValueException - */ -class InvalidHeaderValueException extends InvalidHeaderException -{ -} diff --git a/src/Exception/InvalidHeaderValueParameterException.php b/src/Exception/InvalidHeaderValueParameterException.php deleted file mode 100644 index b537bba..0000000 --- a/src/Exception/InvalidHeaderValueParameterException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * InvalidHeaderValueParameterException - */ -class InvalidHeaderValueParameterException extends InvalidHeaderValueException -{ -} diff --git a/src/Exception/InvalidStreamException.php b/src/Exception/InvalidStreamException.php deleted file mode 100644 index bada40e..0000000 --- a/src/Exception/InvalidStreamException.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * InvalidStreamException - */ -class InvalidStreamException extends RuntimeException -{ - - /** - * @return self - */ - final public static function noResource(): self - { - return new self('The stream without a resource so the operation is not possible'); - } -} diff --git a/src/Exception/InvalidStreamOperationException.php b/src/Exception/InvalidStreamOperationException.php deleted file mode 100644 index 093af94..0000000 --- a/src/Exception/InvalidStreamOperationException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * InvalidStreamOperationException - */ -class InvalidStreamOperationException extends RuntimeException -{ -} diff --git a/src/Exception/InvalidUploadedFileException.php b/src/Exception/InvalidUploadedFileException.php deleted file mode 100644 index 38293e5..0000000 --- a/src/Exception/InvalidUploadedFileException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * InvalidUploadedFileException - */ -class InvalidUploadedFileException extends RuntimeException -{ -} diff --git a/src/Exception/InvalidUploadedFileOperationException.php b/src/Exception/InvalidUploadedFileOperationException.php deleted file mode 100644 index cf481db..0000000 --- a/src/Exception/InvalidUploadedFileOperationException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * InvalidUploadedFileOperationException - */ -class InvalidUploadedFileOperationException extends RuntimeException -{ -} From d011d59f4ae6d77912b65b556b2aa87bef4a3ad3 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:13:39 +0100 Subject: [PATCH 04/49] Improved code --- .../AccessControlAllowHeadersHeader.php | 12 +- .../AccessControlAllowMethodsHeader.php | 7 +- src/Header/AccessControlAllowOriginHeader.php | 8 +- .../AccessControlExposeHeadersHeader.php | 12 +- src/Header/AccessControlMaxAgeHeader.php | 8 +- src/Header/AgeHeader.php | 8 +- src/Header/AllowHeader.php | 7 +- src/Header/CacheControlHeader.php | 5 +- src/Header/ClearSiteDataHeader.php | 12 +- src/Header/ConnectionHeader.php | 71 -------- src/Header/ContentDispositionHeader.php | 12 +- src/Header/ContentEncodingHeader.php | 37 ++-- src/Header/ContentLanguageHeader.php | 12 +- src/Header/ContentLengthHeader.php | 8 +- src/Header/ContentLocationHeader.php | 4 +- src/Header/ContentMD5Header.php | 4 +- src/Header/ContentRangeHeader.php | 10 +- src/Header/ContentSecurityPolicyHeader.php | 7 +- src/Header/ContentTypeHeader.php | 12 +- src/Header/EtagHeader.php | 4 +- src/Header/KeepAliveHeader.php | 5 +- src/Header/LinkHeader.php | 9 +- src/Header/LocationHeader.php | 4 +- src/Header/RefreshHeader.php | 8 +- src/Header/SetCookieHeader.php | 163 ++++++++++-------- src/Header/TrailerHeader.php | 4 +- src/Header/TransferEncodingHeader.php | 37 ++-- src/Header/VaryHeader.php | 12 +- src/Header/WWWAuthenticateHeader.php | 73 +++++--- src/Header/WarningHeader.php | 53 ++++-- 30 files changed, 317 insertions(+), 311 deletions(-) delete mode 100644 src/Header/ConnectionHeader.php diff --git a/src/Header/AccessControlAllowHeadersHeader.php b/src/Header/AccessControlAllowHeadersHeader.php index 70d3d7e..46af826 100644 --- a/src/Header/AccessControlAllowHeadersHeader.php +++ b/src/Header/AccessControlAllowHeadersHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -31,23 +31,23 @@ class AccessControlAllowHeadersHeader extends Header /** * @var list */ - private array $headers; + private array $headers = []; /** * Constructor of the class * * @param string ...$headers * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the header names isn't valid. */ public function __construct(string ...$headers) { - /** @var list $headers */ - $this->validateToken(...$headers); - $this->headers = $headers; + foreach ($headers as $header) { + $this->headers[] = $header; + } } /** diff --git a/src/Header/AccessControlAllowMethodsHeader.php b/src/Header/AccessControlAllowMethodsHeader.php index ed8ab4b..b2020b5 100644 --- a/src/Header/AccessControlAllowMethodsHeader.php +++ b/src/Header/AccessControlAllowMethodsHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -39,16 +39,13 @@ class AccessControlAllowMethodsHeader extends Header * * @param string ...$methods * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the methods isn't valid. */ public function __construct(string ...$methods) { - /** @var list $methods */ - $this->validateToken(...$methods); - // normalize the list of methods... foreach ($methods as $method) { $this->methods[] = strtoupper($method); } diff --git a/src/Header/AccessControlAllowOriginHeader.php b/src/Header/AccessControlAllowOriginHeader.php index 02defb6..78adf10 100644 --- a/src/Header/AccessControlAllowOriginHeader.php +++ b/src/Header/AccessControlAllowOriginHeader.php @@ -15,7 +15,7 @@ * Import classes */ use Psr\Http\Message\UriInterface; -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Exception\InvalidUriException; use Sunrise\Http\Message\Header; use Sunrise\Http\Message\Uri; @@ -44,7 +44,7 @@ class AccessControlAllowOriginHeader extends Header * @throws InvalidUriException * If the URI isn't valid. * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the URI isn't valid. */ public function __construct($uri = null) @@ -91,13 +91,13 @@ public function getFieldValue(): string * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the URI isn't valid. */ private function validateUri(UriInterface $uri): void { if ($uri->getScheme() === '' || $uri->getHost() === '') { - throw new InvalidHeaderValueException(sprintf( + throw new InvalidHeaderException(sprintf( 'The URI "%2$s" for the header "%1$s" is not valid', $this->getFieldName(), $uri->__toString() diff --git a/src/Header/AccessControlExposeHeadersHeader.php b/src/Header/AccessControlExposeHeadersHeader.php index 999c974..554aea2 100644 --- a/src/Header/AccessControlExposeHeadersHeader.php +++ b/src/Header/AccessControlExposeHeadersHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -31,23 +31,23 @@ class AccessControlExposeHeadersHeader extends Header /** * @var list */ - private array $headers; + private array $headers = []; /** * Constructor of the class * * @param string ...$headers * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the header names isn't valid. */ public function __construct(string ...$headers) { - /** @var list $headers */ - $this->validateToken(...$headers); - $this->headers = $headers; + foreach ($headers as $header) { + $this->headers[] = $header; + } } /** diff --git a/src/Header/AccessControlMaxAgeHeader.php b/src/Header/AccessControlMaxAgeHeader.php index fa6d361..33e3f2a 100644 --- a/src/Header/AccessControlMaxAgeHeader.php +++ b/src/Header/AccessControlMaxAgeHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -38,7 +38,7 @@ class AccessControlMaxAgeHeader extends Header * * @param int $value * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the value isn't valid. */ public function __construct(int $value) @@ -71,13 +71,13 @@ public function getFieldValue(): string * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the value isn't valid. */ private function validateValue(int $value): void { if (! ($value === -1 || $value >= 1)) { - throw new InvalidHeaderValueException(sprintf( + throw new InvalidHeaderException(sprintf( 'The value "%2$d" for the header "%1$s" is not valid.', $this->getFieldName(), $value diff --git a/src/Header/AgeHeader.php b/src/Header/AgeHeader.php index faa4a60..4b007d5 100644 --- a/src/Header/AgeHeader.php +++ b/src/Header/AgeHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -38,7 +38,7 @@ class AgeHeader extends Header * * @param int $value * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the value isn't valid. */ public function __construct(int $value) @@ -71,13 +71,13 @@ public function getFieldValue(): string * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the value isn't valid. */ private function validateValue(int $value): void { if (! ($value >= 0)) { - throw new InvalidHeaderValueException(sprintf( + throw new InvalidHeaderException(sprintf( 'The value "%2$d" for the header "%1$s" is not valid', $this->getFieldName(), $value diff --git a/src/Header/AllowHeader.php b/src/Header/AllowHeader.php index fc943c4..c03cfd0 100644 --- a/src/Header/AllowHeader.php +++ b/src/Header/AllowHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -39,16 +39,13 @@ class AllowHeader extends Header * * @param string ...$methods * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the methods isn't valid. */ public function __construct(string ...$methods) { - /** @var list $methods */ - $this->validateToken(...$methods); - // normalize the list of methods... foreach ($methods as $method) { $this->methods[] = strtoupper($method); } diff --git a/src/Header/CacheControlHeader.php b/src/Header/CacheControlHeader.php index 1dfe3e0..f87e227 100644 --- a/src/Header/CacheControlHeader.php +++ b/src/Header/CacheControlHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueParameterException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -39,11 +39,12 @@ class CacheControlHeader extends Header * * @param array $parameters * - * @throws InvalidHeaderValueParameterException + * @throws InvalidHeaderException * If the parameters aren't valid. */ public function __construct(array $parameters) { + // validate and normalize the parameters... $parameters = $this->validateParameters($parameters); $this->parameters = $parameters; diff --git a/src/Header/ClearSiteDataHeader.php b/src/Header/ClearSiteDataHeader.php index 664f65b..865545b 100644 --- a/src/Header/ClearSiteDataHeader.php +++ b/src/Header/ClearSiteDataHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -32,23 +32,23 @@ class ClearSiteDataHeader extends Header /** * @var list */ - private array $directives; + private array $directives = []; /** * Constructor of the class * * @param string ...$directives * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the directives isn't valid. */ public function __construct(string ...$directives) { - /** @var list $directives */ - $this->validateQuotedString(...$directives); - $this->directives = $directives; + foreach ($directives as $directive) { + $this->directives[] = $directive; + } } /** diff --git a/src/Header/ConnectionHeader.php b/src/Header/ConnectionHeader.php deleted file mode 100644 index 171fe8d..0000000 --- a/src/Header/ConnectionHeader.php +++ /dev/null @@ -1,71 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; -use Sunrise\Http\Message\Header; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.10 - */ -class ConnectionHeader extends Header -{ - - /** - * @var string - */ - public const CONNECTION_CLOSE = 'close'; - - /** - * @var string - */ - public const CONNECTION_KEEP_ALIVE = 'keep-alive'; - - /** - * @var string - */ - private string $value; - - /** - * Constructor of the class - * - * @param string $value - * - * @throws InvalidHeaderValueException - * If the value isn't valid. - */ - public function __construct(string $value) - { - $this->validateToken($value); - - $this->value = $value; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Connection'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return $this->value; - } -} diff --git a/src/Header/ContentDispositionHeader.php b/src/Header/ContentDispositionHeader.php index 60d5bbf..4ddf063 100644 --- a/src/Header/ContentDispositionHeader.php +++ b/src/Header/ContentDispositionHeader.php @@ -14,8 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; -use Sunrise\Http\Message\Exception\InvalidHeaderValueParameterException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -45,16 +44,15 @@ class ContentDispositionHeader extends Header * @param string $type * @param array $parameters * - * @throws InvalidHeaderValueException - * If the type isn't valid. - * - * @throws InvalidHeaderValueParameterException - * If the parameters aren't valid. + * @throws InvalidHeaderException + * - If the type isn't valid; + * - If the parameters aren't valid. */ public function __construct(string $type, array $parameters = []) { $this->validateToken($type); + // validate and normalize the parameters,,, $parameters = $this->validateParameters($parameters); $this->type = $type; diff --git a/src/Header/ContentEncodingHeader.php b/src/Header/ContentEncodingHeader.php index af51152..2b3e958 100644 --- a/src/Header/ContentEncodingHeader.php +++ b/src/Header/ContentEncodingHeader.php @@ -14,7 +14,8 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Enum\Encoding; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -29,35 +30,45 @@ class ContentEncodingHeader extends Header { /** - * Directives - * - * @var string + * @deprecated Use the {@see Encoding} enum. + */ + public const BR = Encoding::BR; + + /** + * @deprecated Use the {@see Encoding} enum. */ - public const GZIP = 'gzip'; - public const COMPRESS = 'compress'; - public const DEFLATE = 'deflate'; - public const BR = 'br'; + public const COMPRESS = Encoding::COMPRESS; + + /** + * @deprecated Use the {@see Encoding} enum. + */ + public const DEFLATE = Encoding::DEFLATE; + + /** + * @deprecated Use the {@see Encoding} enum. + */ + public const GZIP = Encoding::GZIP; /** * @var list */ - private array $directives; + private array $directives = []; /** * Constructor of the class * * @param string ...$directives * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the directives isn't valid. */ public function __construct(string ...$directives) { - /** @var list $directives */ - $this->validateToken(...$directives); - $this->directives = $directives; + foreach ($directives as $directive) { + $this->directives[] = $directive; + } } /** diff --git a/src/Header/ContentLanguageHeader.php b/src/Header/ContentLanguageHeader.php index f81c33a..066f3d4 100644 --- a/src/Header/ContentLanguageHeader.php +++ b/src/Header/ContentLanguageHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -40,23 +40,23 @@ class ContentLanguageHeader extends Header /** * @var list */ - private array $languages; + private array $languages = []; /** * Constructor of the class * * @param string ...$languages * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the language codes isn't valid. */ public function __construct(string ...$languages) { - /** @var list $languages */ - $this->validateValueByRegex(self::RFC2616_LANGUAGE_TAG, ...$languages); - $this->languages = $languages; + foreach ($languages as $language) { + $this->languages[] = $language; + } } /** diff --git a/src/Header/ContentLengthHeader.php b/src/Header/ContentLengthHeader.php index c0a8393..d02df7c 100644 --- a/src/Header/ContentLengthHeader.php +++ b/src/Header/ContentLengthHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -38,7 +38,7 @@ class ContentLengthHeader extends Header * * @param int $value * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the value isn't valid. */ public function __construct(int $value) @@ -71,13 +71,13 @@ public function getFieldValue(): string * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the value isn't valid. */ private function validateValue(int $value): void { if (! ($value >= 0)) { - throw new InvalidHeaderValueException(sprintf( + throw new InvalidHeaderException(sprintf( 'The value "%2$d" for the header "%1$s" is not valid', $this->getFieldName(), $value diff --git a/src/Header/ContentLocationHeader.php b/src/Header/ContentLocationHeader.php index b2fd64e..c7852e8 100644 --- a/src/Header/ContentLocationHeader.php +++ b/src/Header/ContentLocationHeader.php @@ -40,9 +40,7 @@ class ContentLocationHeader extends Header */ public function __construct($uri) { - $uri = Uri::create($uri); - - $this->uri = $uri; + $this->uri = Uri::create($uri); } /** diff --git a/src/Header/ContentMD5Header.php b/src/Header/ContentMD5Header.php index 2ed1133..4a1c88d 100644 --- a/src/Header/ContentMD5Header.php +++ b/src/Header/ContentMD5Header.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -42,7 +42,7 @@ class ContentMD5Header extends Header * * @param string $value * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the value isn't valid. */ public function __construct(string $value) diff --git a/src/Header/ContentRangeHeader.php b/src/Header/ContentRangeHeader.php index 3f6884f..f90c7be 100644 --- a/src/Header/ContentRangeHeader.php +++ b/src/Header/ContentRangeHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -50,7 +50,7 @@ class ContentRangeHeader extends Header * @param int $lastBytePosition * @param int $instanceLength * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the range isn't valid. */ public function __construct(int $firstBytePosition, int $lastBytePosition, int $instanceLength) @@ -92,20 +92,20 @@ public function getFieldValue(): string * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the range isn't valid. */ private function validateRange(int $firstBytePosition, int $lastBytePosition, int $instanceLength): void { if (! ($firstBytePosition <= $lastBytePosition)) { - throw new InvalidHeaderValueException( + throw new InvalidHeaderException( 'The "first-byte-pos" value of the content range ' . 'must be less than or equal to the "last-byte-pos" value' ); } if (! ($lastBytePosition < $instanceLength)) { - throw new InvalidHeaderValueException( + throw new InvalidHeaderException( 'The "last-byte-pos" value of the content range ' . 'must be less than the "instance-length" value' ); diff --git a/src/Header/ContentSecurityPolicyHeader.php b/src/Header/ContentSecurityPolicyHeader.php index 620bb3f..ec59bd3 100644 --- a/src/Header/ContentSecurityPolicyHeader.php +++ b/src/Header/ContentSecurityPolicyHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueParameterException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -57,11 +57,12 @@ class ContentSecurityPolicyHeader extends Header * * @param array $parameters * - * @throws InvalidHeaderValueParameterException + * @throws InvalidHeaderException * If the parameters aren't valid. */ public function __construct(array $parameters = []) { + // validate and normalize the parameters... $parameters = $this->validateParametersByRegex( $parameters, self::VALID_DIRECTIVE_NAME, @@ -86,7 +87,7 @@ public function getFieldValue(): string { $directives = []; foreach ($this->parameters as $directive => $value) { - // the directive can be without value... + // the directive can be without value, // e.g. sandbox, upgrade-insecure-requests, etc. if ($value === '') { $directives[] = $directive; diff --git a/src/Header/ContentTypeHeader.php b/src/Header/ContentTypeHeader.php index 8964004..a4b2505 100644 --- a/src/Header/ContentTypeHeader.php +++ b/src/Header/ContentTypeHeader.php @@ -14,8 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; -use Sunrise\Http\Message\Exception\InvalidHeaderValueParameterException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -54,16 +53,15 @@ class ContentTypeHeader extends Header * @param string $type * @param array $parameters * - * @throws InvalidHeaderValueException - * If the type isn't valid. - * - * @throws InvalidHeaderValueParameterException - * If the parameters aren't valid. + * @throws InvalidHeaderException + * - If the type isn't valid; + * - If the parameters aren't valid. */ public function __construct(string $type, array $parameters = []) { $this->validateValueByRegex(self::RFC6838_CONTENT_TYPE, $type); + // validate and normalize the parameters... $parameters = $this->validateParameters($parameters); $this->type = $type; diff --git a/src/Header/EtagHeader.php b/src/Header/EtagHeader.php index d5d421f..f789388 100644 --- a/src/Header/EtagHeader.php +++ b/src/Header/EtagHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -38,7 +38,7 @@ class EtagHeader extends Header * * @param string $value * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the value isn't valid. */ public function __construct(string $value) diff --git a/src/Header/KeepAliveHeader.php b/src/Header/KeepAliveHeader.php index fbbc205..7d3a430 100644 --- a/src/Header/KeepAliveHeader.php +++ b/src/Header/KeepAliveHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueParameterException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -39,11 +39,12 @@ class KeepAliveHeader extends Header * * @param array $parameters * - * @throws InvalidHeaderValueParameterException + * @throws InvalidHeaderException * If the parameters aren't valid. */ public function __construct(array $parameters = []) { + // validate and normalize the parameters... $parameters = $this->validateParameters($parameters); $this->parameters = $parameters; diff --git a/src/Header/LinkHeader.php b/src/Header/LinkHeader.php index 506f54c..5cb0c3c 100644 --- a/src/Header/LinkHeader.php +++ b/src/Header/LinkHeader.php @@ -15,7 +15,7 @@ * Import classes */ use Psr\Http\Message\UriInterface; -use Sunrise\Http\Message\Exception\InvalidHeaderValueParameterException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Exception\InvalidUriException; use Sunrise\Http\Message\Header; use Sunrise\Http\Message\Uri; @@ -50,16 +50,15 @@ class LinkHeader extends Header * @throws InvalidUriException * If the URI isn't valid. * - * @throws InvalidHeaderValueParameterException + * @throws InvalidHeaderException * If the parameters aren't valid. */ public function __construct($uri, array $parameters = []) { - $uri = Uri::create($uri); - + // validate and normalize the parameters... $parameters = $this->validateParameters($parameters); - $this->uri = $uri; + $this->uri = Uri::create($uri); $this->parameters = $parameters; } diff --git a/src/Header/LocationHeader.php b/src/Header/LocationHeader.php index 0eef409..e7a76c3 100644 --- a/src/Header/LocationHeader.php +++ b/src/Header/LocationHeader.php @@ -40,9 +40,7 @@ class LocationHeader extends Header */ public function __construct($uri) { - $uri = Uri::create($uri); - - $this->uri = $uri; + $this->uri = Uri::create($uri); } /** diff --git a/src/Header/RefreshHeader.php b/src/Header/RefreshHeader.php index 4065b94..70089df 100644 --- a/src/Header/RefreshHeader.php +++ b/src/Header/RefreshHeader.php @@ -15,7 +15,7 @@ * Import classes */ use Psr\Http\Message\UriInterface; -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Exception\InvalidUriException; use Sunrise\Http\Message\Header; use Sunrise\Http\Message\Uri; @@ -50,7 +50,7 @@ class RefreshHeader extends Header * @throws InvalidUriException * If the URI isn't valid. * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the delay isn't valid. */ public function __construct(int $delay, $uri) @@ -86,13 +86,13 @@ public function getFieldValue(): string * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the delay isn't valid. */ private function validateDelay(int $delay): void { if (! ($delay >= 0)) { - throw new InvalidHeaderValueException(sprintf( + throw new InvalidHeaderException(sprintf( 'The delay "%2$d" for the header "%1$s" is not valid', $this->getFieldName(), $delay diff --git a/src/Header/SetCookieHeader.php b/src/Header/SetCookieHeader.php index cfcc990..ee17a29 100644 --- a/src/Header/SetCookieHeader.php +++ b/src/Header/SetCookieHeader.php @@ -16,7 +16,8 @@ */ use DateTimeImmutable; use DateTimeInterface; -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Enum\CookieSameSite; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -36,70 +37,65 @@ class SetCookieHeader extends Header { /** - * Cookies are not sent on normal cross-site subrequests, but - * are sent when a user is navigating to the origin site. - * - * This is the default cookie value if SameSite has not been - * explicitly specified in recent browser versions.. - * - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax + * Default cookie options * - * @var string + * @var array{ + * path?: ?string, + * domain?: ?string, + * secure?: ?bool, + * httpOnly?: ?bool, + * sameSite?: ?string + * } */ - public const SAME_SITE_LAX = 'Lax'; + public const DEFAULT_OPTIONS = [ + self::OPTION_KEY_PATH => '/', + self::OPTION_KEY_DOMAIN => null, + self::OPTION_KEY_SECURE => null, + self::OPTION_KEY_HTTP_ONLY => true, + self::OPTION_KEY_SAME_SITE => CookieSameSite::LAX, + ]; /** - * Cookies will only be sent in a first-party context and not - * be sent along with requests initiated by third party websites. - * - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#strict - * - * @var string + * Cookie option keys */ - public const SAME_SITE_STRICT = 'Strict'; + public const OPTION_KEY_PATH = 'path'; + public const OPTION_KEY_DOMAIN = 'domain'; + public const OPTION_KEY_SECURE = 'secure'; + public const OPTION_KEY_HTTP_ONLY = 'httpOnly'; + public const OPTION_KEY_SAME_SITE = 'sameSite'; /** - * Cookies will be sent in all contexts, i.e. in responses to - * both first-party and cross-site requests. - * - * If SameSite=None is set, the cookie Secure attribute must - * also be set (or the cookie will be blocked). - * - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#none - * - * @var string + * @deprecated Use the {@see CookieSameSite} enum. */ - public const SAME_SITE_NONE = 'None'; + public const SAME_SITE_LAX = CookieSameSite::LAX; /** - * Cookie option keys - * - * @var string + * @deprecated Use the {@see CookieSameSite} enum. */ - public const OPTION_KEY_PATH = 'path'; - public const OPTION_KEY_DOMAIN = 'domain'; - public const OPTION_KEY_SECURE = 'secure'; - public const OPTION_KEY_HTTP_ONLY = 'httpOnly'; - public const OPTION_KEY_SAMESITE = 'sameSite'; + public const SAME_SITE_STRICT = CookieSameSite::STRICT; /** - * Default cookie options + * @deprecated Use the {@see CookieSameSite} enum. + */ + public const SAME_SITE_NONE = CookieSameSite::NONE; + + /** + * @deprecated Use the {@see SetCookieHeader::OPTION_KEY_SAME_SITE} constant. + */ + public const OPTION_KEY_SAMESITE = self::OPTION_KEY_SAME_SITE; + + /** + * @var ?array{ + * path?: ?string, + * domain?: ?string, + * secure?: ?bool, + * httpOnly?: ?bool, + * sameSite?: ?string + * } * - * @var array{ - * path?: ?string, - * domain?: ?string, - * secure?: ?bool, - * httpOnly?: ?bool, - * sameSite?: ?string - * } + * @deprecated Use the {@see SetCookieHeader::DEFAULT_OPTIONS} constant. */ - protected static array $defaultOptions = [ - self::OPTION_KEY_PATH => '/', - self::OPTION_KEY_DOMAIN => null, - self::OPTION_KEY_SECURE => null, - self::OPTION_KEY_HTTP_ONLY => true, - self::OPTION_KEY_SAMESITE => self::SAME_SITE_LAX, - ]; + protected static ?array $defaultOptions = null; /** * The cookie name @@ -126,12 +122,12 @@ class SetCookieHeader extends Header * The cookie options * * @var array{ - * path?: ?string, - * domain?: ?string, - * secure?: ?bool, - * httpOnly?: ?bool, - * sameSite?: ?string - * } + * path?: ?string, + * domain?: ?string, + * secure?: ?bool, + * httpOnly?: ?bool, + * sameSite?: ?string + * } */ private array $options; @@ -141,25 +137,40 @@ class SetCookieHeader extends Header * @param string $name * @param string $value * @param DateTimeInterface|null $expires - * @param array{path?: ?string, domain?: ?string, secure?: ?bool, httpOnly?: ?bool, sameSite?: ?string} $options + * @param array{ + * path?: ?string, + * domain?: ?string, + * secure?: ?bool, + * httpOnly?: ?bool, + * sameSite?: ?string + * } $options * - * @throws InvalidHeaderValueException - * If one of the parameters isn't valid. + * @throws InvalidHeaderException + * If one of the arguments isn't valid. */ public function __construct(string $name, string $value, ?DateTimeInterface $expires = null, array $options = []) { $this->validateCookieName($name); if (isset($options[self::OPTION_KEY_PATH])) { - $this->validateCookieOption(self::OPTION_KEY_PATH, $options[self::OPTION_KEY_PATH]); + $this->validateCookieOption( + self::OPTION_KEY_PATH, + $options[self::OPTION_KEY_PATH] + ); } if (isset($options[self::OPTION_KEY_DOMAIN])) { - $this->validateCookieOption(self::OPTION_KEY_DOMAIN, $options[self::OPTION_KEY_DOMAIN]); + $this->validateCookieOption( + self::OPTION_KEY_DOMAIN, + $options[self::OPTION_KEY_DOMAIN] + ); } - if (isset($options[self::OPTION_KEY_SAMESITE])) { - $this->validateCookieOption(self::OPTION_KEY_SAMESITE, $options[self::OPTION_KEY_SAMESITE]); + if (isset($options[self::OPTION_KEY_SAME_SITE])) { + $this->validateCookieOption( + self::OPTION_KEY_SAME_SITE, + $options[self::OPTION_KEY_SAME_SITE] + ); } if ($value === '') { @@ -167,7 +178,7 @@ public function __construct(string $name, string $value, ?DateTimeInterface $exp $expires = new DateTimeImmutable('1 year ago'); } - $options += static::$defaultOptions; + $options += (static::$defaultOptions ?? static::DEFAULT_OPTIONS); $this->name = $name; $this->value = $value; @@ -213,8 +224,8 @@ public function getFieldValue(): string $result .= '; HttpOnly'; } - if (isset($this->options[self::OPTION_KEY_SAMESITE])) { - $result .= '; SameSite=' . $this->options[self::OPTION_KEY_SAMESITE]; + if (isset($this->options[self::OPTION_KEY_SAME_SITE])) { + $result .= '; SameSite=' . $this->options[self::OPTION_KEY_SAME_SITE]; } return $result; @@ -227,18 +238,18 @@ public function getFieldValue(): string * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the cookie name isn't valid. */ private function validateCookieName(string $name): void { if ('' === $name) { - throw new InvalidHeaderValueException('Cookie name cannot be empty'); + throw new InvalidHeaderException('Cookie name cannot be empty'); } // https://github.com/php/php-src/blob/02a5335b710aa36cd0c3108bfb9c6f7a57d40000/ext/standard/head.c#L93 if (strpbrk($name, "=,; \t\r\n\013\014") !== false) { - throw new InvalidHeaderValueException(sprintf( + throw new InvalidHeaderException(sprintf( 'The cookie name "%s" contains prohibited characters', $name )); @@ -248,29 +259,29 @@ private function validateCookieName(string $name): void /** * Validates the given cookie option * - * @param string $validKey + * @param string $key * @param mixed $value * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the cookie option isn't valid. */ - private function validateCookieOption(string $validKey, $value): void + private function validateCookieOption(string $key, $value): void { if (!is_string($value)) { - throw new InvalidHeaderValueException(sprintf( + throw new InvalidHeaderException(sprintf( 'The cookie option "%s" must be a string', - $validKey + $key )); } // https://github.com/php/php-src/blob/02a5335b710aa36cd0c3108bfb9c6f7a57d40000/ext/standard/head.c#L103 // https://github.com/php/php-src/blob/02a5335b710aa36cd0c3108bfb9c6f7a57d40000/ext/standard/head.c#L108 if (strpbrk($value, ",; \t\r\n\013\014") !== false) { - throw new InvalidHeaderValueException(sprintf( + throw new InvalidHeaderException(sprintf( 'The cookie option "%s" contains prohibited characters', - $validKey + $key )); } } diff --git a/src/Header/TrailerHeader.php b/src/Header/TrailerHeader.php index 684621a..779a59b 100644 --- a/src/Header/TrailerHeader.php +++ b/src/Header/TrailerHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -33,7 +33,7 @@ class TrailerHeader extends Header * * @param string $value * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the value isn't valid. */ public function __construct(string $value) diff --git a/src/Header/TransferEncodingHeader.php b/src/Header/TransferEncodingHeader.php index dabeda2..16be643 100644 --- a/src/Header/TransferEncodingHeader.php +++ b/src/Header/TransferEncodingHeader.php @@ -14,7 +14,8 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Enum\Encoding; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -29,35 +30,45 @@ class TransferEncodingHeader extends Header { /** - * Directives - * - * @var string + * @deprecated Use the {@see Encoding} enum. + */ + public const CHUNKED = Encoding::CHUNKED; + + /** + * @deprecated Use the {@see Encoding} enum. */ - public const CHUNKED = 'chunked'; - public const COMPRESS = 'compress'; - public const DEFLATE = 'deflate'; - public const GZIP = 'gzip'; + public const COMPRESS = Encoding::COMPRESS; + + /** + * @deprecated Use the {@see Encoding} enum. + */ + public const DEFLATE = Encoding::DEFLATE; + + /** + * @deprecated Use the {@see Encoding} enum. + */ + public const GZIP = Encoding::GZIP; /** * @var list */ - private array $directives; + private array $directives = []; /** * Constructor of the class * * @param string ...$directives * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the directives isn't valid. */ public function __construct(string ...$directives) { - /** @var list $directives */ - $this->validateToken(...$directives); - $this->directives = $directives; + foreach ($directives as $directive) { + $this->directives[] = $directive; + } } /** diff --git a/src/Header/VaryHeader.php b/src/Header/VaryHeader.php index dd12ef3..1d56444 100644 --- a/src/Header/VaryHeader.php +++ b/src/Header/VaryHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -31,23 +31,23 @@ class VaryHeader extends Header /** * @var list */ - private array $value; + private array $value = []; /** * Constructor of the class * * @param string ...$value * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the values isn't valid. */ public function __construct(string ...$value) { - /** @var list $value */ - $this->validateToken(...$value); - $this->value = $value; + foreach ($value as $item) { + $this->value[] = $item; + } } /** diff --git a/src/Header/WWWAuthenticateHeader.php b/src/Header/WWWAuthenticateHeader.php index c6c475b..ced02b1 100644 --- a/src/Header/WWWAuthenticateHeader.php +++ b/src/Header/WWWAuthenticateHeader.php @@ -14,8 +14,8 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; -use Sunrise\Http\Message\Exception\InvalidHeaderValueParameterException; +use Sunrise\Http\Message\Enum\AuthenticationScheme; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -31,20 +31,54 @@ class WWWAuthenticateHeader extends Header { /** - * HTTP Authentication Schemes - * - * @link https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml + * @deprecated Use the {@see AuthenticationScheme} enum. + */ + public const HTTP_AUTHENTICATE_SCHEME_BASIC = AuthenticationScheme::BASIC; + + /** + * @deprecated Use the {@see AuthenticationScheme} enum. */ - public const HTTP_AUTHENTICATE_SCHEME_BASIC = 'Basic'; - public const HTTP_AUTHENTICATE_SCHEME_BEARER = 'Bearer'; - public const HTTP_AUTHENTICATE_SCHEME_DIGEST = 'Digest'; - public const HTTP_AUTHENTICATE_SCHEME_HOBA = 'HOBA'; - public const HTTP_AUTHENTICATE_SCHEME_MUTUAL = 'Mutual'; - public const HTTP_AUTHENTICATE_SCHEME_NEGOTIATE = 'Negotiate'; - public const HTTP_AUTHENTICATE_SCHEME_OAUTH = 'OAuth'; - public const HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_1 = 'SCRAM-SHA-1'; - public const HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_256 = 'SCRAM-SHA-256'; - public const HTTP_AUTHENTICATE_SCHEME_VAPID = 'vapid'; + public const HTTP_AUTHENTICATE_SCHEME_BEARER = AuthenticationScheme::BEARER; + + /** + * @deprecated Use the {@see AuthenticationScheme} enum. + */ + public const HTTP_AUTHENTICATE_SCHEME_DIGEST = AuthenticationScheme::DIGEST; + + /** + * @deprecated Use the {@see AuthenticationScheme} enum. + */ + public const HTTP_AUTHENTICATE_SCHEME_HOBA = AuthenticationScheme::HOBA; + + /** + * @deprecated Use the {@see AuthenticationScheme} enum. + */ + public const HTTP_AUTHENTICATE_SCHEME_MUTUAL = AuthenticationScheme::MUTUAL; + + /** + * @deprecated Use the {@see AuthenticationScheme} enum. + */ + public const HTTP_AUTHENTICATE_SCHEME_NEGOTIATE = AuthenticationScheme::NEGOTIATE; + + /** + * @deprecated Use the {@see AuthenticationScheme} enum. + */ + public const HTTP_AUTHENTICATE_SCHEME_OAUTH = AuthenticationScheme::OAUTH; + + /** + * @deprecated Use the {@see AuthenticationScheme} enum. + */ + public const HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_1 = AuthenticationScheme::SCRAM_SHA_1; + + /** + * @deprecated Use the {@see AuthenticationScheme} enum. + */ + public const HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_256 = AuthenticationScheme::SCRAM_SHA_256; + + /** + * @deprecated Use the {@see AuthenticationScheme} enum. + */ + public const HTTP_AUTHENTICATE_SCHEME_VAPID = AuthenticationScheme::VAPID; /** * @var string @@ -62,16 +96,15 @@ class WWWAuthenticateHeader extends Header * @param string $scheme * @param array $parameters * - * @throws InvalidHeaderValueException - * If the scheme isn't valid. - * - * @throws InvalidHeaderValueParameterException - * If the parameters aren't valid. + * @throws InvalidHeaderException + * - If the scheme isn't valid; + * - If the parameters aren't valid. */ public function __construct(string $scheme, array $parameters = []) { $this->validateToken($scheme); + // validate and normalize the parameters... $parameters = $this->validateParameters($parameters); $this->scheme = $scheme; diff --git a/src/Header/WarningHeader.php b/src/Header/WarningHeader.php index 7132def..de36b05 100644 --- a/src/Header/WarningHeader.php +++ b/src/Header/WarningHeader.php @@ -15,7 +15,8 @@ * Import classes */ use DateTimeInterface; -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; +use Sunrise\Http\Message\Enum\WarningCode; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; /** @@ -30,17 +31,39 @@ class WarningHeader extends Header { /** - * HTTP Warning Codes - * - * @link https://www.iana.org/assignments/http-warn-codes/http-warn-codes.xhtml + * @deprecated Use the {@see WarningCode} enum. + */ + public const HTTP_WARNING_CODE_RESPONSE_IS_STALE = WarningCode::RESPONSE_IS_STALE; + + /** + * @deprecated Use the {@see WarningCode} enum. + */ + public const HTTP_WARNING_CODE_REVALIDATION_FAILED = WarningCode::REVALIDATION_FAILED; + + /** + * @deprecated Use the {@see WarningCode} enum. + */ + public const HTTP_WARNING_CODE_DISCONNECTED_OPERATION = WarningCode::DISCONNECTED_OPERATION; + + /** + * @deprecated Use the {@see WarningCode} enum. + */ + public const HTTP_WARNING_CODE_HEURISTIC_EXPIRATION = WarningCode::HEURISTIC_EXPIRATION; + + /** + * @deprecated Use the {@see WarningCode} enum. + */ + public const HTTP_WARNING_CODE_MISCELLANEOUS_WARNING = WarningCode::MISCELLANEOUS_WARNING; + + /** + * @deprecated Use the {@see WarningCode} enum. + */ + public const HTTP_WARNING_CODE_TRANSFORMATION_APPLIED = WarningCode::TRANSFORMATION_APPLIED; + + /** + * @deprecated Use the {@see WarningCode} enum. */ - public const HTTP_WARNING_CODE_RESPONSE_IS_STALE = 110; - public const HTTP_WARNING_CODE_REVALIDATION_FAILED = 111; - public const HTTP_WARNING_CODE_DISCONNECTED_OPERATION = 112; - public const HTTP_WARNING_CODE_HEURISTIC_EXPIRATION = 113; - public const HTTP_WARNING_CODE_MISCELLANEOUS_WARNING = 199; - public const HTTP_WARNING_CODE_TRANSFORMATION_APPLIED = 214; - public const HTTP_WARNING_CODE_MISCELLANEOUS_PERSISTENT_WARNING = 299; + public const HTTP_WARNING_CODE_MISCELLANEOUS_PERSISTENT_WARNING = WarningCode::MISCELLANEOUS_PERSISTENT_WARNING; /** * @var int @@ -70,8 +93,8 @@ class WarningHeader extends Header * @param string $text * @param DateTimeInterface|null $date * - * @throws InvalidHeaderValueException - * If one of parameters isn't valid. + * @throws InvalidHeaderException + * If one of arguments isn't valid. */ public function __construct(int $code, string $agent, string $text, ?DateTimeInterface $date = null) { @@ -114,13 +137,13 @@ public function getFieldValue(): string * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If the code isn't valid. */ private function validateCode(int $code): void { if (! ($code >= 100 && $code <= 999)) { - throw new InvalidHeaderValueException(sprintf( + throw new InvalidHeaderException(sprintf( 'The code "%2$d" for the header "%1$s" is not valid', $this->getFieldName(), $code From 35365cbd03ec961a57c0c04a6d5385f35ae009dc Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:15:21 +0100 Subject: [PATCH 05/49] Improved code --- functions/server_request_files.php | 39 ++++++--- functions/server_request_protocol_version.php | 2 +- src/Header.php | 25 +++--- src/HeaderInterface.php | 2 +- src/Message.php | 43 +++++----- src/Request.php | 2 +- src/Response.php | 2 +- src/Response/HtmlResponse.php | 20 ++--- src/Response/JsonResponse.php | 18 ++--- src/ServerRequest.php | 2 +- src/Stream.php | 57 ++++++------- src/Stream/FileStream.php | 19 +---- src/Stream/PhpInputStream.php | 2 +- src/Stream/PhpMemoryStream.php | 2 +- src/Stream/PhpTempStream.php | 2 +- src/Stream/TmpfileStream.php | 12 +-- src/UploadedFile.php | 79 ++++++++----------- src/Uri.php | 30 ++----- src/Uri/Component/Password.php | 18 +++++ src/Uri/Component/User.php | 18 +++++ src/Uri/Component/UserInfo.php | 4 +- 21 files changed, 189 insertions(+), 209 deletions(-) diff --git a/functions/server_request_files.php b/functions/server_request_files.php index ce8d050..08c1a39 100644 --- a/functions/server_request_files.php +++ b/functions/server_request_files.php @@ -27,11 +27,11 @@ use const UPLOAD_ERR_NO_FILE; /** - * Gets the request uploaded files + * Gets the request's uploaded files * * Note that not sent files will not be handled. * - * @param array|null $rawUploadedFiles + * @param array|null $files * * @return array * @@ -40,19 +40,34 @@ * @link https://www.php.net/manual/ru/features.file-upload.multiple.php * @link https://github.com/php/php-src/blob/8c5b41cefb88b753c630b731956ede8d9da30c5d/main/rfc1867.c */ -function server_request_files(?array $rawUploadedFiles = null): array +function server_request_files(?array $files = null): array { - $rawUploadedFiles ??= $_FILES; + $files ??= $_FILES; $walker = function ($path, $size, $error, $name, $type) use (&$walker) { - if (! is_array($path)) { - return new UploadedFile(new FileStream($path, 'rb'), $size, $error, $name, $type); + if (!is_array($path)) { + // What if the path is an empty string? + $stream = new FileStream($path, 'rb'); + + return new UploadedFile( + $stream, + $size, + $error, + $name, + $type + ); } $result = []; foreach ($path as $key => $_) { if (UPLOAD_ERR_NO_FILE <> $error[$key]) { - $result[$key] = $walker($path[$key], $size[$key], $error[$key], $name[$key], $type[$key]); + $result[$key] = $walker( + $path[$key], + $size[$key], + $error[$key], + $name[$key], + $type[$key] + ); } } @@ -60,9 +75,15 @@ function server_request_files(?array $rawUploadedFiles = null): array }; $result = []; - foreach ($rawUploadedFiles as $key => $file) { + foreach ($files as $key => $file) { if (UPLOAD_ERR_NO_FILE <> $file['error']) { - $result[$key] = $walker($file['tmp_name'], $file['size'], $file['error'], $file['name'], $file['type']); + $result[$key] = $walker( + $file['tmp_name'], + $file['size'], + $file['error'], + $file['name'], + $file['type'] + ); } } diff --git a/functions/server_request_protocol_version.php b/functions/server_request_protocol_version.php index 2edc6ab..fdff200 100644 --- a/functions/server_request_protocol_version.php +++ b/functions/server_request_protocol_version.php @@ -18,7 +18,7 @@ use function sscanf; /** - * Gets the request protocol version + * Gets the request's protocol version * * @param array|null $serverParams * diff --git a/src/Header.php b/src/Header.php index abd8846..8d82ecb 100644 --- a/src/Header.php +++ b/src/Header.php @@ -14,9 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; -use Sunrise\Http\Message\Exception\InvalidHeaderValueParameterException; -use ArrayIterator; +use Sunrise\Http\Message\Exception\InvalidHeaderException; use DateTime; use DateTimeImmutable; use DateTimeInterface; @@ -79,7 +77,8 @@ abstract class Header implements HeaderInterface */ final public function getIterator(): Traversable { - return new ArrayIterator([$this->getFieldName(), $this->getFieldValue()]); + yield $this->getFieldName(); + yield $this->getFieldValue(); } /** @@ -109,7 +108,7 @@ final protected function isToken(string $token): bool * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the tokens isn't valid. */ final protected function validateToken(string ...$tokens): void @@ -124,7 +123,7 @@ final protected function validateToken(string ...$tokens): void * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the field values isn't valid. */ final protected function validateFieldValue(string ...$fieldValues): void @@ -139,7 +138,7 @@ final protected function validateFieldValue(string ...$fieldValues): void * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the quoted strings isn't valid. */ final protected function validateQuotedString(string ...$quotedStrings): void @@ -155,7 +154,7 @@ final protected function validateQuotedString(string ...$quotedStrings): void * @return array * The normalized parameters. * - * @throws InvalidHeaderValueParameterException + * @throws InvalidHeaderException * If one of the parameters isn't valid. */ final protected function validateParameters(array $parameters): array @@ -175,14 +174,14 @@ final protected function validateParameters(array $parameters): array * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidHeaderException * If one of the values isn't valid. */ final protected function validateValueByRegex(string $regex, string ...$values): void { foreach ($values as $value) { if (!preg_match($regex, $value)) { - throw new InvalidHeaderValueException(sprintf( + throw new InvalidHeaderException(sprintf( 'The value "%2$s" for the header "%1$s" is not valid', $this->getFieldName(), $value @@ -201,14 +200,14 @@ final protected function validateValueByRegex(string $regex, string ...$values): * @return array * The normalized parameters. * - * @throws InvalidHeaderValueParameterException + * @throws InvalidHeaderException * If one of the parameters isn't valid. */ final protected function validateParametersByRegex(array $parameters, string $nameRegex, string $valueRegex): array { foreach ($parameters as $name => &$value) { if (!is_string($name) || !preg_match($nameRegex, $name)) { - throw new InvalidHeaderValueParameterException(sprintf( + throw new InvalidHeaderException(sprintf( 'The parameter name "%2$s" for the header "%1$s" is not valid', $this->getFieldName(), (is_string($name) ? $name : ('<' . gettype($name) . '>')) @@ -221,7 +220,7 @@ final protected function validateParametersByRegex(array $parameters, string $na } if (!is_string($value) || !preg_match($valueRegex, $value)) { - throw new InvalidHeaderValueParameterException(sprintf( + throw new InvalidHeaderException(sprintf( 'The parameter value "%2$s" for the header "%1$s" is not valid', $this->getFieldName(), (is_string($value) ? $value : ('<' . gettype($value) . '>')) diff --git a/src/HeaderInterface.php b/src/HeaderInterface.php index 67afa98..898e3b7 100644 --- a/src/HeaderInterface.php +++ b/src/HeaderInterface.php @@ -21,7 +21,7 @@ * $response->withHeader(...new SetCookieHeader('foo', 'bar')) * * - * @template-implements IteratorAggregate + * @extends IteratorAggregate */ interface HeaderInterface extends IteratorAggregate { diff --git a/src/Message.php b/src/Message.php index 443cffc..55080cc 100644 --- a/src/Message.php +++ b/src/Message.php @@ -17,9 +17,6 @@ use Psr\Http\Message\MessageInterface; use Psr\Http\Message\StreamInterface; use Sunrise\Http\Message\Exception\InvalidArgumentException; -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Exception\InvalidHeaderNameException; -use Sunrise\Http\Message\Exception\InvalidHeaderValueException; use Sunrise\Http\Message\Stream\PhpTempStream; /** @@ -181,7 +178,7 @@ public function getHeaderLine($name): string * * @return static * - * @throws InvalidHeaderException + * @throws InvalidArgumentException * If the header isn't valid. */ public function withHeader($name, $value): MessageInterface @@ -200,7 +197,7 @@ public function withHeader($name, $value): MessageInterface * * @return static * - * @throws InvalidHeaderException + * @throws InvalidArgumentException * If the header isn't valid. */ public function withAddedHeader($name, $value): MessageInterface @@ -277,7 +274,7 @@ final protected function setProtocolVersion($protocolVersion): void * * @return void * - * @throws InvalidHeaderException + * @throws InvalidArgumentException * If the header isn't valid. */ final protected function setHeader($name, $value, bool $replace = true): void @@ -310,7 +307,7 @@ final protected function setHeader($name, $value, bool $replace = true): void * * @return void * - * @throws InvalidHeaderException + * @throws InvalidArgumentException * If one of the headers isn't valid. */ final protected function setHeaders(array $headers): void @@ -373,21 +370,21 @@ private function validateProtocolVersion($protocolVersion): void * * @return void * - * @throws InvalidHeaderNameException + * @throws InvalidArgumentException * If the header name isn't valid. */ private function validateHeaderName($name): void { if ($name === '') { - throw new InvalidHeaderNameException('HTTP header name cannot be an empty'); + throw new InvalidArgumentException('HTTP header name cannot be an empty'); } if (!is_string($name)) { - throw new InvalidHeaderNameException('HTTP header name must be a string'); + throw new InvalidArgumentException('HTTP header name must be a string'); } if (!preg_match(Header::RFC7230_VALID_TOKEN, $name)) { - throw new InvalidHeaderNameException('HTTP header name is invalid'); + throw new InvalidArgumentException('HTTP header name is invalid'); } } @@ -399,36 +396,36 @@ private function validateHeaderName($name): void * * @return void * - * @throws InvalidHeaderValueException + * @throws InvalidArgumentException * If the header value isn't valid. */ private function validateHeaderValue(string $validName, array $value): void { if ([] === $value) { - throw new InvalidHeaderValueException(sprintf( + throw new InvalidArgumentException(sprintf( 'The "%s" HTTP header value cannot be an empty array', $validName, )); } - foreach ($value as $i => $subvalue) { - if ('' === $subvalue) { + foreach ($value as $key => $item) { + if ('' === $item) { continue; } - if (!is_string($subvalue)) { - throw new InvalidHeaderValueException(sprintf( - 'The "%s[%d]" HTTP header value must be a string', + if (!is_string($item)) { + throw new InvalidArgumentException(sprintf( + 'The "%s[%s]" HTTP header value must be a string', $validName, - $i + $key )); } - if (!preg_match(Header::RFC7230_VALID_FIELD_VALUE, $subvalue)) { - throw new InvalidHeaderValueException(sprintf( - 'The "%s[%d]" HTTP header value is invalid', + if (!preg_match(Header::RFC7230_VALID_FIELD_VALUE, $item)) { + throw new InvalidArgumentException(sprintf( + 'The "%s[%s]" HTTP header value is invalid', $validName, - $i + $key )); } } diff --git a/src/Request.php b/src/Request.php index 54a3751..fb96c46 100644 --- a/src/Request.php +++ b/src/Request.php @@ -89,7 +89,7 @@ class Request extends Message implements RequestInterface, RequestMethodInterfac * @param StreamInterface|null $body * * @throws InvalidArgumentException - * If one of the parameters isn't valid. + * If one of the arguments isn't valid. */ public function __construct( ?string $method = null, diff --git a/src/Response.php b/src/Response.php index d0c99cd..a99e31d 100644 --- a/src/Response.php +++ b/src/Response.php @@ -160,7 +160,7 @@ class Response extends Message implements ResponseInterface, StatusCodeInterface * @param StreamInterface|null $body * * @throws InvalidArgumentException - * If one of the parameters isn't valid. + * If one of the arguments isn't valid. */ public function __construct( ?int $statusCode = null, diff --git a/src/Response/HtmlResponse.php b/src/Response/HtmlResponse.php index 1c71b96..955967d 100644 --- a/src/Response/HtmlResponse.php +++ b/src/Response/HtmlResponse.php @@ -27,18 +27,11 @@ use function method_exists; /** - * HTML Response + * HTML response */ -class HtmlResponse extends Response +final class HtmlResponse extends Response { - /** - * The response content type - * - * @var string - */ - public const CONTENT_TYPE = 'text/html; charset=utf-8'; - /** * Constructor of the class * @@ -49,11 +42,11 @@ class HtmlResponse extends Response */ public function __construct(int $statusCode, $html) { - $body = $this->createBody($html); + parent::__construct($statusCode); - $headers = ['Content-Type' => self::CONTENT_TYPE]; + $this->setBody($this->createBody($html)); - parent::__construct($statusCode, null, $headers, $body); + $this->setHeader('Content-Type', 'text/html; charset=utf-8'); } /** @@ -64,7 +57,6 @@ public function __construct(int $statusCode, $html) * @return StreamInterface * * @throws InvalidArgumentException - * If the response body cannot be created from the given HTML. */ private function createBody($html): StreamInterface { @@ -78,7 +70,7 @@ private function createBody($html): StreamInterface } if (!is_string($html)) { - throw new InvalidArgumentException('Unable to create HTML response due to invalid body'); + throw new InvalidArgumentException('Unable to create HTML response due to unexpected HTML data'); } $stream = new PhpTempStream('r+b'); diff --git a/src/Response/JsonResponse.php b/src/Response/JsonResponse.php index 4057f4f..fd5c227 100644 --- a/src/Response/JsonResponse.php +++ b/src/Response/JsonResponse.php @@ -31,18 +31,11 @@ use const JSON_THROW_ON_ERROR; /** - * JSON Response + * JSON response */ -class JsonResponse extends Response +final class JsonResponse extends Response { - /** - * The response content type - * - * @var string - */ - public const CONTENT_TYPE = 'application/json; charset=utf-8'; - /** * Constructor of the class * @@ -55,11 +48,11 @@ class JsonResponse extends Response */ public function __construct(int $statusCode, $data, int $flags = 0, int $depth = 512) { - $body = $this->createBody($data, $flags, $depth); + parent::__construct($statusCode); - $headers = ['Content-Type' => self::CONTENT_TYPE]; + $this->setBody($this->createBody($data, $flags, $depth)); - parent::__construct($statusCode, null, $headers, $body); + $this->setHeader('Content-Type', 'application/json; charset=utf-8'); } /** @@ -72,7 +65,6 @@ public function __construct(int $statusCode, $data, int $flags = 0, int $depth = * @return StreamInterface * * @throws InvalidArgumentException - * If the response body cannot be created from the given JSON data. */ private function createBody($data, int $flags, int $depth): StreamInterface { diff --git a/src/ServerRequest.php b/src/ServerRequest.php index 0d9503c..a7420f4 100644 --- a/src/ServerRequest.php +++ b/src/ServerRequest.php @@ -94,7 +94,7 @@ class ServerRequest extends Request implements ServerRequestInterface * @param array $attributes * * @throws InvalidArgumentException - * If one of the parameters isn't valid. + * If one of the arguments isn't valid. */ public function __construct( ?string $protocolVersion = null, diff --git a/src/Stream.php b/src/Stream.php index 22a0e5c..56f7783 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -15,10 +15,8 @@ * Import classes */ use Psr\Http\Message\StreamInterface; -use Sunrise\Http\Message\Exception\FailedStreamOperationException; use Sunrise\Http\Message\Exception\InvalidArgumentException; -use Sunrise\Http\Message\Exception\InvalidStreamException; -use Sunrise\Http\Message\Exception\InvalidStreamOperationException; +use Sunrise\Http\Message\Exception\RuntimeException; use Throwable; /** @@ -90,7 +88,7 @@ public function __construct($resource, bool $autoClose = true) * @return StreamInterface * * @throws InvalidArgumentException - * If a stream cannot be created. + * If the stream cannot be initialized with the resource. */ public static function create($resource): StreamInterface { @@ -166,18 +164,17 @@ public function eof(): bool * * @return int * - * @throws InvalidStreamException - * @throws FailedStreamOperationException + * @throws RuntimeException */ public function tell(): int { if (!is_resource($this->resource)) { - throw InvalidStreamException::noResource(); + throw new RuntimeException('The stream without a resource so the operation is not possible'); } $result = ftell($this->resource); if ($result === false) { - throw new FailedStreamOperationException('Unable to get the stream pointer position'); + throw new RuntimeException('Unable to get the stream pointer position'); } return $result; @@ -205,9 +202,7 @@ public function isSeekable(): bool * * @return void * - * @throws InvalidStreamException - * @throws InvalidStreamOperationException - * @throws FailedStreamOperationException + * @throws RuntimeException */ public function rewind(): void { @@ -224,23 +219,21 @@ public function rewind(): void * * @return void * - * @throws InvalidStreamException - * @throws InvalidStreamOperationException - * @throws FailedStreamOperationException + * @throws RuntimeException */ public function seek($offset, $whence = SEEK_SET): void { if (!is_resource($this->resource)) { - throw InvalidStreamException::noResource(); + throw new RuntimeException('The stream without a resource so the operation is not possible'); } if (!$this->isSeekable()) { - throw new InvalidStreamOperationException('Stream is not seekable'); + throw new RuntimeException('Stream is not seekable'); } $result = fseek($this->resource, $offset, $whence); if ($result !== 0) { - throw new FailedStreamOperationException('Unable to move the stream pointer position'); + throw new RuntimeException('Unable to move the stream pointer position'); } } @@ -272,23 +265,21 @@ public function isWritable(): bool * * @return int * - * @throws InvalidStreamException - * @throws InvalidStreamOperationException - * @throws FailedStreamOperationException + * @throws RuntimeException */ public function write($string): int { if (!is_resource($this->resource)) { - throw InvalidStreamException::noResource(); + throw new RuntimeException('The stream without a resource so the operation is not possible'); } if (!$this->isWritable()) { - throw new InvalidStreamOperationException('Stream is not writable'); + throw new RuntimeException('Stream is not writable'); } $result = fwrite($this->resource, $string); if ($result === false) { - throw new FailedStreamOperationException('Unable to write to the stream'); + throw new RuntimeException('Unable to write to the stream'); } return $result; @@ -320,23 +311,21 @@ public function isReadable(): bool * * @return string * - * @throws InvalidStreamException - * @throws InvalidStreamOperationException - * @throws FailedStreamOperationException + * @throws RuntimeException */ public function read($length): string { if (!is_resource($this->resource)) { - throw InvalidStreamException::noResource(); + throw new RuntimeException('The stream without a resource so the operation is not possible'); } if (!$this->isReadable()) { - throw new InvalidStreamOperationException('Stream is not readable'); + throw new RuntimeException('Stream is not readable'); } $result = fread($this->resource, $length); if ($result === false) { - throw new FailedStreamOperationException('Unable to read from the stream'); + throw new RuntimeException('Unable to read from the stream'); } return $result; @@ -349,23 +338,21 @@ public function read($length): string * * @return string * - * @throws InvalidStreamException - * @throws InvalidStreamOperationException - * @throws FailedStreamOperationException + * @throws RuntimeException */ public function getContents(): string { if (!is_resource($this->resource)) { - throw InvalidStreamException::noResource(); + throw new RuntimeException('The stream without a resource so the operation is not possible'); } if (!$this->isReadable()) { - throw new InvalidStreamOperationException('Stream is not readable'); + throw new RuntimeException('Stream is not readable'); } $result = stream_get_contents($this->resource); if ($result === false) { - throw new FailedStreamOperationException('Unable to read the remainder of the stream'); + throw new RuntimeException('Unable to read the remainder of the stream'); } return $result; diff --git a/src/Stream/FileStream.php b/src/Stream/FileStream.php index 5e01e62..7bdc99a 100644 --- a/src/Stream/FileStream.php +++ b/src/Stream/FileStream.php @@ -14,7 +14,6 @@ /** * Import classes */ -use Psr\Http\Message\StreamInterface; use Sunrise\Http\Message\Exception\RuntimeException; use Sunrise\Http\Message\Stream; use Throwable; @@ -25,13 +24,11 @@ use function fopen; use function is_resource; use function sprintf; -use function sys_get_temp_dir; -use function tempnam; /** * FileStream */ -class FileStream extends Stream +final class FileStream extends Stream { /** @@ -60,18 +57,4 @@ public function __construct(string $filename, string $mode) parent::__construct($resource); } - - /** - * Creates a new temporary file in the temporary directory - * - * @return StreamInterface - * - * @throws RuntimeException - */ - public static function tempFile(): StreamInterface - { - $filename = tempnam(sys_get_temp_dir(), 'sunrisephp'); - - return new self($filename, 'w+b'); - } } diff --git a/src/Stream/PhpInputStream.php b/src/Stream/PhpInputStream.php index 018213b..dff405e 100644 --- a/src/Stream/PhpInputStream.php +++ b/src/Stream/PhpInputStream.php @@ -25,7 +25,7 @@ /** * @link https://www.php.net/manual/en/wrappers.php.php#wrappers.php.input */ -class PhpInputStream extends Stream +final class PhpInputStream extends Stream { /** diff --git a/src/Stream/PhpMemoryStream.php b/src/Stream/PhpMemoryStream.php index 0ae1b46..67dc1a1 100644 --- a/src/Stream/PhpMemoryStream.php +++ b/src/Stream/PhpMemoryStream.php @@ -24,7 +24,7 @@ /** * @link https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory */ -class PhpMemoryStream extends Stream +final class PhpMemoryStream extends Stream { /** diff --git a/src/Stream/PhpTempStream.php b/src/Stream/PhpTempStream.php index d16b889..d3618a0 100644 --- a/src/Stream/PhpTempStream.php +++ b/src/Stream/PhpTempStream.php @@ -26,7 +26,7 @@ /** * @link https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory */ -class PhpTempStream extends Stream +final class PhpTempStream extends Stream { /** diff --git a/src/Stream/TmpfileStream.php b/src/Stream/TmpfileStream.php index b6e217b..51f331c 100644 --- a/src/Stream/TmpfileStream.php +++ b/src/Stream/TmpfileStream.php @@ -32,7 +32,7 @@ * * @link https://www.php.net/tmpfile */ -class TmpfileStream extends Stream +final class TmpfileStream extends Stream { /** @@ -42,16 +42,16 @@ class TmpfileStream extends Stream */ public function __construct() { - $tmpdir = sys_get_temp_dir(); - if (!is_writable($tmpdir)) { + $dirname = sys_get_temp_dir(); + if (!is_writable($dirname)) { throw new RuntimeException('Temporary files directory is not writable'); } - $tmpfile = tmpfile(); - if (!is_resource($tmpfile)) { + $resource = tmpfile(); + if (!is_resource($resource)) { throw new RuntimeException('Temporary file cannot be created or opened'); } - parent::__construct($tmpfile); + parent::__construct($resource); } } diff --git a/src/UploadedFile.php b/src/UploadedFile.php index fedaaeb..2a9a3a1 100644 --- a/src/UploadedFile.php +++ b/src/UploadedFile.php @@ -16,9 +16,7 @@ */ use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UploadedFileInterface; -use Sunrise\Http\Message\Exception\FailedUploadedFileOperationException; -use Sunrise\Http\Message\Exception\InvalidUploadedFileException; -use Sunrise\Http\Message\Exception\InvalidUploadedFileOperationException; +use Sunrise\Http\Message\Exception\RuntimeException; use Sunrise\Http\Message\Stream\FileStream; /** @@ -60,23 +58,15 @@ class UploadedFile implements UploadedFileInterface */ public const UPLOAD_ERRORS = [ UPLOAD_ERR_OK => 'No error', - UPLOAD_ERR_INI_SIZE => 'The uploaded file exceeds the upload_max_filesize directive in php.ini', - UPLOAD_ERR_FORM_SIZE => 'The uploaded file exceeds the MAX_FILE_SIZE directive ' . - 'that was specified in the HTML form', - UPLOAD_ERR_PARTIAL => 'The uploaded file was only partially uploaded', + UPLOAD_ERR_INI_SIZE => 'Uploaded file exceeds the upload_max_filesize directive in php.ini', + UPLOAD_ERR_FORM_SIZE => 'Uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the form', + UPLOAD_ERR_PARTIAL => 'Uploaded file was only partially uploaded', UPLOAD_ERR_NO_FILE => 'No file was uploaded', UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder', UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk', UPLOAD_ERR_EXTENSION => 'File upload stopped by extension', ]; - /** - * Unknown error description - * - * @var string - */ - public const UNKNOWN_ERROR_TEXT = 'Unknown error'; - /** * The file stream * @@ -106,14 +96,14 @@ class UploadedFile implements UploadedFileInterface private string $errorMessage; /** - * The file name + * The client's file name * * @var string|null */ private ?string $clientFilename; /** - * The file type + * The client's file media type * * @var string|null */ @@ -139,11 +129,11 @@ public function __construct( $this->stream = $stream; } - $errorMessage = self::UPLOAD_ERRORS[$error] ?? self::UNKNOWN_ERROR_TEXT; + $message = self::UPLOAD_ERRORS[$error] ?? 'Unknown error'; $this->size = $size; $this->errorCode = $error; - $this->errorMessage = $errorMessage; + $this->errorMessage = $message; $this->clientFilename = $clientFilename; $this->clientMediaType = $clientMediaType; } @@ -153,23 +143,23 @@ public function __construct( * * @return StreamInterface * - * @throws InvalidUploadedFileException - * If the file has no a stream due to an error or - * if the file was already moved. + * @throws RuntimeException + * - If the file has no a stream due to an error; + * - If the file was already moved. */ public function getStream(): StreamInterface { if (UPLOAD_ERR_OK <> $this->errorCode) { - throw new InvalidUploadedFileException(sprintf( - 'The uploaded file has no a stream due to the error #%d (%s)', + throw new RuntimeException(sprintf( + 'Uploaded file has no a stream due to the error #%d (%s)', $this->errorCode, $this->errorMessage )); } if (!isset($this->stream)) { - throw new InvalidUploadedFileException( - 'The uploaded file has no a stream because it was already moved' + throw new RuntimeException( + 'Uploaded file has no a stream because it was already moved' ); } @@ -183,42 +173,38 @@ public function getStream(): StreamInterface * * @return void * - * @throws InvalidUploadedFileException - * If the file has no a stream due to an error or - * if the file was already moved. - * - * @throws InvalidUploadedFileOperationException - * If the file cannot be read. - * - * @throws FailedUploadedFileOperationException - * If the target path cannot be used. + * @throws RuntimeException + * - If the file has no a stream due to an error; + * - If the file was already moved; + * - If the file cannot be read; + * - If the target path cannot be used. */ public function moveTo($targetPath): void { if (UPLOAD_ERR_OK <> $this->errorCode) { - throw new InvalidUploadedFileException(sprintf( - 'The uploaded file cannot be moved due to the error #%d (%s)', + throw new RuntimeException(sprintf( + 'Uploaded file cannot be moved due to the error #%d (%s)', $this->errorCode, $this->errorMessage )); } if (!isset($this->stream)) { - throw new InvalidUploadedFileException( - 'The uploaded file cannot be moved because it was already moved' + throw new RuntimeException( + 'Uploaded file cannot be moved because it was already moved' ); } if (!$this->stream->isReadable()) { - throw new InvalidUploadedFileOperationException( - 'The uploaded file cannot be moved because it is not readable' + throw new RuntimeException( + 'Uploaded file cannot be moved because it is not readable' ); } $targetDir = dirname($targetPath); if (!is_dir($targetDir) || !is_writable($targetDir)) { - throw new FailedUploadedFileOperationException(sprintf( - 'The uploaded file cannot be moved because the directory "%s" is not writable', + throw new RuntimeException(sprintf( + 'Uploaded file cannot be moved because the directory "%s" is not writable', $targetDir )); } @@ -230,8 +216,9 @@ public function moveTo($targetPath): void } while (!$this->stream->eof()) { - $piece = $this->stream->read(4096); - $targetStream->write($piece); + $targetStream->write( + $this->stream->read(4096) + ); } $targetStream->close(); @@ -271,7 +258,7 @@ public function getError(): int } /** - * Gets the file name + * Gets the client's file name * * @return string|null */ @@ -281,7 +268,7 @@ public function getClientFilename(): ?string } /** - * Gets the file type + * Gets the client's file media type * * @return string|null */ diff --git a/src/Uri.php b/src/Uri.php index 5059c37..95fa3eb 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -151,7 +151,7 @@ public function __construct(string $uri = '') * @return UriInterface * * @throws InvalidArgumentException - * If a URI cannot be created. + * If the URI isn't valid. */ public static function create($uri): UriInterface { @@ -424,9 +424,7 @@ public function __toString(): string */ final protected function setScheme($scheme): void { - $component = new Scheme($scheme); - - $this->scheme = $component->getValue(); + $this->scheme = (new Scheme($scheme))->getValue(); } /** @@ -442,9 +440,7 @@ final protected function setScheme($scheme): void */ final protected function setUserInfo($user, $password): void { - $component = new UserInfo($user, $password); - - $this->userInfo = $component->getValue(); + $this->userInfo = (new UserInfo($user, $password))->getValue(); } /** @@ -459,9 +455,7 @@ final protected function setUserInfo($user, $password): void */ final protected function setHost($host): void { - $component = new Host($host); - - $this->host = $component->getValue(); + $this->host = (new Host($host))->getValue(); } /** @@ -476,9 +470,7 @@ final protected function setHost($host): void */ final protected function setPort($port): void { - $component = new Port($port); - - $this->port = $component->getValue(); + $this->port = (new Port($port))->getValue(); } /** @@ -493,9 +485,7 @@ final protected function setPort($port): void */ final protected function setPath($path): void { - $component = new Path($path); - - $this->path = $component->getValue(); + $this->path = (new Path($path))->getValue(); } /** @@ -510,9 +500,7 @@ final protected function setPath($path): void */ final protected function setQuery($query): void { - $component = new Query($query); - - $this->query = $component->getValue(); + $this->query = (new Query($query))->getValue(); } /** @@ -527,8 +515,6 @@ final protected function setQuery($query): void */ final protected function setFragment($fragment): void { - $component = new Fragment($fragment); - - $this->fragment = $component->getValue(); + $this->fragment = (new Fragment($fragment))->getValue(); } } diff --git a/src/Uri/Component/Password.php b/src/Uri/Component/Password.php index 11be4ea..969db1c 100644 --- a/src/Uri/Component/Password.php +++ b/src/Uri/Component/Password.php @@ -70,6 +70,24 @@ public function __construct($value) }, $value); } + /** + * Creates a password component + * + * @param mixed $password + * + * @return Password + * + * @throws InvalidUriComponentException + */ + public static function create($password): Password + { + if ($password instanceof Password) { + return $password; + } + + return new Password($password); + } + /** * {@inheritdoc} * diff --git a/src/Uri/Component/User.php b/src/Uri/Component/User.php index 9d84249..8b15bc9 100644 --- a/src/Uri/Component/User.php +++ b/src/Uri/Component/User.php @@ -70,6 +70,24 @@ public function __construct($value) }, $value); } + /** + * Creates a user component + * + * @param mixed $user + * + * @return User + * + * @throws InvalidUriComponentException + */ + public static function create($user): User + { + if ($user instanceof User) { + return $user; + } + + return new User($user); + } + /** * {@inheritdoc} * diff --git a/src/Uri/Component/UserInfo.php b/src/Uri/Component/UserInfo.php index 1bf6e4d..434fc94 100644 --- a/src/Uri/Component/UserInfo.php +++ b/src/Uri/Component/UserInfo.php @@ -41,10 +41,10 @@ final class UserInfo implements ComponentInterface */ public function __construct($user, $password = null) { - $this->user = $user instanceof User ? $user : new User($user); + $this->user = User::create($user); if (isset($password)) { - $this->password = $password instanceof Password ? $password : new Password($password); + $this->password = Password::create($password); } } From 07ce6d13fc445e0912e915cbb4b1b36ef2e33d7c Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:15:46 +0100 Subject: [PATCH 06/49] New AuthenticationScheme enum --- src/Enum/AuthenticationScheme.php | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/Enum/AuthenticationScheme.php diff --git a/src/Enum/AuthenticationScheme.php b/src/Enum/AuthenticationScheme.php new file mode 100644 index 0000000..c9976ec --- /dev/null +++ b/src/Enum/AuthenticationScheme.php @@ -0,0 +1,31 @@ + + * @copyright Copyright (c) 2018, Anatoly Nekhay + * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE + * @link https://github.com/sunrise-php/http-message + */ + +namespace Sunrise\Http\Message\Enum; + +/** + * HTTP authentication schemes + * + * @link https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml + */ +final class AuthenticationScheme +{ + public const BASIC = 'Basic'; + public const BEARER = 'Bearer'; + public const DIGEST = 'Digest'; + public const HOBA = 'HOBA'; + public const MUTUAL = 'Mutual'; + public const NEGOTIATE = 'Negotiate'; + public const OAUTH = 'OAuth'; + public const SCRAM_SHA_1 = 'SCRAM-SHA-1'; + public const SCRAM_SHA_256 = 'SCRAM-SHA-256'; + public const VAPID = 'vapid'; +} From 287fb59a3eb8c8c7e2fdacc6cd410f10ad8d8b07 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:15:55 +0100 Subject: [PATCH 07/49] New CookieSameSite enum --- src/Enum/CookieSameSite.php | 51 +++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/Enum/CookieSameSite.php diff --git a/src/Enum/CookieSameSite.php b/src/Enum/CookieSameSite.php new file mode 100644 index 0000000..1582fe1 --- /dev/null +++ b/src/Enum/CookieSameSite.php @@ -0,0 +1,51 @@ + + * @copyright Copyright (c) 2018, Anatoly Nekhay + * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE + * @link https://github.com/sunrise-php/http-message + */ + +namespace Sunrise\Http\Message\Enum; + +/** + * CookieSameSite + * + * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + */ +final class CookieSameSite +{ + + /** + * Cookies are not sent on normal cross-site subrequests, but are + * sent when a user is navigating to the origin site. + * + * This is the default cookie value if SameSite has not been + * explicitly specified in recent browser versions. + * + * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax + */ + public const LAX = 'Lax'; + + /** + * Cookies will only be sent in a first-party context and not be + * sent along with requests initiated by third party websites. + * + * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#strict + */ + public const STRICT = 'Strict'; + + /** + * Cookies will be sent in all contexts, i.e. in responses to both + * first-party and cross-site requests. + * + * If SameSite=None is set, the cookie Secure attribute must also + * be set (or the cookie will be blocked). + * + * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#none + */ + public const NONE = 'None'; +} From 3d13bb0da390a9026ec653569d3068dd617070bf Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:16:04 +0100 Subject: [PATCH 08/49] New Encoding enum --- src/Enum/Encoding.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/Enum/Encoding.php diff --git a/src/Enum/Encoding.php b/src/Enum/Encoding.php new file mode 100644 index 0000000..f127ce0 --- /dev/null +++ b/src/Enum/Encoding.php @@ -0,0 +1,24 @@ + + * @copyright Copyright (c) 2018, Anatoly Nekhay + * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE + * @link https://github.com/sunrise-php/http-message + */ + +namespace Sunrise\Http\Message\Enum; + +/** + * Encoding + */ +final class Encoding +{ + public const BR = 'br'; + public const CHUNKED = 'chunked'; + public const COMPRESS = 'compress'; + public const DEFLATE = 'deflate'; + public const GZIP = 'gzip'; +} From ade0201b35d8a0745a694b4cdbcbcaab1aa26eae Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:16:20 +0100 Subject: [PATCH 09/49] New WarningCode enum --- src/Enum/WarningCode.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/Enum/WarningCode.php diff --git a/src/Enum/WarningCode.php b/src/Enum/WarningCode.php new file mode 100644 index 0000000..c49388f --- /dev/null +++ b/src/Enum/WarningCode.php @@ -0,0 +1,28 @@ + + * @copyright Copyright (c) 2018, Anatoly Nekhay + * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE + * @link https://github.com/sunrise-php/http-message + */ + +namespace Sunrise\Http\Message\Enum; + +/** + * HTTP warning codes + * + * @link https://www.iana.org/assignments/http-warn-codes/http-warn-codes.xhtml + */ +final class WarningCode +{ + public const RESPONSE_IS_STALE = 110; + public const REVALIDATION_FAILED = 111; + public const DISCONNECTED_OPERATION = 112; + public const HEURISTIC_EXPIRATION = 113; + public const MISCELLANEOUS_WARNING = 199; + public const TRANSFORMATION_APPLIED = 214; + public const MISCELLANEOUS_PERSISTENT_WARNING = 299; +} From 87a9933476314f7418a302412592f75ea044b85e Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:16:58 +0100 Subject: [PATCH 10/49] New IpAddress entity --- src/Entity/IpAddress.php | 137 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 src/Entity/IpAddress.php diff --git a/src/Entity/IpAddress.php b/src/Entity/IpAddress.php new file mode 100644 index 0000000..3a200b0 --- /dev/null +++ b/src/Entity/IpAddress.php @@ -0,0 +1,137 @@ + + * @copyright Copyright (c) 2018, Anatoly Nekhay + * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE + * @link https://github.com/sunrise-php/http-message + */ + +namespace Sunrise\Http\Message\Entity; + +/** + * Import functions + */ +use function filter_var; + +/** + * Import constants + */ +use const FILTER_FLAG_IPV4; +use const FILTER_FLAG_IPV6; +use const FILTER_FLAG_NO_PRIV_RANGE; +use const FILTER_FLAG_NO_RES_RANGE; +use const FILTER_VALIDATE_IP; + +/** + * IP address + */ +final class IpAddress +{ + + /** + * The IP address value + * + * @var string + */ + private string $value; + + /** + * The list of proxies in front of this IP address + * + * @var list + */ + private array $proxies; + + /** + * Constructor of the class + * + * @param string $value + * @param list $proxies + */ + public function __construct(string $value, array $proxies = []) + { + $this->value = $value; + $this->proxies = $proxies; + } + + /** + * Gets the IP address value + * + * @return string + */ + public function getValue(): string + { + return $this->value; + } + + /** + * Gets the list of proxies in front of this IP address + * + * @return list + */ + public function getProxies(): array + { + return $this->proxies; + } + + /** + * Checks if the IP address is valid + * + * @return bool + */ + public function isValid(): bool + { + return false !== filter_var($this->value, FILTER_VALIDATE_IP); + } + + /** + * Checks if the IP address is IPv4 + * + * @return bool + */ + public function isVersion4(): bool + { + return false !== filter_var($this->value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); + } + + /** + * Checks if the IP address is IPv6 + * + * @return bool + */ + public function isVersion6(): bool + { + return false !== filter_var($this->value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); + } + + /** + * Checks if the IP address is in the private range + * + * @return bool + */ + public function isInPrivateRange(): bool + { + if (!$this->isValid()) { + return false; + } + + return false === filter_var($this->value, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); + } + + /** + * Checks if the IP address is in the reserved range + * + * @return bool + */ + public function isInReservedRange(): bool + { + if (!$this->isValid()) { + return false; + } + + return false === filter_var($this->value, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE); + } +} From cb5351719753605d853239c9e63943433bcfa03b Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:17:07 +0100 Subject: [PATCH 11/49] New TempFileStream stream --- src/Stream/TempFileStream.php | 63 +++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/Stream/TempFileStream.php diff --git a/src/Stream/TempFileStream.php b/src/Stream/TempFileStream.php new file mode 100644 index 0000000..de6c4c9 --- /dev/null +++ b/src/Stream/TempFileStream.php @@ -0,0 +1,63 @@ + + * @copyright Copyright (c) 2018, Anatoly Nekhay + * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE + * @link https://github.com/sunrise-php/http-message + */ + +namespace Sunrise\Http\Message\Stream; + +/** + * Import classes + */ +use Sunrise\Http\Message\Exception\RuntimeException; +use Sunrise\Http\Message\Stream; + +/** + * Import functions + */ +use function fopen; +use function is_resource; +use function is_writable; +use function sys_get_temp_dir; +use function tempnam; + +/** + * TempFileStream + */ +final class TempFileStream extends Stream +{ + + /** + * Constructor of the class + * + * @param string|null $prefix + * + * @throws RuntimeException + */ + public function __construct(?string $prefix = null) + { + $prefix ??= 'sunrisephp'; + + $dirname = sys_get_temp_dir(); + if (!is_writable($dirname)) { + throw new RuntimeException('Temporary files directory is not writable'); + } + + $filename = tempnam($dirname, $prefix); + if ($filename === false) { + throw new RuntimeException('Temporary file name cannot be generated'); + } + + $resource = fopen($filename, 'w+b'); + if (!is_resource($resource)) { + throw new RuntimeException('Temporary file cannot be created or opened'); + } + + parent::__construct($resource); + } +} From 17f9337096bbcd6a7dc9ee669ea0c780aeb910dc Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 04:17:14 +0100 Subject: [PATCH 12/49] Deleted --- tests/Header/ConnectionHeaderTest.php | 78 --------------------------- 1 file changed, 78 deletions(-) delete mode 100644 tests/Header/ConnectionHeaderTest.php diff --git a/tests/Header/ConnectionHeaderTest.php b/tests/Header/ConnectionHeaderTest.php deleted file mode 100644 index 036b9ed..0000000 --- a/tests/Header/ConnectionHeaderTest.php +++ /dev/null @@ -1,78 +0,0 @@ -assertSame('close', ConnectionHeader::CONNECTION_CLOSE); - $this->assertSame('keep-alive', ConnectionHeader::CONNECTION_KEEP_ALIVE); - } - - public function testContracts() - { - $header = new ConnectionHeader('foo'); - - $this->assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new ConnectionHeader('foo'); - - $this->assertSame('Connection', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new ConnectionHeader('foo'); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Connection" is not valid'); - - new ConnectionHeader(''); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Connection" is not valid'); - - // isn't a token... - new ConnectionHeader('@'); - } - - public function testBuild() - { - $header = new ConnectionHeader('foo'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new ConnectionHeader('foo'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} From 8c28f90167eed17ed2fe5f29b17fb28520fe7883 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 05:01:15 +0100 Subject: [PATCH 13/49] Update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index f9244b0..676318d 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,17 @@ $tmpfileStream = new TmpfileStream(); $tmpfileStream->getMetadata('uri'); ``` +If you don't need the above behavior, you can use another temporary file stream: + +```php +use Sunrise\Http\Message\Stream\TempFileStream; + +$tempFileStream = new TempFileStream(); + +// Returns the file path... +$tempFileStream->getMetadata('uri'); +``` + ### PSR-7 and PSR-17 The following classes implement PSR-7: From 5849f8e75dac2ed5202334314975dfb086893ddd Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 05:01:29 +0100 Subject: [PATCH 14/49] Improve tests --- .../ServerRequestProxyIntegrationTest.php | 36 ++++++++++++++++ tests/Response/HtmlResponseTest.php | 2 +- tests/ServerRequestProxyTest.php | 17 ++++++++ tests/StreamTest.php | 42 +++++++++---------- tests/UploadedFileTest.php | 35 ++++++++-------- 5 files changed, 91 insertions(+), 41 deletions(-) create mode 100644 tests/Integration/ServerRequestProxyIntegrationTest.php create mode 100644 tests/ServerRequestProxyTest.php diff --git a/tests/Integration/ServerRequestProxyIntegrationTest.php b/tests/Integration/ServerRequestProxyIntegrationTest.php new file mode 100644 index 0000000..ac18f47 --- /dev/null +++ b/tests/Integration/ServerRequestProxyIntegrationTest.php @@ -0,0 +1,36 @@ +expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Unable to create HTML response due to invalid body'); + $this->expectExceptionMessage('Unable to create HTML response due to unexpected HTML data'); new HtmlResponse(200, null); } diff --git a/tests/ServerRequestProxyTest.php b/tests/ServerRequestProxyTest.php new file mode 100644 index 0000000..9da9243 --- /dev/null +++ b/tests/ServerRequestProxyTest.php @@ -0,0 +1,17 @@ +testStream->detach(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->tell(); @@ -160,7 +158,7 @@ public function testTellAfterClose(): void { $this->testStream->close(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->tell(); @@ -170,7 +168,7 @@ public function testFailedTell(): void { $testStream = new Stream(STDIN, false); - $this->expectException(FailedStreamOperationException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Unable to get the stream pointer position'); $testStream->tell(); @@ -211,7 +209,7 @@ public function testRewindAfterDetach(): void { $this->testStream->detach(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->rewind(); @@ -221,7 +219,7 @@ public function testRewindAfterClose(): void { $this->testStream->close(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->rewind(); @@ -231,7 +229,7 @@ public function testRewindInUnseekableResource(): void { $testStream = new Stream(STDIN, false); - $this->expectException(InvalidStreamOperationException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Stream is not seekable'); $testStream->rewind(); @@ -248,7 +246,7 @@ public function testSeekAfterDetach(): void { $this->testStream->detach(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->seek(0); @@ -258,7 +256,7 @@ public function testSeekAfterClose(): void { $this->testStream->close(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->seek(0); @@ -268,7 +266,7 @@ public function testSeekInUnseekableResource(): void { $testStream = new Stream(STDIN, false); - $this->expectException(InvalidStreamOperationException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Stream is not seekable'); $testStream->seek(0); @@ -276,7 +274,7 @@ public function testSeekInUnseekableResource(): void public function testFailedSeek(): void { - $this->expectException(FailedStreamOperationException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Unable to move the stream pointer position'); $this->testStream->seek(1); @@ -316,7 +314,7 @@ public function testWriteAfterDetach(): void { $this->testStream->detach(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->write('foo'); @@ -326,7 +324,7 @@ public function testWriteAfterClose(): void { $this->testStream->close(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->write('foo'); @@ -336,7 +334,7 @@ public function testWriteToUnwritableResource(): void { $testStream = new Stream(STDIN, false); - $this->expectException(InvalidStreamOperationException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Stream is not writable'); $testStream->write('foo'); @@ -376,7 +374,7 @@ public function testReadAfterDetach(): void { $this->testStream->detach(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->read(1); @@ -386,7 +384,7 @@ public function testReadAfterClose(): void { $this->testStream->close(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->read(1); @@ -396,7 +394,7 @@ public function testReadFromUnreadableResource(): void { $testStream = new Stream(STDOUT, false); - $this->expectException(InvalidStreamOperationException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Stream is not readable'); $testStream->read(1); @@ -413,7 +411,7 @@ public function testGetContentsAfterDetach(): void { $this->testStream->detach(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->getContents(); @@ -423,7 +421,7 @@ public function testGetContentsAfterClose(): void { $this->testStream->close(); - $this->expectException(InvalidStreamException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); $this->testStream->getContents(); @@ -433,7 +431,7 @@ public function testGetContentsFromUnreadableResource(): void { $testStream = new Stream(STDOUT, false); - $this->expectException(InvalidStreamOperationException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Stream is not readable'); $testStream->getContents(); diff --git a/tests/UploadedFileTest.php b/tests/UploadedFileTest.php index ab6f160..ba0c0f3 100644 --- a/tests/UploadedFileTest.php +++ b/tests/UploadedFileTest.php @@ -6,11 +6,10 @@ use PHPUnit\Framework\TestCase; use Psr\Http\Message\UploadedFileInterface; -use Sunrise\Http\Message\Exception\FailedUploadedFileOperationException; -use Sunrise\Http\Message\Exception\InvalidUploadedFileException; -use Sunrise\Http\Message\Exception\InvalidUploadedFileOperationException; +use Sunrise\Http\Message\Exception\RuntimeException; use Sunrise\Http\Message\Stream\FileStream; use Sunrise\Http\Message\Stream\PhpTempStream; +use Sunrise\Http\Message\Stream\TempFileStream; use Sunrise\Http\Message\Stream\TmpfileStream; use Sunrise\Http\Message\UploadedFile; @@ -63,11 +62,11 @@ public function testGetsStreamWithError(int $errorCode): void { $file = new UploadedFile(new PhpTempStream(), null, $errorCode); - $errorMessage = UploadedFile::UPLOAD_ERRORS[$errorCode] ?? UploadedFile::UNKNOWN_ERROR_TEXT; + $errorMessage = UploadedFile::UPLOAD_ERRORS[$errorCode] ?? 'Unknown error'; - $this->expectException(InvalidUploadedFileException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage(sprintf( - 'The uploaded file has no a stream due to the error #%d (%s)', + 'Uploaded file has no a stream due to the error #%d (%s)', $errorCode, $errorMessage )); @@ -82,9 +81,9 @@ public function testGetsStreamAfterMove(): void $file = new UploadedFile(new PhpTempStream()); $file->moveTo($tmpfile->getMetadata('uri')); - $this->expectException(InvalidUploadedFileException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage( - 'The uploaded file has no a stream because it was already moved' + 'Uploaded file has no a stream because it was already moved' ); $file->getStream(); @@ -93,7 +92,7 @@ public function testGetsStreamAfterMove(): void public function testMove(): void { // will be deleted after the move - $srcStream = FileStream::tempFile(); + $srcStream = new TempFileStream(); $srcStream->write('foo'); $srcPath = $srcStream->getMetadata('uri'); @@ -115,11 +114,11 @@ public function testMoveWithError(int $errorCode): void { $file = new UploadedFile(new PhpTempStream(), null, $errorCode); - $errorMessage = UploadedFile::UPLOAD_ERRORS[$errorCode] ?? UploadedFile::UNKNOWN_ERROR_TEXT; + $errorMessage = UploadedFile::UPLOAD_ERRORS[$errorCode] ?? 'Unknown error'; - $this->expectException(InvalidUploadedFileException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage(sprintf( - 'The uploaded file cannot be moved due to the error #%d (%s)', + 'Uploaded file cannot be moved due to the error #%d (%s)', $errorCode, $errorMessage )); @@ -134,9 +133,9 @@ public function testMoveAfterMove(): void $file = new UploadedFile(new PhpTempStream()); $file->moveTo($tmpfile->getMetadata('uri')); - $this->expectException(InvalidUploadedFileException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage( - 'The uploaded file cannot be moved because it was already moved' + 'Uploaded file cannot be moved because it was already moved' ); $file->moveTo('/foo'); @@ -148,9 +147,9 @@ public function testMoveUnreadableFile(): void $file = new UploadedFile(new FileStream($tmpfile->getMetadata('uri'), 'w')); - $this->expectException(InvalidUploadedFileOperationException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage( - 'The uploaded file cannot be moved because it is not readable' + 'Uploaded file cannot be moved because it is not readable' ); $file->moveTo('/foo'); @@ -160,9 +159,9 @@ public function testMoveUnwritableDirectory(): void { $file = new UploadedFile(new PhpTempStream()); - $this->expectException(FailedUploadedFileOperationException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage( - 'The uploaded file cannot be moved because ' . + 'Uploaded file cannot be moved because ' . 'the directory "/4c32dad5-181f-46b7-a86a-15568e11fdf9" is not writable' ); From 0676cc97b199ccde396ceb5beee4822b05610e82 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 22 Feb 2023 05:01:54 +0100 Subject: [PATCH 15/49] Proxy for ServerRequest with additional methods --- src/ServerRequestProxy.php | 619 +++++++++++++++++++++++++++++++++++++ 1 file changed, 619 insertions(+) create mode 100644 src/ServerRequestProxy.php diff --git a/src/ServerRequestProxy.php b/src/ServerRequestProxy.php new file mode 100644 index 0000000..a26b5b3 --- /dev/null +++ b/src/ServerRequestProxy.php @@ -0,0 +1,619 @@ + + * @copyright Copyright (c) 2018, Anatoly Nekhay + * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE + * @link https://github.com/sunrise-php/http-message + */ + +namespace Sunrise\Http\Message; + +/** + * Import classes + */ +use Fig\Http\Message\RequestMethodInterface; +use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\StreamInterface; +use Psr\Http\Message\UriInterface; +use Sunrise\Http\Message\Entity\IpAddress; + +/** + * Import functions + */ +use function explode; +use function key; +use function reset; +use function strncmp; +use function strpos; +use function strstr; +use function strtolower; +use function trim; + +/** + * ServerRequestProxy + */ +final class ServerRequestProxy implements ServerRequestInterface, RequestMethodInterface +{ + + /** + * @var ServerRequestInterface + */ + private ServerRequestInterface $request; + + /** + * Constructor of the class + * + * @param ServerRequestInterface $request + */ + public function __construct(ServerRequestInterface $request) + { + $this->request = $request; + } + + /** + * Creates the proxy from the given object + * + * @param ServerRequestInterface $request + * + * @return self + */ + public static function create(ServerRequestInterface $request): self + { + if ($request instanceof self) { + return $request; + } + + return new self($request); + } + + /** + * Checks if the request is JSON + * + * @link https://tools.ietf.org/html/rfc4627 + * + * @return bool + */ + public function isJson(): bool + { + return $this->clientProducesMediaType([ + 'application/json', + ]); + } + + /** + * Checks if the request is XML + * + * @link https://tools.ietf.org/html/rfc2376 + * + * @return bool + */ + public function isXml(): bool + { + return $this->clientProducesMediaType([ + 'application/xml', + 'text/xml', + ]); + } + + /** + * Gets the client's IP address + * + * @param array $proxyChain + * + * @return IpAddress + */ + public function getClientIpAddress(array $proxyChain = []): IpAddress + { + $env = $this->request->getServerParams(); + + /** @var string */ + $client = $env['REMOTE_ADDR'] ?? '::1'; + + /** @var list */ + $proxies = []; + + while (isset($proxyChain[$client])) { + $proxyHeader = $proxyChain[$client]; + unset($proxyChain[$client]); + + $header = $this->request->getHeaderLine($proxyHeader); + if ($header === '') { + break; + } + + $addresses = explode(',', $header); + foreach ($addresses as $i => $address) { + $addresses[$i] = trim($address); + if ($addresses[$i] === '') { + unset($addresses[$i]); + } + } + + if ($addresses === []) { + break; + } + + $client = reset($addresses); + unset($addresses[key($addresses)]); + + foreach ($addresses as $address) { + $proxies[] = $address; + } + } + + return new IpAddress($client, $proxies); + } + + /** + * Gets the client's produced media type + * + * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.1 + * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.5 + * + * @return string + */ + public function getClientProducedMediaType(): string + { + $header = $this->request->getHeaderLine('Content-Type'); + if ($header === '') { + return ''; + } + + $result = strstr($header, ';', true); + if ($result === false) { + $result = $header; + } + + $result = trim($result); + if ($result === '') { + return ''; + } + + return strtolower($result); + } + + /** + * Gets the client's consumed media types + * + * @link https://tools.ietf.org/html/rfc7231#section-1.2 + * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.1 + * @link https://tools.ietf.org/html/rfc7231#section-5.3.2 + * + * @return array> + */ + public function getClientConsumedMediaTypes(): array + { + $header = $this->request->getHeaderLine('Accept'); + if ($header === '') { + return []; + } + + $accept = header_accept_parse($header); + if ($accept === []) { + return []; + } + + $result = []; + foreach ($accept as $type => $params) { + $result[strtolower($type)] = $params; + } + + return $result; + } + + /** + * Gets the client's consumed encodings + * + * @return array> + */ + public function getClientConsumedEncodings(): array + { + $header = $this->request->getHeaderLine('Accept-Encoding'); + if ($header === '') { + return []; + } + + $accept = header_accept_parse($header); + if ($accept === []) { + return []; + } + + return $accept; + } + + /** + * Gets the client's consumed languages + * + * @return array> + */ + public function getClientConsumedLanguages(): array + { + $header = $this->request->getHeaderLine('Accept-Language'); + if ($header === '') { + return []; + } + + $accept = header_accept_parse($header); + if ($accept === []) { + return []; + } + + return $accept; + } + + /** + * Checks if the client produces one of the given media types + * + * @param list $consumes + * + * @return bool + */ + public function clientProducesMediaType(array $consumes): bool + { + if ($consumes === []) { + return true; + } + + $produced = $this->getClientProducedMediaType(); + if ($produced === '') { + return false; + } + + foreach ($consumes as $consumed) { + if ($this->equalsMediaTypes($consumed, $produced)) { + return true; + } + } + + return false; + } + + /** + * Checks if the client consumes one of the given media types + * + * @param list $produces + * + * @return bool + */ + public function clientConsumesMediaType(array $produces): bool + { + if ($produces === []) { + return true; + } + + $consumes = $this->getClientConsumedMediaTypes(); + if ($consumes === []) { + return true; + } + + if (isset($consumes['*/*'])) { + return true; + } + + foreach ($produces as $a) { + foreach ($consumes as $b => $_) { + if ($this->equalsMediaTypes($a, $b)) { + return true; + } + } + } + + return false; + } + + /** + * Checks if the given media types are equal + * + * @param string $a + * @param string $b + * + * @return bool + */ + public function equalsMediaTypes(string $a, string $b): bool + { + if ($a === $b) { + return true; + } + + $slash = strpos($a, '/'); + if ($slash === false || !isset($b[$slash]) || $b[$slash] !== '/') { + return false; + } + + $star = $slash + 1; + if (!isset($a[$star], $b[$star])) { + return false; + } + + if (!($a[$star] === '*' || $b[$star] === '*')) { + return false; + } + + return strncmp($a, $b, $star) === 0; + } + + /** + * {@inheritdoc} + */ + public function getProtocolVersion(): string + { + return $this->request->getProtocolVersion(); + } + + /** + * {@inheritdoc} + */ + public function withProtocolVersion($version) + { + $clone = clone $this; + $clone->request = $clone->request->withProtocolVersion($version); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function getHeaders(): array + { + return $this->request->getHeaders(); + } + + /** + * {@inheritdoc} + */ + public function hasHeader($name): bool + { + return $this->request->hasHeader($name); + } + + /** + * {@inheritdoc} + */ + public function getHeader($name): array + { + return $this->request->getHeader($name); + } + + /** + * {@inheritdoc} + */ + public function getHeaderLine($name): string + { + return $this->request->getHeaderLine($name); + } + + /** + * {@inheritdoc} + */ + public function withHeader($name, $value) + { + $clone = clone $this; + $clone->request = $clone->request->withHeader($name, $value); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function withAddedHeader($name, $value) + { + $clone = clone $this; + $clone->request = $clone->request->withAddedHeader($name, $value); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function withoutHeader($name) + { + $clone = clone $this; + $clone->request = $clone->request->withoutHeader($name); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function getBody(): StreamInterface + { + return $this->request->getBody(); + } + + /** + * {@inheritdoc} + */ + public function withBody(StreamInterface $body) + { + $clone = clone $this; + $clone->request = $clone->request->withBody($body); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function getMethod(): string + { + return $this->request->getMethod(); + } + + /** + * {@inheritdoc} + */ + public function withMethod($method) + { + $clone = clone $this; + $clone->request = $clone->request->withMethod($method); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function getUri(): UriInterface + { + return $this->request->getUri(); + } + + /** + * {@inheritdoc} + */ + public function withUri(UriInterface $uri, $preserveHost = false) + { + $clone = clone $this; + $clone->request = $clone->request->withUri($uri, $preserveHost); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function getRequestTarget(): string + { + return $this->request->getRequestTarget(); + } + + /** + * {@inheritdoc} + */ + public function withRequestTarget($requestTarget) + { + $clone = clone $this; + $clone->request = $clone->request->withRequestTarget($requestTarget); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function getServerParams(): array + { + return $this->request->getServerParams(); + } + + /** + * {@inheritdoc} + */ + public function getQueryParams(): array + { + return $this->request->getQueryParams(); + } + + /** + * {@inheritdoc} + */ + public function withQueryParams(array $query) + { + $clone = clone $this; + $clone->request = $clone->request->withQueryParams($query); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function getCookieParams(): array + { + return $this->request->getCookieParams(); + } + + /** + * {@inheritdoc} + */ + public function withCookieParams(array $cookies) + { + $clone = clone $this; + $clone->request = $clone->request->withCookieParams($cookies); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function getUploadedFiles(): array + { + return $this->request->getUploadedFiles(); + } + + /** + * {@inheritdoc} + */ + public function withUploadedFiles(array $uploadedFiles) + { + $clone = clone $this; + $clone->request = $clone->request->withUploadedFiles($uploadedFiles); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function getParsedBody() + { + return $this->request->getParsedBody(); + } + + /** + * {@inheritdoc} + */ + public function withParsedBody($data) + { + $clone = clone $this; + $clone->request = $clone->request->withParsedBody($data); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function getAttributes(): array + { + return $this->request->getAttributes(); + } + + /** + * {@inheritdoc} + */ + public function getAttribute($name, $default = null) + { + return $this->request->getAttribute($name, $default); + } + + /** + * {@inheritdoc} + */ + public function withAttribute($name, $value) + { + $clone = clone $this; + $clone->request = $clone->request->withAttribute($name, $value); + + return $clone; + } + + /** + * {@inheritdoc} + */ + public function withoutAttribute($name) + { + $clone = clone $this; + $clone->request = $clone->request->withoutAttribute($name); + + return $clone; + } +} From f94dcace8743128fcfe47a58083c8c7b7707812b Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:54:14 +0100 Subject: [PATCH 16/49] Update headers.md --- docs/headers.md | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/docs/headers.md b/docs/headers.md index 44f2d71..fc798de 100644 --- a/docs/headers.md +++ b/docs/headers.md @@ -163,25 +163,6 @@ $header = new ClearSiteDataHeader(['*']); $response = $response->withHeader(...$header); ``` -#### Connection - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection - -```php -use Sunrise\Http\Message\Header\ConnectionHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -// close -$header = new ConnectionHeader(ConnectionHeader::CONNECTION_CLOSE); -$response = $response->withHeader(...$header); - -// keep-alive -$header = new ConnectionHeader(ConnectionHeader::CONNECTION_KEEP_ALIVE); -$response = $response->withHeader(...$header); -``` - #### Content-Disposition > Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition @@ -407,20 +388,6 @@ $header = new ExpiresHeader(new DateTime('1 day ago')); $response = $response->withHeader(...$header); ``` -#### Keep-Alive - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive - -```php -use Sunrise\Http\Message\Header\KeepAliveHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new KeepAliveHeader(['timeout' => '5', 'max' => '1000']); -$response = $response->withHeader(...$header); -``` - #### Last-Modified > Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified From c49690bcca7a5a2363e68a045e9e238790d0596a Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:54:23 +0100 Subject: [PATCH 17/49] Delete header_accept_parse.php --- functions/header_accept_parse.php | 129 ------------------------------ 1 file changed, 129 deletions(-) delete mode 100644 functions/header_accept_parse.php diff --git a/functions/header_accept_parse.php b/functions/header_accept_parse.php deleted file mode 100644 index 66f6f85..0000000 --- a/functions/header_accept_parse.php +++ /dev/null @@ -1,129 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message; - -/** - * Import functions - */ -use function uasort; - -/** - * Parses the given Accept header - * - * @param string $header - * - * @return array> - */ -function header_accept_parse(string $header): array -{ - static $cache = []; - - if (isset($cache[$header])) { - return $cache[$header]; - } - - $cursor = -1; - $cursorInValue = true; - $cursorInParameter = false; - $cursorInParameterName = false; - $cursorInParameterValue = false; - $cursorInQuotedParameterValue = false; - - $data = []; - $valueIndex = 0; - $parameterIndex = 0; - - while (true) { - $char = $header[++$cursor] ?? null; - $prev = $header[$cursor-1] ?? null; - $next = $header[$cursor+1] ?? null; - - if ($char === null) { - break; - } - - if ($char === ' ' && !$cursorInQuotedParameterValue) { - continue; - } - - if ($char === ',' && !$cursorInQuotedParameterValue) { - $cursorInValue = true; - $cursorInParameter = false; - $cursorInParameterName = false; - $cursorInParameterValue = false; - $cursorInQuotedParameterValue = false; - $parameterIndex = 0; - $valueIndex++; - continue; - } - if ($char === ';' && !$cursorInQuotedParameterValue) { - $cursorInValue = false; - $cursorInParameter = true; - $cursorInParameterName = true; - $cursorInParameterValue = false; - $cursorInQuotedParameterValue = false; - $parameterIndex++; - continue; - } - if ($char === '=' && !$cursorInQuotedParameterValue && $cursorInParameterName) { - $cursorInValue = false; - $cursorInParameter = true; - $cursorInParameterName = false; - $cursorInParameterValue = true; - $cursorInQuotedParameterValue = false; - continue; - } - - if ($char === '"' && !$cursorInQuotedParameterValue && $cursorInParameterValue) { - $cursorInQuotedParameterValue = true; - continue; - } - if ($char === '\\' && $next === '"' && $cursorInQuotedParameterValue) { - continue; - } - if ($char === '"' && $prev !== '\\' && $cursorInQuotedParameterValue) { - $cursorInParameterValue = false; - $cursorInQuotedParameterValue = false; - continue; - } - - if ($cursorInValue) { - $data[$valueIndex][0] ??= ''; - $data[$valueIndex][0] .= $char; - continue; - } - if ($cursorInParameterName && isset($data[$valueIndex][0])) { - $data[$valueIndex][1][$parameterIndex][0] ??= ''; - $data[$valueIndex][1][$parameterIndex][0] .= $char; - continue; - } - if ($cursorInParameterValue && isset($data[$valueIndex][1][$parameterIndex][0])) { - $data[$valueIndex][1][$parameterIndex][1] ??= ''; - $data[$valueIndex][1][$parameterIndex][1] .= $char; - continue; - } - } - - $result = []; - foreach ($data as $item) { - $result[$item[0]] = []; - if (isset($item[1])) { - foreach ($item[1] as $param) { - $result[$item[0]][$param[0]] = $param[1] ?? ''; - } - } - } - - uasort($result, fn(array $a, array $b): int => ($b['q'] ?? 1) <=> ($a['q'] ?? 1)); - - return $cache[$header] = $result; -} From 661cd86915f1648e0538b2ca0e3a39a1b13a84b1 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:54:39 +0100 Subject: [PATCH 18/49] New function for accept-like headers parse --- composer.json | 2 +- functions/header_accept_like_parse.php | 201 +++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 functions/header_accept_like_parse.php diff --git a/composer.json b/composer.json index 5e66a80..3ba002d 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ }, "autoload": { "files": [ - "functions/header_accept_parse.php", + "functions/header_accept_like_parse.php", "functions/server_request_files.php", "functions/server_request_headers.php", "functions/server_request_method.php", diff --git a/functions/header_accept_like_parse.php b/functions/header_accept_like_parse.php new file mode 100644 index 0000000..8e5b15c --- /dev/null +++ b/functions/header_accept_like_parse.php @@ -0,0 +1,201 @@ + + * @copyright Copyright (c) 2018, Anatoly Nekhay + * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE + * @link https://github.com/sunrise-php/http-message + */ + +namespace Sunrise\Http\Message; + +/** + * Import functions + */ +use function uasort; + +/** + * Parses the given accept-like header + * + * Returns null if the header isn't valid. + * + * @param string $header + * + * @return ?array> + */ +function header_accept_like_parse(string $header): ?array +{ + // OWS according to RFC-7230 + static $ows = [ + "\x09" => 1, "\x20" => 1, + ]; + + // token according to RFC-7230 + static $token = [ + "\x21" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1, "\x2a" => 1, "\x2b" => 1, + "\x2d" => 1, "\x2e" => 1, "\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, + "\x36" => 1, "\x37" => 1, "\x38" => 1, "\x39" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, + "\x45" => 1, "\x46" => 1, "\x47" => 1, "\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, + "\x4d" => 1, "\x4e" => 1, "\x4f" => 1, "\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, + "\x55" => 1, "\x56" => 1, "\x57" => 1, "\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5e" => 1, "\x5f" => 1, + "\x60" => 1, "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1, + "\x68" => 1, "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1, + "\x70" => 1, "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1, + "\x78" => 1, "\x79" => 1, "\x7a" => 1, "\x7c" => 1, "\x7e" => 1, + ]; + + // quoted-string according to RFC-7230 + static $quotedString = [ + "\x09" => 1, "\x20" => 1, "\x21" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1, + "\x28" => 1, "\x29" => 1, "\x2a" => 1, "\x2b" => 1, "\x2c" => 1, "\x2d" => 1, "\x2e" => 1, "\x2f" => 1, + "\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, "\x36" => 1, "\x37" => 1, + "\x38" => 1, "\x39" => 1, "\x3a" => 1, "\x3b" => 1, "\x3c" => 1, "\x3d" => 1, "\x3e" => 1, "\x3f" => 1, + "\x40" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, "\x45" => 1, "\x46" => 1, "\x47" => 1, + "\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, "\x4d" => 1, "\x4e" => 1, "\x4f" => 1, + "\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, "\x55" => 1, "\x56" => 1, "\x57" => 1, + "\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5b" => 1, "\x5d" => 1, "\x5e" => 1, "\x5f" => 1, "\x60" => 1, + "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1, "\x68" => 1, + "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1, "\x70" => 1, + "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1, "\x78" => 1, + "\x79" => 1, "\x7a" => 1, "\x7b" => 1, "\x7c" => 1, "\x7d" => 1, "\x7e" => 1, "\x80" => 1, "\x81" => 1, + "\x82" => 1, "\x83" => 1, "\x84" => 1, "\x85" => 1, "\x86" => 1, "\x87" => 1, "\x88" => 1, "\x89" => 1, + "\x8a" => 1, "\x8b" => 1, "\x8c" => 1, "\x8d" => 1, "\x8e" => 1, "\x8f" => 1, "\x90" => 1, "\x91" => 1, + "\x92" => 1, "\x93" => 1, "\x94" => 1, "\x95" => 1, "\x96" => 1, "\x97" => 1, "\x98" => 1, "\x99" => 1, + "\x9a" => 1, "\x9b" => 1, "\x9c" => 1, "\x9d" => 1, "\x9e" => 1, "\x9f" => 1, "\xa0" => 1, "\xa1" => 1, + "\xa2" => 1, "\xa3" => 1, "\xa4" => 1, "\xa5" => 1, "\xa6" => 1, "\xa7" => 1, "\xa8" => 1, "\xa9" => 1, + "\xaa" => 1, "\xab" => 1, "\xac" => 1, "\xad" => 1, "\xae" => 1, "\xaf" => 1, "\xb0" => 1, "\xb1" => 1, + "\xb2" => 1, "\xb3" => 1, "\xb4" => 1, "\xb5" => 1, "\xb6" => 1, "\xb7" => 1, "\xb8" => 1, "\xb9" => 1, + "\xba" => 1, "\xbb" => 1, "\xbc" => 1, "\xbd" => 1, "\xbe" => 1, "\xbf" => 1, "\xc0" => 1, "\xc1" => 1, + "\xc2" => 1, "\xc3" => 1, "\xc4" => 1, "\xc5" => 1, "\xc6" => 1, "\xc7" => 1, "\xc8" => 1, "\xc9" => 1, + "\xca" => 1, "\xcb" => 1, "\xcc" => 1, "\xcd" => 1, "\xce" => 1, "\xcf" => 1, "\xd0" => 1, "\xd1" => 1, + "\xd2" => 1, "\xd3" => 1, "\xd4" => 1, "\xd5" => 1, "\xd6" => 1, "\xd7" => 1, "\xd8" => 1, "\xd9" => 1, + "\xda" => 1, "\xdb" => 1, "\xdc" => 1, "\xdd" => 1, "\xde" => 1, "\xdf" => 1, "\xe0" => 1, "\xe1" => 1, + "\xe2" => 1, "\xe3" => 1, "\xe4" => 1, "\xe5" => 1, "\xe6" => 1, "\xe7" => 1, "\xe8" => 1, "\xe9" => 1, + "\xea" => 1, "\xeb" => 1, "\xec" => 1, "\xed" => 1, "\xee" => 1, "\xef" => 1, "\xf0" => 1, "\xf1" => 1, + "\xf2" => 1, "\xf3" => 1, "\xf4" => 1, "\xf5" => 1, "\xf6" => 1, "\xf7" => 1, "\xf8" => 1, "\xf9" => 1, + "\xfa" => 1, "\xfb" => 1, "\xfc" => 1, "\xfd" => 1, "\xfe" => 1, "\xff" => 1, + ]; + + $offset = -1; + + $inToken = true; + $inParamName = false; + $inParamValue = false; + $inQuotes = false; + + $data = []; + + $i = 0; + $p = 0; + + while (true) { + $char = $header[++$offset] ?? null; + + if (!isset($char)) { + break; + } + + $prev = $header[$offset-1] ?? null; + $next = $header[$offset+1] ?? null; + + if ($char === ',' && !$inQuotes) { + $inToken = true; + $inParamName = false; + $inParamValue = false; + $i++; + $p = 0; + continue; + } + if ($char === ';' && !$inQuotes) { + $inToken = false; + $inParamName = true; + $inParamValue = false; + $p++; + continue; + } + if ($char === '=' && !$inQuotes) { + $inToken = false; + $inParamName = false; + $inParamValue = true; + continue; + } + + // ignoring whitespaces around tokens... + if (isset($ows[$char]) && !$inQuotes) { + // en-GB[ ], ... + // ~~~~~~^~~~~~~ + if ($inToken && isset($data[$i][0])) { + $inToken = false; + } + // en-GB; q[ ]= 0.8, ... + // ~~~~~~~~~^~~~~~~~~~~~ + if ($inParamName && isset($data[$i][1][$p][0])) { + $inParamName = false; + } + // en-GB; q = 0.8[ ], ... + // ~~~~~~~~~~~~~~~^~~~~~~ + if ($inParamValue && isset($data[$i][1][$p][1])) { + $inParamValue = false; + } + + continue; + } + + // ignoring backslashes before double quotes in the quoted parameter value... + if (($char . $next) === '\"' && $inQuotes) { + continue; + } + + if ($char === '"' && $inParamValue && !$inQuotes && !isset($data[$i][1][$p][1])) { + $inQuotes = true; + continue; + } + if ($char === '"' && $prev !== '\\' && $inQuotes) { + $inParamValue = false; + $inQuotes = false; + continue; + } + + // [en-GB]; q=0.8, ... + // ~^^^^^~~~~~~~~~~~~~ + if ($inToken && (isset($token[$char]) || $char === '/')) { + $data[$i][0] ??= ''; + $data[$i][0] .= $char; + continue; + } + // en-GB; [q]=0.8, ... + // ~~~~~~~~^~~~~~~~~~~ + if ($inParamName && isset($token[$char]) && isset($data[$i][0])) { + $data[$i][1][$p][0] ??= ''; + $data[$i][1][$p][0] .= $char; + continue; + } + // en-GB; q=[0.8], ... + // ~~~~~~~~~~^^^~~~~~~ + // phpcs:ignore Generic.Files.LineLength + if ($inParamValue && (isset($token[$char]) || ($inQuotes && (isset($quotedString[$char]) || ($prev . $char) === '\"'))) && isset($data[$i][1][$p][0])) { + $data[$i][1][$p][1] ??= ''; + $data[$i][1][$p][1] .= $char; + continue; + } + + // the header is invalid... + return null; + } + + $result = []; + foreach ($data as $item) { + $result[$item[0]] = []; + if (isset($item[1])) { + foreach ($item[1] as $param) { + $result[$item[0]][$param[0]] = $param[1] ?? ''; + } + } + } + + uasort($result, fn(array $a, array $b): int => ($b['q'] ?? 1) <=> ($a['q'] ?? 1)); + + return $result; +} From eff69b05a9250f91c545f4b6755d1ada1291ef6c Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:54:52 +0100 Subject: [PATCH 19/49] Deleted --- src/Enum/AuthenticationScheme.php | 31 ------------------- src/Enum/CookieSameSite.php | 51 ------------------------------- src/Enum/Encoding.php | 24 --------------- src/Enum/WarningCode.php | 28 ----------------- 4 files changed, 134 deletions(-) delete mode 100644 src/Enum/AuthenticationScheme.php delete mode 100644 src/Enum/CookieSameSite.php delete mode 100644 src/Enum/Encoding.php delete mode 100644 src/Enum/WarningCode.php diff --git a/src/Enum/AuthenticationScheme.php b/src/Enum/AuthenticationScheme.php deleted file mode 100644 index c9976ec..0000000 --- a/src/Enum/AuthenticationScheme.php +++ /dev/null @@ -1,31 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Enum; - -/** - * HTTP authentication schemes - * - * @link https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml - */ -final class AuthenticationScheme -{ - public const BASIC = 'Basic'; - public const BEARER = 'Bearer'; - public const DIGEST = 'Digest'; - public const HOBA = 'HOBA'; - public const MUTUAL = 'Mutual'; - public const NEGOTIATE = 'Negotiate'; - public const OAUTH = 'OAuth'; - public const SCRAM_SHA_1 = 'SCRAM-SHA-1'; - public const SCRAM_SHA_256 = 'SCRAM-SHA-256'; - public const VAPID = 'vapid'; -} diff --git a/src/Enum/CookieSameSite.php b/src/Enum/CookieSameSite.php deleted file mode 100644 index 1582fe1..0000000 --- a/src/Enum/CookieSameSite.php +++ /dev/null @@ -1,51 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Enum; - -/** - * CookieSameSite - * - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite - */ -final class CookieSameSite -{ - - /** - * Cookies are not sent on normal cross-site subrequests, but are - * sent when a user is navigating to the origin site. - * - * This is the default cookie value if SameSite has not been - * explicitly specified in recent browser versions. - * - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax - */ - public const LAX = 'Lax'; - - /** - * Cookies will only be sent in a first-party context and not be - * sent along with requests initiated by third party websites. - * - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#strict - */ - public const STRICT = 'Strict'; - - /** - * Cookies will be sent in all contexts, i.e. in responses to both - * first-party and cross-site requests. - * - * If SameSite=None is set, the cookie Secure attribute must also - * be set (or the cookie will be blocked). - * - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#none - */ - public const NONE = 'None'; -} diff --git a/src/Enum/Encoding.php b/src/Enum/Encoding.php deleted file mode 100644 index f127ce0..0000000 --- a/src/Enum/Encoding.php +++ /dev/null @@ -1,24 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Enum; - -/** - * Encoding - */ -final class Encoding -{ - public const BR = 'br'; - public const CHUNKED = 'chunked'; - public const COMPRESS = 'compress'; - public const DEFLATE = 'deflate'; - public const GZIP = 'gzip'; -} diff --git a/src/Enum/WarningCode.php b/src/Enum/WarningCode.php deleted file mode 100644 index c49388f..0000000 --- a/src/Enum/WarningCode.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Enum; - -/** - * HTTP warning codes - * - * @link https://www.iana.org/assignments/http-warn-codes/http-warn-codes.xhtml - */ -final class WarningCode -{ - public const RESPONSE_IS_STALE = 110; - public const REVALIDATION_FAILED = 111; - public const DISCONNECTED_OPERATION = 112; - public const HEURISTIC_EXPIRATION = 113; - public const MISCELLANEOUS_WARNING = 199; - public const TRANSFORMATION_APPLIED = 214; - public const MISCELLANEOUS_PERSISTENT_WARNING = 299; -} From 6e8ef37ad1e9af980635391904a15cb96787864d Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:55:02 +0100 Subject: [PATCH 20/49] cs --- functions/server_request_files.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/functions/server_request_files.php b/functions/server_request_files.php index 08c1a39..3e9763d 100644 --- a/functions/server_request_files.php +++ b/functions/server_request_files.php @@ -24,6 +24,7 @@ /** * Import constants */ +use const UPLOAD_ERR_OK; use const UPLOAD_ERR_NO_FILE; /** @@ -44,10 +45,16 @@ function server_request_files(?array $files = null): array { $files ??= $_FILES; - $walker = function ($path, $size, $error, $name, $type) use (&$walker) { + $walker = function ( + $path, + $size, + $error, + $name, + $type + ) use (&$walker) { if (!is_array($path)) { - // What if the path is an empty string? - $stream = new FileStream($path, 'rb'); + // It makes no sense to create a stream if the file has not been successfully uploaded. + $stream = UPLOAD_ERR_OK <> $error ? null : new FileStream($path, 'rb'); return new UploadedFile( $stream, From 50c28881200afb43beb863400f07244ece05ff47 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:55:19 +0100 Subject: [PATCH 21/49] Delete KeepAliveHeader.php --- src/Header/KeepAliveHeader.php | 81 ---------------------------------- 1 file changed, 81 deletions(-) delete mode 100644 src/Header/KeepAliveHeader.php diff --git a/src/Header/KeepAliveHeader.php b/src/Header/KeepAliveHeader.php deleted file mode 100644 index 7d3a430..0000000 --- a/src/Header/KeepAliveHeader.php +++ /dev/null @@ -1,81 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; -use function sprintf; - -/** - * @link https://tools.ietf.org/html/rfc2068#section-19.7.1.1 - */ -class KeepAliveHeader extends Header -{ - - /** - * @var array - */ - private array $parameters; - - /** - * Constructor of the class - * - * @param array $parameters - * - * @throws InvalidHeaderException - * If the parameters aren't valid. - */ - public function __construct(array $parameters = []) - { - // validate and normalize the parameters... - $parameters = $this->validateParameters($parameters); - - $this->parameters = $parameters; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Keep-Alive'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - $segments = []; - foreach ($this->parameters as $name => $value) { - // the construction isn't valid... - if ($value === '') { - $segments[] = $name; - continue; - } - - $format = $this->isToken($value) ? '%s=%s' : '%s="%s"'; - - $segments[] = sprintf($format, $name, $value); - } - - return implode(', ', $segments); - } -} From fb04adbf010df02a0e8b69a77a5e2a0097b61e59 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:55:31 +0100 Subject: [PATCH 22/49] cs --- src/Header/CacheControlHeader.php | 12 ++- src/Header/ContentDispositionHeader.php | 1 - src/Header/ContentEncodingHeader.php | 27 +++---- src/Header/ContentLanguageHeader.php | 2 +- src/Header/ContentSecurityPolicyHeader.php | 1 - src/Header/ContentTypeHeader.php | 1 - src/Header/LinkHeader.php | 1 - src/Header/SetCookieHeader.php | 91 ++++++---------------- src/Header/TransferEncodingHeader.php | 26 ++----- src/Header/WWWAuthenticateHeader.php | 68 ++++------------ src/Header/WarningHeader.php | 43 +++------- 11 files changed, 76 insertions(+), 197 deletions(-) diff --git a/src/Header/CacheControlHeader.php b/src/Header/CacheControlHeader.php index f87e227..c7ed9c5 100644 --- a/src/Header/CacheControlHeader.php +++ b/src/Header/CacheControlHeader.php @@ -44,7 +44,6 @@ class CacheControlHeader extends Header */ public function __construct(array $parameters) { - // validate and normalize the parameters... $parameters = $this->validateParameters($parameters); $this->parameters = $parameters; @@ -65,15 +64,20 @@ public function getFieldValue(): string { $segments = []; foreach ($this->parameters as $name => $value) { - // the construction isn't valid... + // e.g., no-cache if ($value === '') { $segments[] = $name; continue; } - $format = $this->isToken($value) ? '%s=%s' : '%s="%s"'; + // e.g., max-age=604800 + if ($this->isToken($value)) { + $segments[] = sprintf('%s=%s', $name, $value); + continue; + } - $segments[] = sprintf($format, $name, $value); + // e.g., community="UCI" + $segments[] = sprintf('%s="%s"', $name, $value); } return implode(', ', $segments); diff --git a/src/Header/ContentDispositionHeader.php b/src/Header/ContentDispositionHeader.php index 4ddf063..bdbe9e6 100644 --- a/src/Header/ContentDispositionHeader.php +++ b/src/Header/ContentDispositionHeader.php @@ -52,7 +52,6 @@ public function __construct(string $type, array $parameters = []) { $this->validateToken($type); - // validate and normalize the parameters,,, $parameters = $this->validateParameters($parameters); $this->type = $type; diff --git a/src/Header/ContentEncodingHeader.php b/src/Header/ContentEncodingHeader.php index 2b3e958..f397369 100644 --- a/src/Header/ContentEncodingHeader.php +++ b/src/Header/ContentEncodingHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Enum\Encoding; +use Sunrise\Http\Message\Dictionary\Encoding; use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; @@ -30,24 +30,15 @@ class ContentEncodingHeader extends Header { /** - * @deprecated Use the {@see Encoding} enum. - */ - public const BR = Encoding::BR; - - /** - * @deprecated Use the {@see Encoding} enum. - */ - public const COMPRESS = Encoding::COMPRESS; - - /** - * @deprecated Use the {@see Encoding} enum. - */ - public const DEFLATE = Encoding::DEFLATE; - - /** - * @deprecated Use the {@see Encoding} enum. + * HTTP Content Encodings + * + * @link https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding */ - public const GZIP = Encoding::GZIP; + public const HTTP_CONTENT_ENCODING_AES128GCM = 'aes128gcm'; + public const HTTP_CONTENT_ENCODING_BR = 'br'; + public const HTTP_CONTENT_ENCODING_COMPRESS = 'compress'; + public const HTTP_CONTENT_ENCODING_DEFLATE = 'deflate'; + public const HTTP_CONTENT_ENCODING_GZIP = 'gzip'; /** * @var list diff --git a/src/Header/ContentLanguageHeader.php b/src/Header/ContentLanguageHeader.php index 066f3d4..4c38027 100644 --- a/src/Header/ContentLanguageHeader.php +++ b/src/Header/ContentLanguageHeader.php @@ -35,7 +35,7 @@ class ContentLanguageHeader extends Header * * @var string */ - public const RFC2616_LANGUAGE_TAG = '/^[a-zA-Z]{1,8}(?:\-[a-zA-Z]{1,8})?$/'; + public const RFC2616_LANGUAGE_TAG = '/^[a-zA-Z]{1,8}(?:-[a-zA-Z]{1,8})?$/'; /** * @var list diff --git a/src/Header/ContentSecurityPolicyHeader.php b/src/Header/ContentSecurityPolicyHeader.php index ec59bd3..ec2556f 100644 --- a/src/Header/ContentSecurityPolicyHeader.php +++ b/src/Header/ContentSecurityPolicyHeader.php @@ -62,7 +62,6 @@ class ContentSecurityPolicyHeader extends Header */ public function __construct(array $parameters = []) { - // validate and normalize the parameters... $parameters = $this->validateParametersByRegex( $parameters, self::VALID_DIRECTIVE_NAME, diff --git a/src/Header/ContentTypeHeader.php b/src/Header/ContentTypeHeader.php index a4b2505..42c6c43 100644 --- a/src/Header/ContentTypeHeader.php +++ b/src/Header/ContentTypeHeader.php @@ -61,7 +61,6 @@ public function __construct(string $type, array $parameters = []) { $this->validateValueByRegex(self::RFC6838_CONTENT_TYPE, $type); - // validate and normalize the parameters... $parameters = $this->validateParameters($parameters); $this->type = $type; diff --git a/src/Header/LinkHeader.php b/src/Header/LinkHeader.php index 5cb0c3c..924696a 100644 --- a/src/Header/LinkHeader.php +++ b/src/Header/LinkHeader.php @@ -55,7 +55,6 @@ class LinkHeader extends Header */ public function __construct($uri, array $parameters = []) { - // validate and normalize the parameters... $parameters = $this->validateParameters($parameters); $this->uri = Uri::create($uri); diff --git a/src/Header/SetCookieHeader.php b/src/Header/SetCookieHeader.php index ee17a29..4608d99 100644 --- a/src/Header/SetCookieHeader.php +++ b/src/Header/SetCookieHeader.php @@ -16,7 +16,6 @@ */ use DateTimeImmutable; use DateTimeInterface; -use Sunrise\Http\Message\Enum\CookieSameSite; use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; @@ -37,23 +36,13 @@ class SetCookieHeader extends Header { /** - * Default cookie options + * Acceptable the SameSite attribute values * - * @var array{ - * path?: ?string, - * domain?: ?string, - * secure?: ?bool, - * httpOnly?: ?bool, - * sameSite?: ?string - * } + * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite */ - public const DEFAULT_OPTIONS = [ - self::OPTION_KEY_PATH => '/', - self::OPTION_KEY_DOMAIN => null, - self::OPTION_KEY_SECURE => null, - self::OPTION_KEY_HTTP_ONLY => true, - self::OPTION_KEY_SAME_SITE => CookieSameSite::LAX, - ]; + public const SAME_SITE_LAX = 'Lax'; + public const SAME_SITE_STRICT = 'Strict'; + public const SAME_SITE_NONE = 'None'; /** * Cookie option keys @@ -62,40 +51,20 @@ class SetCookieHeader extends Header public const OPTION_KEY_DOMAIN = 'domain'; public const OPTION_KEY_SECURE = 'secure'; public const OPTION_KEY_HTTP_ONLY = 'httpOnly'; - public const OPTION_KEY_SAME_SITE = 'sameSite'; - - /** - * @deprecated Use the {@see CookieSameSite} enum. - */ - public const SAME_SITE_LAX = CookieSameSite::LAX; - - /** - * @deprecated Use the {@see CookieSameSite} enum. - */ - public const SAME_SITE_STRICT = CookieSameSite::STRICT; + public const OPTION_KEY_SAMESITE = 'sameSite'; /** - * @deprecated Use the {@see CookieSameSite} enum. - */ - public const SAME_SITE_NONE = CookieSameSite::NONE; - - /** - * @deprecated Use the {@see SetCookieHeader::OPTION_KEY_SAME_SITE} constant. - */ - public const OPTION_KEY_SAMESITE = self::OPTION_KEY_SAME_SITE; - - /** - * @var ?array{ - * path?: ?string, - * domain?: ?string, - * secure?: ?bool, - * httpOnly?: ?bool, - * sameSite?: ?string - * } + * Default cookie options * - * @deprecated Use the {@see SetCookieHeader::DEFAULT_OPTIONS} constant. + * @var array{path?: ?string, domain?: ?string, secure?: ?bool, httpOnly?: ?bool, sameSite?: ?string} */ - protected static ?array $defaultOptions = null; + protected static array $defaultOptions = [ + self::OPTION_KEY_PATH => '/', + self::OPTION_KEY_DOMAIN => null, + self::OPTION_KEY_SECURE => null, + self::OPTION_KEY_HTTP_ONLY => true, + self::OPTION_KEY_SAMESITE => self::SAME_SITE_LAX, + ]; /** * The cookie name @@ -112,7 +81,7 @@ class SetCookieHeader extends Header private string $value; /** - * The cookie expiration date + * The cookie's expiration date * * @var DateTimeInterface|null */ @@ -121,13 +90,7 @@ class SetCookieHeader extends Header /** * The cookie options * - * @var array{ - * path?: ?string, - * domain?: ?string, - * secure?: ?bool, - * httpOnly?: ?bool, - * sameSite?: ?string - * } + * @var array{path?: ?string, domain?: ?string, secure?: ?bool, httpOnly?: ?bool, sameSite?: ?string} */ private array $options; @@ -137,13 +100,7 @@ class SetCookieHeader extends Header * @param string $name * @param string $value * @param DateTimeInterface|null $expires - * @param array{ - * path?: ?string, - * domain?: ?string, - * secure?: ?bool, - * httpOnly?: ?bool, - * sameSite?: ?string - * } $options + * @param array{path?: ?string, domain?: ?string, secure?: ?bool, httpOnly?: ?bool, sameSite?: ?string} $options * * @throws InvalidHeaderException * If one of the arguments isn't valid. @@ -166,10 +123,10 @@ public function __construct(string $name, string $value, ?DateTimeInterface $exp ); } - if (isset($options[self::OPTION_KEY_SAME_SITE])) { + if (isset($options[self::OPTION_KEY_SAMESITE])) { $this->validateCookieOption( - self::OPTION_KEY_SAME_SITE, - $options[self::OPTION_KEY_SAME_SITE] + self::OPTION_KEY_SAMESITE, + $options[self::OPTION_KEY_SAMESITE] ); } @@ -178,7 +135,7 @@ public function __construct(string $name, string $value, ?DateTimeInterface $exp $expires = new DateTimeImmutable('1 year ago'); } - $options += (static::$defaultOptions ?? static::DEFAULT_OPTIONS); + $options += static::$defaultOptions; $this->name = $name; $this->value = $value; @@ -224,8 +181,8 @@ public function getFieldValue(): string $result .= '; HttpOnly'; } - if (isset($this->options[self::OPTION_KEY_SAME_SITE])) { - $result .= '; SameSite=' . $this->options[self::OPTION_KEY_SAME_SITE]; + if (isset($this->options[self::OPTION_KEY_SAMESITE])) { + $result .= '; SameSite=' . $this->options[self::OPTION_KEY_SAMESITE]; } return $result; diff --git a/src/Header/TransferEncodingHeader.php b/src/Header/TransferEncodingHeader.php index 16be643..6800084 100644 --- a/src/Header/TransferEncodingHeader.php +++ b/src/Header/TransferEncodingHeader.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Enum\Encoding; +use Sunrise\Http\Message\Dictionary\Encoding; use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; @@ -30,24 +30,14 @@ class TransferEncodingHeader extends Header { /** - * @deprecated Use the {@see Encoding} enum. - */ - public const CHUNKED = Encoding::CHUNKED; - - /** - * @deprecated Use the {@see Encoding} enum. - */ - public const COMPRESS = Encoding::COMPRESS; - - /** - * @deprecated Use the {@see Encoding} enum. - */ - public const DEFLATE = Encoding::DEFLATE; - - /** - * @deprecated Use the {@see Encoding} enum. + * HTTP Transfer Encodings + * + * @link https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#transfer-coding */ - public const GZIP = Encoding::GZIP; + public const HTTP_TRANSFER_ENCODING_CHUNKED = 'chunked'; + public const HTTP_TRANSFER_ENCODING_COMPRESS = 'compress'; + public const HTTP_TRANSFER_ENCODING_DEFLATE = 'deflate'; + public const HTTP_TRANSFER_ENCODING_GZIP = 'gzip'; /** * @var list diff --git a/src/Header/WWWAuthenticateHeader.php b/src/Header/WWWAuthenticateHeader.php index ced02b1..ed9f67a 100644 --- a/src/Header/WWWAuthenticateHeader.php +++ b/src/Header/WWWAuthenticateHeader.php @@ -14,7 +14,6 @@ /** * Import classes */ -use Sunrise\Http\Message\Enum\AuthenticationScheme; use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; @@ -31,54 +30,20 @@ class WWWAuthenticateHeader extends Header { /** - * @deprecated Use the {@see AuthenticationScheme} enum. - */ - public const HTTP_AUTHENTICATE_SCHEME_BASIC = AuthenticationScheme::BASIC; - - /** - * @deprecated Use the {@see AuthenticationScheme} enum. - */ - public const HTTP_AUTHENTICATE_SCHEME_BEARER = AuthenticationScheme::BEARER; - - /** - * @deprecated Use the {@see AuthenticationScheme} enum. - */ - public const HTTP_AUTHENTICATE_SCHEME_DIGEST = AuthenticationScheme::DIGEST; - - /** - * @deprecated Use the {@see AuthenticationScheme} enum. - */ - public const HTTP_AUTHENTICATE_SCHEME_HOBA = AuthenticationScheme::HOBA; - - /** - * @deprecated Use the {@see AuthenticationScheme} enum. - */ - public const HTTP_AUTHENTICATE_SCHEME_MUTUAL = AuthenticationScheme::MUTUAL; - - /** - * @deprecated Use the {@see AuthenticationScheme} enum. - */ - public const HTTP_AUTHENTICATE_SCHEME_NEGOTIATE = AuthenticationScheme::NEGOTIATE; - - /** - * @deprecated Use the {@see AuthenticationScheme} enum. - */ - public const HTTP_AUTHENTICATE_SCHEME_OAUTH = AuthenticationScheme::OAUTH; - - /** - * @deprecated Use the {@see AuthenticationScheme} enum. - */ - public const HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_1 = AuthenticationScheme::SCRAM_SHA_1; - - /** - * @deprecated Use the {@see AuthenticationScheme} enum. - */ - public const HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_256 = AuthenticationScheme::SCRAM_SHA_256; - - /** - * @deprecated Use the {@see AuthenticationScheme} enum. + * HTTP Authentication Schemes + * + * @link https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml */ - public const HTTP_AUTHENTICATE_SCHEME_VAPID = AuthenticationScheme::VAPID; + public const HTTP_AUTHENTICATE_SCHEME_BASIC = 'Basic'; + public const HTTP_AUTHENTICATE_SCHEME_BEARER = 'Bearer'; + public const HTTP_AUTHENTICATE_SCHEME_DIGEST = 'Digest'; + public const HTTP_AUTHENTICATE_SCHEME_HOBA = 'HOBA'; + public const HTTP_AUTHENTICATE_SCHEME_MUTUAL = 'Mutual'; + public const HTTP_AUTHENTICATE_SCHEME_NEGOTIATE = 'Negotiate'; + public const HTTP_AUTHENTICATE_SCHEME_OAUTH = 'OAuth'; + public const HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_1 = 'SCRAM-SHA-1'; + public const HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_256 = 'SCRAM-SHA-256'; + public const HTTP_AUTHENTICATE_SCHEME_VAPID = 'vapid'; /** * @var string @@ -104,7 +69,6 @@ public function __construct(string $scheme, array $parameters = []) { $this->validateToken($scheme); - // validate and normalize the parameters... $parameters = $this->validateParameters($parameters); $this->scheme = $scheme; @@ -124,7 +88,7 @@ public function getFieldName(): string */ public function getFieldValue(): string { - $v = $this->scheme; + $result = $this->scheme; $challenge = []; foreach ($this->parameters as $name => $value) { @@ -132,9 +96,9 @@ public function getFieldValue(): string } if (!empty($challenge)) { - $v .= implode(',', $challenge); + $result .= implode(',', $challenge); } - return $v; + return $result; } } diff --git a/src/Header/WarningHeader.php b/src/Header/WarningHeader.php index de36b05..40573af 100644 --- a/src/Header/WarningHeader.php +++ b/src/Header/WarningHeader.php @@ -15,7 +15,6 @@ * Import classes */ use DateTimeInterface; -use Sunrise\Http\Message\Enum\WarningCode; use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; @@ -31,39 +30,17 @@ class WarningHeader extends Header { /** - * @deprecated Use the {@see WarningCode} enum. - */ - public const HTTP_WARNING_CODE_RESPONSE_IS_STALE = WarningCode::RESPONSE_IS_STALE; - - /** - * @deprecated Use the {@see WarningCode} enum. - */ - public const HTTP_WARNING_CODE_REVALIDATION_FAILED = WarningCode::REVALIDATION_FAILED; - - /** - * @deprecated Use the {@see WarningCode} enum. - */ - public const HTTP_WARNING_CODE_DISCONNECTED_OPERATION = WarningCode::DISCONNECTED_OPERATION; - - /** - * @deprecated Use the {@see WarningCode} enum. - */ - public const HTTP_WARNING_CODE_HEURISTIC_EXPIRATION = WarningCode::HEURISTIC_EXPIRATION; - - /** - * @deprecated Use the {@see WarningCode} enum. - */ - public const HTTP_WARNING_CODE_MISCELLANEOUS_WARNING = WarningCode::MISCELLANEOUS_WARNING; - - /** - * @deprecated Use the {@see WarningCode} enum. - */ - public const HTTP_WARNING_CODE_TRANSFORMATION_APPLIED = WarningCode::TRANSFORMATION_APPLIED; - - /** - * @deprecated Use the {@see WarningCode} enum. + * HTTP Warning Codes + * + * @link https://www.iana.org/assignments/http-warn-codes/http-warn-codes.xhtml */ - public const HTTP_WARNING_CODE_MISCELLANEOUS_PERSISTENT_WARNING = WarningCode::MISCELLANEOUS_PERSISTENT_WARNING; + public const HTTP_WARNING_CODE_RESPONSE_IS_STALE = 110; + public const HTTP_WARNING_CODE_REVALIDATION_FAILED = 111; + public const HTTP_WARNING_CODE_DISCONNECTED_OPERATION = 112; + public const HTTP_WARNING_CODE_HEURISTIC_EXPIRATION = 113; + public const HTTP_WARNING_CODE_MISCELLANEOUS_WARNING = 199; + public const HTTP_WARNING_CODE_TRANSFORMATION_APPLIED = 214; + public const HTTP_WARNING_CODE_MISCELLANEOUS_PERSISTENT_WARNING = 299; /** * @var int From 9f6324e3c9a12c5bc5004d08bc390f87375085f4 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:55:43 +0100 Subject: [PATCH 23/49] Improve code --- src/Response.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Response.php b/src/Response.php index a99e31d..33ce462 100644 --- a/src/Response.php +++ b/src/Response.php @@ -281,6 +281,10 @@ private function validateStatusCode($statusCode): void */ private function validateReasonPhrase($reasonPhrase): void { + if ('' === $reasonPhrase) { + return; + } + if (!is_string($reasonPhrase)) { throw new InvalidArgumentException('HTTP reason phrase must be a string'); } From 3883605c8b635e989037f44dad7440a0bd8d3d14 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:55:53 +0100 Subject: [PATCH 24/49] cs --- src/Response/HtmlResponse.php | 4 ++-- src/Response/JsonResponse.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Response/HtmlResponse.php b/src/Response/HtmlResponse.php index 955967d..e5a4a10 100644 --- a/src/Response/HtmlResponse.php +++ b/src/Response/HtmlResponse.php @@ -44,9 +44,9 @@ public function __construct(int $statusCode, $html) { parent::__construct($statusCode); - $this->setBody($this->createBody($html)); - $this->setHeader('Content-Type', 'text/html; charset=utf-8'); + + $this->setBody($this->createBody($html)); } /** diff --git a/src/Response/JsonResponse.php b/src/Response/JsonResponse.php index fd5c227..8d9c87f 100644 --- a/src/Response/JsonResponse.php +++ b/src/Response/JsonResponse.php @@ -50,9 +50,9 @@ public function __construct(int $statusCode, $data, int $flags = 0, int $depth = { parent::__construct($statusCode); - $this->setBody($this->createBody($data, $flags, $depth)); - $this->setHeader('Content-Type', 'application/json; charset=utf-8'); + + $this->setBody($this->createBody($data, $flags, $depth)); } /** From 4bcc760eebd4894923617fd9726a076e59dbea8a Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:56:19 +0100 Subject: [PATCH 25/49] cs --- src/ServerRequestFactory.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index 9d33a51..a0483d8 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -47,11 +47,11 @@ public static function fromGlobals( ?array $uploadedFiles = null, ?array $parsedBody = null ): ServerRequestInterface { - $serverParams = $serverParams ?? $_SERVER; - $queryParams = $queryParams ?? $_GET; - $cookieParams = $cookieParams ?? $_COOKIE; - $uploadedFiles = $uploadedFiles ?? $_FILES; - $parsedBody = $parsedBody ?? $_POST; + $serverParams ??= $_SERVER; + $queryParams ??= $_GET; + $cookieParams ??= $_COOKIE; + $uploadedFiles ??= $_FILES; + $parsedBody ??= $_POST; return new ServerRequest( server_request_protocol_version($serverParams), From c15cfbf48440f33f4aa1682e15cb5659b4672be7 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:56:48 +0100 Subject: [PATCH 26/49] Support for quoted-pairs --- src/Header.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Header.php b/src/Header.php index 8d82ecb..19edcd6 100644 --- a/src/Header.php +++ b/src/Header.php @@ -37,40 +37,40 @@ abstract class Header implements HeaderInterface { /** - * Regular Expression for a token validation + * DateTime format according to RFC-822 * - * @link https://tools.ietf.org/html/rfc7230#section-3.2 + * @link https://www.rfc-editor.org/rfc/rfc822#section-5 * * @var string */ - public const RFC7230_VALID_TOKEN = '/^[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+$/'; + public const RFC822_DATE_TIME_FORMAT = 'D, d M y H:i:s O'; /** - * Regular Expression for a field value validation + * Regular Expression for a token validation according to RFC-7230 * * @link https://tools.ietf.org/html/rfc7230#section-3.2 * * @var string */ - public const RFC7230_VALID_FIELD_VALUE = '/^[\x09\x20-\x7E\x80-\xFF]*$/'; + public const RFC7230_VALID_TOKEN = '/^[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+$/'; /** - * Regular Expression for a quoted string validation + * Regular Expression for a field value validation according to RFC-7230 * * @link https://tools.ietf.org/html/rfc7230#section-3.2 * * @var string */ - public const RFC7230_VALID_QUOTED_STRING = '/^[\x09\x20\x21\x23-\x5B\x5D-\x7E\x80-\xFF]*$/'; + public const RFC7230_VALID_FIELD_VALUE = '/^[\x09\x20-\x7E\x80-\xFF]*$/'; /** - * Date and time format + * Regular Expression for a quoted string validation according to RFC-7230 * - * @link https://www.rfc-editor.org/rfc/rfc822#section-5 + * @link https://tools.ietf.org/html/rfc7230#section-3.2 * * @var string */ - public const RFC822_DATE_TIME_FORMAT = 'D, d M y H:i:s O'; + public const RFC7230_VALID_QUOTED_STRING = '/^(?:[\x5C][\x22]|[\x09\x20\x21\x23-\x5B\x5D-\x7E\x80-\xFF])*$/'; /** * {@inheritdoc} From 8961bb4c93e0ce4136ff5db2e100e170c16f546b Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:57:30 +0100 Subject: [PATCH 27/49] Insignificant changes --- src/UploadedFile.php | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/UploadedFile.php b/src/UploadedFile.php index 2a9a3a1..dc830d1 100644 --- a/src/UploadedFile.php +++ b/src/UploadedFile.php @@ -58,15 +58,22 @@ class UploadedFile implements UploadedFileInterface */ public const UPLOAD_ERRORS = [ UPLOAD_ERR_OK => 'No error', - UPLOAD_ERR_INI_SIZE => 'Uploaded file exceeds the upload_max_filesize directive in php.ini', - UPLOAD_ERR_FORM_SIZE => 'Uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the form', + UPLOAD_ERR_INI_SIZE => 'Uploaded file exceeds the upload_max_filesize directive in the php.ini', + UPLOAD_ERR_FORM_SIZE => 'Uploaded file exceeds the MAX_FILE_SIZE directive in the HTML form', UPLOAD_ERR_PARTIAL => 'Uploaded file was only partially uploaded', UPLOAD_ERR_NO_FILE => 'No file was uploaded', - UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder', + UPLOAD_ERR_NO_TMP_DIR => 'Missing temporary directory', UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk', - UPLOAD_ERR_EXTENSION => 'File upload stopped by extension', + UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension', ]; + /** + * Description of unknown error + * + * @var string + */ + public const UNKNOWN_ERROR_TEXT = 'Unknown error'; + /** * The file stream * @@ -112,14 +119,14 @@ class UploadedFile implements UploadedFileInterface /** * Constructor of the class * - * @param StreamInterface $stream + * @param StreamInterface|null $stream * @param int|null $size * @param int $error * @param string|null $clientFilename * @param string|null $clientMediaType */ public function __construct( - StreamInterface $stream, + ?StreamInterface $stream, ?int $size = null, int $error = UPLOAD_ERR_OK, ?string $clientFilename = null, @@ -129,7 +136,7 @@ public function __construct( $this->stream = $stream; } - $message = self::UPLOAD_ERRORS[$error] ?? 'Unknown error'; + $message = self::UPLOAD_ERRORS[$error] ?? self::UNKNOWN_ERROR_TEXT; $this->size = $size; $this->errorCode = $error; From 250e87e3e3334a709d4c714d0feda38f7c55c394 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:57:59 +0100 Subject: [PATCH 28/49] Deleted --- tests/Header/KeepAliveHeaderTest.php | 149 ------------------ .../ServerRequestProxyIntegrationTest.php | 36 ----- tests/ServerRequestProxyTest.php | 17 -- 3 files changed, 202 deletions(-) delete mode 100644 tests/Header/KeepAliveHeaderTest.php delete mode 100644 tests/Integration/ServerRequestProxyIntegrationTest.php delete mode 100644 tests/ServerRequestProxyTest.php diff --git a/tests/Header/KeepAliveHeaderTest.php b/tests/Header/KeepAliveHeaderTest.php deleted file mode 100644 index 2625b47..0000000 --- a/tests/Header/KeepAliveHeaderTest.php +++ /dev/null @@ -1,149 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new KeepAliveHeader(); - - $this->assertSame('Keep-Alive', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new KeepAliveHeader(); - - $this->assertSame('', $header->getFieldValue()); - } - - public function testParameterWithEmptyValue() - { - $header = new KeepAliveHeader([ - 'foo' => '', - ]); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testParameterWithToken() - { - $header = new KeepAliveHeader([ - 'foo' => 'token', - ]); - - $this->assertSame('foo=token', $header->getFieldValue()); - } - - public function testParameterWithQuotedString() - { - $header = new KeepAliveHeader([ - 'foo' => 'quoted string', - ]); - - $this->assertSame('foo="quoted string"', $header->getFieldValue()); - } - - public function testParameterWithInteger() - { - $header = new KeepAliveHeader([ - 'foo' => 1, - ]); - - $this->assertSame('foo=1', $header->getFieldValue()); - } - - public function testSeveralParameters() - { - $header = new KeepAliveHeader([ - 'foo' => '', - 'bar' => 'token', - 'baz' => 'quoted string', - 'qux' => 1, - ]); - - $this->assertSame('foo, bar=token, baz="quoted string", qux=1', $header->getFieldValue()); - } - - public function testInvalidParameterName() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "invalid name" for the header "Keep-Alive" is not valid' - ); - - // cannot contain spaces... - new KeepAliveHeader(['invalid name' => 'value']); - } - - public function testInvalidParameterNameType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "" for the header "Keep-Alive" is not valid' - ); - - new KeepAliveHeader([0 => 'value']); - } - - public function testInvalidParameterValue() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value ""invalid value"" for the header "Keep-Alive" is not valid' - ); - - // cannot contain quotes... - new KeepAliveHeader(['name' => '"invalid value"']); - } - - public function testInvalidParameterValueType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value "" for the header "Keep-Alive" is not valid' - ); - - new KeepAliveHeader(['name' => []]); - } - - public function testBuild() - { - $header = new KeepAliveHeader(['foo' => 'bar']); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new KeepAliveHeader(['foo' => 'bar']); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Integration/ServerRequestProxyIntegrationTest.php b/tests/Integration/ServerRequestProxyIntegrationTest.php deleted file mode 100644 index ac18f47..0000000 --- a/tests/Integration/ServerRequestProxyIntegrationTest.php +++ /dev/null @@ -1,36 +0,0 @@ - Date: Sun, 26 Feb 2023 06:58:08 +0100 Subject: [PATCH 29/49] Renamed --- ...questProxy.php => ServerRequestHelper.php} | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) rename src/{ServerRequestProxy.php => ServerRequestHelper.php} (96%) diff --git a/src/ServerRequestProxy.php b/src/ServerRequestHelper.php similarity index 96% rename from src/ServerRequestProxy.php rename to src/ServerRequestHelper.php index a26b5b3..01b7951 100644 --- a/src/ServerRequestProxy.php +++ b/src/ServerRequestHelper.php @@ -33,9 +33,9 @@ use function trim; /** - * ServerRequestProxy + * ServerRequestHelper */ -final class ServerRequestProxy implements ServerRequestInterface, RequestMethodInterface +final class ServerRequestHelper implements ServerRequestInterface, RequestMethodInterface { /** @@ -182,7 +182,7 @@ public function getClientProducedMediaType(): string * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.1 * @link https://tools.ietf.org/html/rfc7231#section-5.3.2 * - * @return array> + * @return array> */ public function getClientConsumedMediaTypes(): array { @@ -191,13 +191,13 @@ public function getClientConsumedMediaTypes(): array return []; } - $accept = header_accept_parse($header); - if ($accept === []) { + $accepts = header_accept_like_parse($header); + if (empty($accepts)) { return []; } $result = []; - foreach ($accept as $type => $params) { + foreach ($accepts as $type => $params) { $result[strtolower($type)] = $params; } @@ -216,12 +216,12 @@ public function getClientConsumedEncodings(): array return []; } - $accept = header_accept_parse($header); - if ($accept === []) { + $accepts = header_accept_like_parse($header); + if (empty($accepts)) { return []; } - return $accept; + return $accepts; } /** @@ -236,12 +236,12 @@ public function getClientConsumedLanguages(): array return []; } - $accept = header_accept_parse($header); - if ($accept === []) { + $accepts = header_accept_like_parse($header); + if (empty($accepts)) { return []; } - return $accept; + return $accepts; } /** From 92559d4ca80a8040afbf169d7192f0ba43e45998 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sun, 26 Feb 2023 06:58:23 +0100 Subject: [PATCH 30/49] Improve tests --- tests/Header/ContentEncodingHeaderTest.php | 9 ++--- tests/Header/TransferEncodingHeaderTest.php | 8 ++--- .../ServerRequestHelperIntegrationTest.php | 36 +++++++++++++++++++ tests/ServerRequestHelperTest.php | 17 +++++++++ 4 files changed, 62 insertions(+), 8 deletions(-) create mode 100644 tests/Integration/ServerRequestHelperIntegrationTest.php create mode 100644 tests/ServerRequestHelperTest.php diff --git a/tests/Header/ContentEncodingHeaderTest.php b/tests/Header/ContentEncodingHeaderTest.php index 3325846..ff3305a 100644 --- a/tests/Header/ContentEncodingHeaderTest.php +++ b/tests/Header/ContentEncodingHeaderTest.php @@ -12,10 +12,11 @@ class ContentEncodingHeaderTest extends TestCase { public function testConstants() { - $this->assertSame('gzip', ContentEncodingHeader::GZIP); - $this->assertSame('compress', ContentEncodingHeader::COMPRESS); - $this->assertSame('deflate', ContentEncodingHeader::DEFLATE); - $this->assertSame('br', ContentEncodingHeader::BR); + $this->assertSame('aes128gcm', ContentEncodingHeader::HTTP_CONTENT_ENCODING_AES128GCM); + $this->assertSame('br', ContentEncodingHeader::HTTP_CONTENT_ENCODING_BR); + $this->assertSame('compress', ContentEncodingHeader::HTTP_CONTENT_ENCODING_COMPRESS); + $this->assertSame('deflate', ContentEncodingHeader::HTTP_CONTENT_ENCODING_DEFLATE); + $this->assertSame('gzip', ContentEncodingHeader::HTTP_CONTENT_ENCODING_GZIP); } public function testContracts() diff --git a/tests/Header/TransferEncodingHeaderTest.php b/tests/Header/TransferEncodingHeaderTest.php index b5bd0a4..ea39a43 100644 --- a/tests/Header/TransferEncodingHeaderTest.php +++ b/tests/Header/TransferEncodingHeaderTest.php @@ -12,10 +12,10 @@ class TransferEncodingHeaderTest extends TestCase { public function testConstants() { - $this->assertSame('chunked', TransferEncodingHeader::CHUNKED); - $this->assertSame('compress', TransferEncodingHeader::COMPRESS); - $this->assertSame('deflate', TransferEncodingHeader::DEFLATE); - $this->assertSame('gzip', TransferEncodingHeader::GZIP); + $this->assertSame('chunked', TransferEncodingHeader::HTTP_TRANSFER_ENCODING_CHUNKED); + $this->assertSame('compress', TransferEncodingHeader::HTTP_TRANSFER_ENCODING_COMPRESS); + $this->assertSame('deflate', TransferEncodingHeader::HTTP_TRANSFER_ENCODING_DEFLATE); + $this->assertSame('gzip', TransferEncodingHeader::HTTP_TRANSFER_ENCODING_GZIP); } public function testContracts() diff --git a/tests/Integration/ServerRequestHelperIntegrationTest.php b/tests/Integration/ServerRequestHelperIntegrationTest.php new file mode 100644 index 0000000..a0cb718 --- /dev/null +++ b/tests/Integration/ServerRequestHelperIntegrationTest.php @@ -0,0 +1,36 @@ + Date: Tue, 28 Feb 2023 03:51:56 +0100 Subject: [PATCH 31/49] v3.1.1 --- docs/headers.md | 2 +- functions/header_accept_like_parse.php | 2 +- src/Entity/IpAddress.php | 137 ------------------ src/Header.php | 83 +---------- src/Header/ContentLanguageHeader.php | 11 +- src/Header/ContentLengthHeader.php | 2 +- src/Header/ContentMD5Header.php | 70 --------- src/Header/ContentSecurityPolicyHeader.php | 101 ------------- .../ContentSecurityPolicyReportOnlyHeader.php | 27 ---- src/Header/ContentTypeHeader.php | 16 +- src/Header/DateHeader.php | 3 +- src/Header/ExpiresHeader.php | 3 +- src/Header/LastModifiedHeader.php | 3 +- src/Header/RetryAfterHeader.php | 3 +- src/Header/SetCookieHeader.php | 3 +- src/Header/SunsetHeader.php | 3 +- src/Header/WarningHeader.php | 3 +- src/HeaderInterface.php | 30 +++- src/HeaderUtils.php | 43 ++++++ src/Message.php | 40 +++-- src/Request.php | 17 +-- src/Response.php | 22 +-- src/Response/HtmlResponse.php | 4 +- src/Response/JsonResponse.php | 4 +- src/ServerRequestFactory.php | 8 +- src/ServerRequestHelper.php | 4 +- src/UploadedFile.php | 9 +- src/Uri/Component/Fragment.php | 5 +- src/Uri/Component/Host.php | 5 +- src/Uri/Component/Password.php | 5 +- src/Uri/Component/Path.php | 5 +- src/Uri/Component/Query.php | 5 +- src/Uri/Component/Scheme.php | 4 +- src/Uri/Component/User.php | 5 +- tests/Header/ContentMD5HeaderTest.php | 79 ---------- .../ContentSecurityPolicyHeaderTest.php | 137 ------------------ ...tentSecurityPolicyReportOnlyHeaderTest.php | 137 ------------------ 37 files changed, 158 insertions(+), 882 deletions(-) delete mode 100644 src/Entity/IpAddress.php delete mode 100644 src/Header/ContentMD5Header.php delete mode 100644 src/Header/ContentSecurityPolicyHeader.php delete mode 100644 src/Header/ContentSecurityPolicyReportOnlyHeader.php create mode 100644 src/HeaderUtils.php delete mode 100644 tests/Header/ContentMD5HeaderTest.php delete mode 100644 tests/Header/ContentSecurityPolicyHeaderTest.php delete mode 100644 tests/Header/ContentSecurityPolicyReportOnlyHeaderTest.php diff --git a/docs/headers.md b/docs/headers.md index fc798de..56c2c6c 100644 --- a/docs/headers.md +++ b/docs/headers.md @@ -326,7 +326,7 @@ use Sunrise\Http\Message\ResponseFactory; $response = (new ResponseFactory)->createResponse(); -$header = new ContentTypeHeader('application/json', ['charset' => 'utf-8']); +$header = new ContentTypeHeader('application', 'json', ['charset' => 'utf-8']); $response = $response->withHeader(...$header); ``` diff --git a/functions/header_accept_like_parse.php b/functions/header_accept_like_parse.php index 8e5b15c..7c155ed 100644 --- a/functions/header_accept_like_parse.php +++ b/functions/header_accept_like_parse.php @@ -144,7 +144,7 @@ function header_accept_like_parse(string $header): ?array } // ignoring backslashes before double quotes in the quoted parameter value... - if (($char . $next) === '\"' && $inQuotes) { + if ($char === '\\' && $next === '"' && $inQuotes) { continue; } diff --git a/src/Entity/IpAddress.php b/src/Entity/IpAddress.php deleted file mode 100644 index 3a200b0..0000000 --- a/src/Entity/IpAddress.php +++ /dev/null @@ -1,137 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Entity; - -/** - * Import functions - */ -use function filter_var; - -/** - * Import constants - */ -use const FILTER_FLAG_IPV4; -use const FILTER_FLAG_IPV6; -use const FILTER_FLAG_NO_PRIV_RANGE; -use const FILTER_FLAG_NO_RES_RANGE; -use const FILTER_VALIDATE_IP; - -/** - * IP address - */ -final class IpAddress -{ - - /** - * The IP address value - * - * @var string - */ - private string $value; - - /** - * The list of proxies in front of this IP address - * - * @var list - */ - private array $proxies; - - /** - * Constructor of the class - * - * @param string $value - * @param list $proxies - */ - public function __construct(string $value, array $proxies = []) - { - $this->value = $value; - $this->proxies = $proxies; - } - - /** - * Gets the IP address value - * - * @return string - */ - public function getValue(): string - { - return $this->value; - } - - /** - * Gets the list of proxies in front of this IP address - * - * @return list - */ - public function getProxies(): array - { - return $this->proxies; - } - - /** - * Checks if the IP address is valid - * - * @return bool - */ - public function isValid(): bool - { - return false !== filter_var($this->value, FILTER_VALIDATE_IP); - } - - /** - * Checks if the IP address is IPv4 - * - * @return bool - */ - public function isVersion4(): bool - { - return false !== filter_var($this->value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4); - } - - /** - * Checks if the IP address is IPv6 - * - * @return bool - */ - public function isVersion6(): bool - { - return false !== filter_var($this->value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6); - } - - /** - * Checks if the IP address is in the private range - * - * @return bool - */ - public function isInPrivateRange(): bool - { - if (!$this->isValid()) { - return false; - } - - return false === filter_var($this->value, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE); - } - - /** - * Checks if the IP address is in the reserved range - * - * @return bool - */ - public function isInReservedRange(): bool - { - if (!$this->isValid()) { - return false; - } - - return false === filter_var($this->value, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE); - } -} diff --git a/src/Header.php b/src/Header.php index 19edcd6..02755c0 100644 --- a/src/Header.php +++ b/src/Header.php @@ -36,42 +36,6 @@ abstract class Header implements HeaderInterface { - /** - * DateTime format according to RFC-822 - * - * @link https://www.rfc-editor.org/rfc/rfc822#section-5 - * - * @var string - */ - public const RFC822_DATE_TIME_FORMAT = 'D, d M y H:i:s O'; - - /** - * Regular Expression for a token validation according to RFC-7230 - * - * @link https://tools.ietf.org/html/rfc7230#section-3.2 - * - * @var string - */ - public const RFC7230_VALID_TOKEN = '/^[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+$/'; - - /** - * Regular Expression for a field value validation according to RFC-7230 - * - * @link https://tools.ietf.org/html/rfc7230#section-3.2 - * - * @var string - */ - public const RFC7230_VALID_FIELD_VALUE = '/^[\x09\x20-\x7E\x80-\xFF]*$/'; - - /** - * Regular Expression for a quoted string validation according to RFC-7230 - * - * @link https://tools.ietf.org/html/rfc7230#section-3.2 - * - * @var string - */ - public const RFC7230_VALID_QUOTED_STRING = '/^(?:[\x5C][\x22]|[\x09\x20\x21\x23-\x5B\x5D-\x7E\x80-\xFF])*$/'; - /** * {@inheritdoc} */ @@ -98,7 +62,7 @@ final public function __toString(): string */ final protected function isToken(string $token): bool { - return preg_match(self::RFC7230_VALID_TOKEN, $token) === 1; + return preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $token) === 1; } /** @@ -113,22 +77,7 @@ final protected function isToken(string $token): bool */ final protected function validateToken(string ...$tokens): void { - $this->validateValueByRegex(self::RFC7230_VALID_TOKEN, ...$tokens); - } - - /** - * Validates the given field value(s) - * - * @param string ...$fieldValues - * - * @return void - * - * @throws InvalidHeaderException - * If one of the field values isn't valid. - */ - final protected function validateFieldValue(string ...$fieldValues): void - { - $this->validateValueByRegex(self::RFC7230_VALID_FIELD_VALUE, ...$fieldValues); + $this->validateValueByRegex(HeaderInterface::RFC7230_TOKEN_REGEX, ...$tokens); } /** @@ -143,7 +92,7 @@ final protected function validateFieldValue(string ...$fieldValues): void */ final protected function validateQuotedString(string ...$quotedStrings): void { - $this->validateValueByRegex(self::RFC7230_VALID_QUOTED_STRING, ...$quotedStrings); + $this->validateValueByRegex(HeaderInterface::RFC7230_QUOTED_STRING_REGEX, ...$quotedStrings); } /** @@ -161,8 +110,8 @@ final protected function validateParameters(array $parameters): array { return $this->validateParametersByRegex( $parameters, - self::RFC7230_VALID_TOKEN, - self::RFC7230_VALID_QUOTED_STRING + HeaderInterface::RFC7230_TOKEN_REGEX, + HeaderInterface::RFC7230_QUOTED_STRING_REGEX ); } @@ -232,26 +181,4 @@ final protected function validateParametersByRegex(array $parameters, string $na return $parameters; } - - /** - * Formats the given date-time object - * - * @link https://tools.ietf.org/html/rfc7230#section-3.2 - * - * @param DateTimeInterface $dateTime - * - * @return string - */ - final protected function formatDateTime(DateTimeInterface $dateTime): string - { - if ($dateTime instanceof DateTime) { - return (clone $dateTime) - ->setTimezone(new DateTimeZone('GMT')) - ->format(self::RFC822_DATE_TIME_FORMAT); - } - - return $dateTime - ->setTimezone(new DateTimeZone('GMT')) - ->format(self::RFC822_DATE_TIME_FORMAT); - } } diff --git a/src/Header/ContentLanguageHeader.php b/src/Header/ContentLanguageHeader.php index 4c38027..e4cd204 100644 --- a/src/Header/ContentLanguageHeader.php +++ b/src/Header/ContentLanguageHeader.php @@ -28,15 +28,6 @@ class ContentLanguageHeader extends Header { - /** - * Regular Expression for a language tag validation - * - * @link https://tools.ietf.org/html/rfc2616#section-3.10 - * - * @var string - */ - public const RFC2616_LANGUAGE_TAG = '/^[a-zA-Z]{1,8}(?:-[a-zA-Z]{1,8})?$/'; - /** * @var list */ @@ -52,7 +43,7 @@ class ContentLanguageHeader extends Header */ public function __construct(string ...$languages) { - $this->validateValueByRegex(self::RFC2616_LANGUAGE_TAG, ...$languages); + $this->validateToken(...$languages); foreach ($languages as $language) { $this->languages[] = $language; diff --git a/src/Header/ContentLengthHeader.php b/src/Header/ContentLengthHeader.php index d02df7c..673cbbd 100644 --- a/src/Header/ContentLengthHeader.php +++ b/src/Header/ContentLengthHeader.php @@ -36,7 +36,7 @@ class ContentLengthHeader extends Header /** * Constructor of the class * - * @param int $value + * @param int<0, max> $value * * @throws InvalidHeaderException * If the value isn't valid. diff --git a/src/Header/ContentMD5Header.php b/src/Header/ContentMD5Header.php deleted file mode 100644 index 4a1c88d..0000000 --- a/src/Header/ContentMD5Header.php +++ /dev/null @@ -1,70 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.15 - */ -class ContentMD5Header extends Header -{ - - /** - * Regular Expression for a MD5 digest validation - * - * @link https://tools.ietf.org/html/rfc2045#section-6.8 - * - * @var string - */ - public const RFC2045_MD5_DIGEST = '/^[A-Za-z0-9\+\/]+=*$/'; - - /** - * @var string - */ - private string $value; - - /** - * Constructor of the class - * - * @param string $value - * - * @throws InvalidHeaderException - * If the value isn't valid. - */ - public function __construct(string $value) - { - $this->validateValueByRegex(self::RFC2045_MD5_DIGEST, $value); - - $this->value = $value; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Content-MD5'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return $this->value; - } -} diff --git a/src/Header/ContentSecurityPolicyHeader.php b/src/Header/ContentSecurityPolicyHeader.php deleted file mode 100644 index ec2556f..0000000 --- a/src/Header/ContentSecurityPolicyHeader.php +++ /dev/null @@ -1,101 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; -use function sprintf; - -/** - * @link https://www.w3.org/TR/CSP3/#csp-header - */ -class ContentSecurityPolicyHeader extends Header -{ - - /** - * Regular Expression for a directive name validation - * - * @link https://www.w3.org/TR/CSP3/#framework-directives - * - * @var string - */ - public const VALID_DIRECTIVE_NAME = '/^[0-9A-Za-z\-]+$/'; - - /** - * Regular Expression for a directive value validation - * - * @link https://www.w3.org/TR/CSP3/#framework-directives - * - * @var string - */ - public const VALID_DIRECTIVE_VALUE = '/^[\x09\x20-\x2B\x2D-\x3A\x3C-\x7E]*$/'; - - /** - * @var array - */ - private array $parameters; - - /** - * Constructor of the class - * - * @param array $parameters - * - * @throws InvalidHeaderException - * If the parameters aren't valid. - */ - public function __construct(array $parameters = []) - { - $parameters = $this->validateParametersByRegex( - $parameters, - self::VALID_DIRECTIVE_NAME, - self::VALID_DIRECTIVE_VALUE - ); - - $this->parameters = $parameters; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Content-Security-Policy'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - $directives = []; - foreach ($this->parameters as $directive => $value) { - // the directive can be without value, - // e.g. sandbox, upgrade-insecure-requests, etc. - if ($value === '') { - $directives[] = $directive; - continue; - } - - $directives[] = sprintf('%s %s', $directive, $value); - } - - return implode('; ', $directives); - } -} diff --git a/src/Header/ContentSecurityPolicyReportOnlyHeader.php b/src/Header/ContentSecurityPolicyReportOnlyHeader.php deleted file mode 100644 index 3b80446..0000000 --- a/src/Header/ContentSecurityPolicyReportOnlyHeader.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * @link https://www.w3.org/TR/CSP3/#cspro-header - */ -class ContentSecurityPolicyReportOnlyHeader extends ContentSecurityPolicyHeader -{ - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Content-Security-Policy-Report-Only'; - } -} diff --git a/src/Header/ContentTypeHeader.php b/src/Header/ContentTypeHeader.php index 42c6c43..bd1e88f 100644 --- a/src/Header/ContentTypeHeader.php +++ b/src/Header/ContentTypeHeader.php @@ -20,6 +20,7 @@ /** * Import functions */ +use function explode; use function sprintf; /** @@ -28,15 +29,6 @@ class ContentTypeHeader extends Header { - /** - * Regular Expression for a content type validation - * - * @link https://tools.ietf.org/html/rfc6838#section-4.2 - * - * @var string - */ - public const RFC6838_CONTENT_TYPE = '/^[\dA-Za-z][\d\w\!#\$&\+\-\.\^]*(?:\/[\dA-Za-z][\d\w\!#\$&\+\-\.\^]*)?$/'; - /** * @var string */ @@ -59,7 +51,11 @@ class ContentTypeHeader extends Header */ public function __construct(string $type, array $parameters = []) { - $this->validateValueByRegex(self::RFC6838_CONTENT_TYPE, $type); + if (strpos($type, '/') === false) { + $type .= '/*'; + } + + $this->validateToken(...explode('/', $type, 2)); $parameters = $this->validateParameters($parameters); diff --git a/src/Header/DateHeader.php b/src/Header/DateHeader.php index 1af8b44..cfa308c 100644 --- a/src/Header/DateHeader.php +++ b/src/Header/DateHeader.php @@ -16,6 +16,7 @@ */ use DateTimeInterface; use Sunrise\Http\Message\Header; +use Sunrise\Http\Message\HeaderUtils; /** * @link https://tools.ietf.org/html/rfc2616#section-14.18 @@ -52,6 +53,6 @@ public function getFieldName(): string */ public function getFieldValue(): string { - return $this->formatDateTime($this->timestamp); + return HeaderUtils::formatDate($this->timestamp); } } diff --git a/src/Header/ExpiresHeader.php b/src/Header/ExpiresHeader.php index 339b95e..f0a6384 100644 --- a/src/Header/ExpiresHeader.php +++ b/src/Header/ExpiresHeader.php @@ -16,6 +16,7 @@ */ use DateTimeInterface; use Sunrise\Http\Message\Header; +use Sunrise\Http\Message\HeaderUtils; /** * @link https://tools.ietf.org/html/rfc2616#section-14.21 @@ -52,6 +53,6 @@ public function getFieldName(): string */ public function getFieldValue(): string { - return $this->formatDateTime($this->timestamp); + return HeaderUtils::formatDate($this->timestamp); } } diff --git a/src/Header/LastModifiedHeader.php b/src/Header/LastModifiedHeader.php index 72c2fbb..3113410 100644 --- a/src/Header/LastModifiedHeader.php +++ b/src/Header/LastModifiedHeader.php @@ -16,6 +16,7 @@ */ use DateTimeInterface; use Sunrise\Http\Message\Header; +use Sunrise\Http\Message\HeaderUtils; /** * @link https://tools.ietf.org/html/rfc2616#section-14.29 @@ -52,6 +53,6 @@ public function getFieldName(): string */ public function getFieldValue(): string { - return $this->formatDateTime($this->timestamp); + return HeaderUtils::formatDate($this->timestamp); } } diff --git a/src/Header/RetryAfterHeader.php b/src/Header/RetryAfterHeader.php index 9dd7a20..88a9f75 100644 --- a/src/Header/RetryAfterHeader.php +++ b/src/Header/RetryAfterHeader.php @@ -16,6 +16,7 @@ */ use DateTimeInterface; use Sunrise\Http\Message\Header; +use Sunrise\Http\Message\HeaderUtils; /** * @link https://tools.ietf.org/html/rfc2616#section-14.37 @@ -52,6 +53,6 @@ public function getFieldName(): string */ public function getFieldValue(): string { - return $this->formatDateTime($this->timestamp); + return HeaderUtils::formatDate($this->timestamp); } } diff --git a/src/Header/SetCookieHeader.php b/src/Header/SetCookieHeader.php index 4608d99..2c454a0 100644 --- a/src/Header/SetCookieHeader.php +++ b/src/Header/SetCookieHeader.php @@ -18,6 +18,7 @@ use DateTimeInterface; use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; +use Sunrise\Http\Message\HeaderUtils; /** * Import functions @@ -161,7 +162,7 @@ public function getFieldValue(): string $result = sprintf('%s=%s', $name, $value); if (isset($this->expires)) { - $result .= '; Expires=' . $this->formatDateTime($this->expires); + $result .= '; Expires=' . HeaderUtils::formatDate($this->expires); $result .= '; Max-Age=' . max($this->expires->getTimestamp() - time(), 0); } diff --git a/src/Header/SunsetHeader.php b/src/Header/SunsetHeader.php index b8682ae..b1b379d 100644 --- a/src/Header/SunsetHeader.php +++ b/src/Header/SunsetHeader.php @@ -16,6 +16,7 @@ */ use DateTimeInterface; use Sunrise\Http\Message\Header; +use Sunrise\Http\Message\HeaderUtils; /** * @link https://tools.ietf.org/id/draft-wilde-sunset-header-03.html @@ -52,6 +53,6 @@ public function getFieldName(): string */ public function getFieldValue(): string { - return $this->formatDateTime($this->timestamp); + return HeaderUtils::formatDate($this->timestamp); } } diff --git a/src/Header/WarningHeader.php b/src/Header/WarningHeader.php index 40573af..d23c59b 100644 --- a/src/Header/WarningHeader.php +++ b/src/Header/WarningHeader.php @@ -17,6 +17,7 @@ use DateTimeInterface; use Sunrise\Http\Message\Exception\InvalidHeaderException; use Sunrise\Http\Message\Header; +use Sunrise\Http\Message\HeaderUtils; /** * Import functions @@ -101,7 +102,7 @@ public function getFieldValue(): string $value = sprintf('%s %s "%s"', $this->code, $this->agent, $this->text); if (isset($this->date)) { - $value .= sprintf(' "%s"', $this->formatDateTime($this->date)); + $value .= sprintf(' "%s"', HeaderUtils::formatDate($this->date)); } return $value; diff --git a/src/HeaderInterface.php b/src/HeaderInterface.php index 898e3b7..54408a2 100644 --- a/src/HeaderInterface.php +++ b/src/HeaderInterface.php @@ -26,6 +26,34 @@ interface HeaderInterface extends IteratorAggregate { + /** + * Date format according to RFC-822 + * + * @var string + */ + public const RFC822_DATE_FORMAT = 'D, d M y H:i:s O'; + + /** + * Regular Expression for a token validation according to RFC-7230 + * + * @var string + */ + public const RFC7230_TOKEN_REGEX = '/^[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+$/'; + + /** + * Regular Expression for a field-value validation according to RFC-7230 + * + * @var string + */ + public const RFC7230_FIELD_VALUE_REGEX = '/^[\x09\x20-\x7E\x80-\xFF]*$/'; + + /** + * Regular Expression for a quoted-string validation according to RFC-7230 + * + * @var string + */ + public const RFC7230_QUOTED_STRING_REGEX = '/^(?:[\x5C][\x22]|[\x09\x20\x21\x23-\x5B\x5D-\x7E\x80-\xFF])*$/'; + /** * Gets the header field name * @@ -43,8 +71,6 @@ public function getFieldValue(): string; /** * Converts the header field to a string * - * @link http://php.net/manual/en/language.oop5.magic.php#object.tostring - * * @return string */ public function __toString(): string; diff --git a/src/HeaderUtils.php b/src/HeaderUtils.php new file mode 100644 index 0000000..3e1f9fd --- /dev/null +++ b/src/HeaderUtils.php @@ -0,0 +1,43 @@ + + * @copyright Copyright (c) 2018, Anatoly Nekhay + * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE + * @link https://github.com/sunrise-php/http-message + */ + +namespace Sunrise\Http\Message; + +/** + * Import classes + */ +use DateTime; +use DateTimeInterface; +use DateTimeZone; + +/** + * HeaderUtils + */ +final class HeaderUtils +{ + + /** + * Formats the given date according to RFC-822 + * + * @param DateTimeInterface $date + * + * @return string + */ + public static function formatDate(DateTimeInterface $date): string + { + if ($date instanceof DateTime) { + $data = clone $date; + } + + return $date->setTimezone(new DateTimeZone('GMT')) + ->format(HeaderInterface::RFC822_DATE_FORMAT); + } +} diff --git a/src/Message.php b/src/Message.php index 55080cc..fee01a1 100644 --- a/src/Message.php +++ b/src/Message.php @@ -40,18 +40,18 @@ abstract class Message implements MessageInterface { /** - * Default HTTP version + * Supported HTTP versions * - * @var string + * @var list */ - public const DEFAULT_HTTP_VERSION = '1.1'; + public const SUPPORTED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2']; /** - * Supported HTTP versions + * Default HTTP version * - * @var list + * @var string */ - public const SUPPORTED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2']; + public const DEFAULT_HTTP_VERSION = '1.1'; /** * The message HTTP version @@ -142,15 +142,13 @@ public function hasHeader($name): bool */ public function getHeader($name): array { - if (!$this->hasHeader($name)) { + $key = strtolower($name); + + if (empty($this->headerNames[$key])) { return []; } - $key = strtolower($name); - $originalName = $this->headerNames[$key]; - $value = $this->headers[$originalName]; - - return $value; + return $this->headers[$this->headerNames[$key]]; } /** @@ -295,8 +293,8 @@ final protected function setHeader($name, $value, bool $replace = true): void $this->headerNames[$key] ??= $name; $this->headers[$this->headerNames[$key]] ??= []; - foreach ($value as $subvalue) { - $this->headers[$this->headerNames[$key]][] = $subvalue; + foreach ($value as $item) { + $this->headers[$this->headerNames[$key]][] = $item; } } @@ -383,7 +381,7 @@ private function validateHeaderName($name): void throw new InvalidArgumentException('HTTP header name must be a string'); } - if (!preg_match(Header::RFC7230_VALID_TOKEN, $name)) { + if (!preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $name)) { throw new InvalidArgumentException('HTTP header name is invalid'); } } @@ -391,7 +389,7 @@ private function validateHeaderName($name): void /** * Validates the given header value * - * @param string $validName + * @param string $name * @param array $value * * @return void @@ -399,12 +397,12 @@ private function validateHeaderName($name): void * @throws InvalidArgumentException * If the header value isn't valid. */ - private function validateHeaderValue(string $validName, array $value): void + private function validateHeaderValue(string $name, array $value): void { if ([] === $value) { throw new InvalidArgumentException(sprintf( 'The "%s" HTTP header value cannot be an empty array', - $validName, + $name, )); } @@ -416,15 +414,15 @@ private function validateHeaderValue(string $validName, array $value): void if (!is_string($item)) { throw new InvalidArgumentException(sprintf( 'The "%s[%s]" HTTP header value must be a string', - $validName, + $name, $key )); } - if (!preg_match(Header::RFC7230_VALID_FIELD_VALUE, $item)) { + if (!preg_match(HeaderInterface::RFC7230_FIELD_VALUE_REGEX, $item)) { throw new InvalidArgumentException(sprintf( 'The "%s[%s]" HTTP header value is invalid', - $validName, + $name, $key )); } diff --git a/src/Request.php b/src/Request.php index fb96c46..00ebeeb 100644 --- a/src/Request.php +++ b/src/Request.php @@ -39,11 +39,9 @@ class Request extends Message implements RequestInterface, RequestMethodInterfac /** * Regular Expression for a request target validation * - * @link https://tools.ietf.org/html/rfc7230#section-5.3 - * * @var string */ - public const RFC7230_VALID_REQUEST_TARGET = '/^[\x21-\x7E\x80-\xFF]+$/'; + public const RFC7230_REQUEST_TARGET_REGEX = '/^[\x21-\x7E\x80-\xFF]+$/'; /** * Default request method @@ -52,13 +50,6 @@ class Request extends Message implements RequestInterface, RequestMethodInterfac */ public const DEFAULT_METHOD = self::METHOD_GET; - /** - * Default request URI - * - * @var string - */ - public const DEFAULT_URI = '/'; - /** * The request method (aka verb) * @@ -101,7 +92,7 @@ public function __construct( $this->setMethod($method); } - $this->setUri($uri ?? self::DEFAULT_URI); + $this->setUri($uri ?? '/'); if (isset($headers)) { $this->setHeaders($headers); @@ -304,7 +295,7 @@ private function validateMethod($method): void throw new InvalidArgumentException('HTTP method must be a string'); } - if (!preg_match(Header::RFC7230_VALID_TOKEN, $method)) { + if (!preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $method)) { throw new InvalidArgumentException('Invalid HTTP method'); } } @@ -329,7 +320,7 @@ private function validateRequestTarget($requestTarget): void throw new InvalidArgumentException('HTTP request target must be a string'); } - if (!preg_match(self::RFC7230_VALID_REQUEST_TARGET, $requestTarget)) { + if (!preg_match(self::RFC7230_REQUEST_TARGET_REGEX, $requestTarget)) { throw new InvalidArgumentException('Invalid HTTP request target'); } } diff --git a/src/Response.php b/src/Response.php index 33ce462..9798c39 100644 --- a/src/Response.php +++ b/src/Response.php @@ -40,7 +40,7 @@ class Response extends Message implements ResponseInterface, StatusCodeInterface * * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml * - * @var array + * @var array, non-empty-string> */ public const REASON_PHRASES = [ @@ -123,20 +123,6 @@ class Response extends Message implements ResponseInterface, StatusCodeInterface */ public const DEFAULT_STATUS_CODE = self::STATUS_OK; - /** - * Default response reason phrase - * - * @var string - */ - public const DEFAULT_REASON_PHRASE = self::REASON_PHRASES[self::DEFAULT_STATUS_CODE]; - - /** - * Reason phrase for unknown status code - * - * @var string - */ - public const UNKNOWN_STATUS_CODE_REASON_PHRASE = 'Unknown Status Code'; - /** * The response's status code * @@ -149,7 +135,7 @@ class Response extends Message implements ResponseInterface, StatusCodeInterface * * @var string */ - private string $reasonPhrase = self::DEFAULT_REASON_PHRASE; + private string $reasonPhrase = self::REASON_PHRASES[self::DEFAULT_STATUS_CODE]; /** * Constrictor of the class @@ -237,7 +223,7 @@ final protected function setStatus($statusCode, $reasonPhrase): void $this->validateReasonPhrase($reasonPhrase); if ('' === $reasonPhrase) { - $reasonPhrase = self::REASON_PHRASES[$statusCode] ?? self::UNKNOWN_STATUS_CODE_REASON_PHRASE; + $reasonPhrase = self::REASON_PHRASES[$statusCode] ?? 'Unknown Status Code'; } $this->statusCode = $statusCode; @@ -289,7 +275,7 @@ private function validateReasonPhrase($reasonPhrase): void throw new InvalidArgumentException('HTTP reason phrase must be a string'); } - if (!preg_match(Header::RFC7230_VALID_FIELD_VALUE, $reasonPhrase)) { + if (!preg_match(HeaderInterface::RFC7230_FIELD_VALUE_REGEX, $reasonPhrase)) { throw new InvalidArgumentException('Invalid HTTP reason phrase'); } } diff --git a/src/Response/HtmlResponse.php b/src/Response/HtmlResponse.php index e5a4a10..955967d 100644 --- a/src/Response/HtmlResponse.php +++ b/src/Response/HtmlResponse.php @@ -44,9 +44,9 @@ public function __construct(int $statusCode, $html) { parent::__construct($statusCode); - $this->setHeader('Content-Type', 'text/html; charset=utf-8'); - $this->setBody($this->createBody($html)); + + $this->setHeader('Content-Type', 'text/html; charset=utf-8'); } /** diff --git a/src/Response/JsonResponse.php b/src/Response/JsonResponse.php index 8d9c87f..fd5c227 100644 --- a/src/Response/JsonResponse.php +++ b/src/Response/JsonResponse.php @@ -50,9 +50,9 @@ public function __construct(int $statusCode, $data, int $flags = 0, int $depth = { parent::__construct($statusCode); - $this->setHeader('Content-Type', 'application/json; charset=utf-8'); - $this->setBody($this->createBody($data, $flags, $depth)); + + $this->setHeader('Content-Type', 'application/json; charset=utf-8'); } /** diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index a0483d8..628e8b0 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -47,11 +47,11 @@ public static function fromGlobals( ?array $uploadedFiles = null, ?array $parsedBody = null ): ServerRequestInterface { - $serverParams ??= $_SERVER; - $queryParams ??= $_GET; - $cookieParams ??= $_COOKIE; + $serverParams ??= $_SERVER; + $queryParams ??= $_GET; + $cookieParams ??= $_COOKIE; $uploadedFiles ??= $_FILES; - $parsedBody ??= $_POST; + $parsedBody ??= $_POST; return new ServerRequest( server_request_protocol_version($serverParams), diff --git a/src/ServerRequestHelper.php b/src/ServerRequestHelper.php index 01b7951..fa5da26 100644 --- a/src/ServerRequestHelper.php +++ b/src/ServerRequestHelper.php @@ -263,7 +263,7 @@ public function clientProducesMediaType(array $consumes): bool } foreach ($consumes as $consumed) { - if ($this->equalsMediaTypes($consumed, $produced)) { + if (media_types_compare($consumed, $produced)) { return true; } } @@ -295,7 +295,7 @@ public function clientConsumesMediaType(array $produces): bool foreach ($produces as $a) { foreach ($consumes as $b => $_) { - if ($this->equalsMediaTypes($a, $b)) { + if (media_types_compare($a, $b)) { return true; } } diff --git a/src/UploadedFile.php b/src/UploadedFile.php index dc830d1..259b9f1 100644 --- a/src/UploadedFile.php +++ b/src/UploadedFile.php @@ -67,13 +67,6 @@ class UploadedFile implements UploadedFileInterface UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension', ]; - /** - * Description of unknown error - * - * @var string - */ - public const UNKNOWN_ERROR_TEXT = 'Unknown error'; - /** * The file stream * @@ -136,7 +129,7 @@ public function __construct( $this->stream = $stream; } - $message = self::UPLOAD_ERRORS[$error] ?? self::UNKNOWN_ERROR_TEXT; + $message = self::UPLOAD_ERRORS[$error] ?? 'Unknown error'; $this->size = $size; $this->errorCode = $error; diff --git a/src/Uri/Component/Fragment.php b/src/Uri/Component/Fragment.php index 8c89e70..cd87a20 100644 --- a/src/Uri/Component/Fragment.php +++ b/src/Uri/Component/Fragment.php @@ -36,7 +36,8 @@ final class Fragment implements ComponentInterface * * @var string */ - private const NORMALIZE_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\-\._~\!\$&\'\(\)\*\+,;\=\:@\/\?]+)|(.?))/u'; + // phpcs:ignore Generic.Files.LineLength + private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x3b\x3d\x3f-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; /** * The component value @@ -63,7 +64,7 @@ public function __construct($value) throw new InvalidUriComponentException('URI component "fragment" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZE_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Host.php b/src/Uri/Component/Host.php index c5b8d69..e42ce24 100644 --- a/src/Uri/Component/Host.php +++ b/src/Uri/Component/Host.php @@ -37,7 +37,8 @@ final class Host implements ComponentInterface * * @var string */ - private const NORMALIZE_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\-\._~\!\$&\'\(\)\*\+,;\=]+)|(.?))/u'; + // phpcs:ignore Generic.Files.LineLength + private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x2e\x30-\x39\x3b\x3d\x41-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; /** * The component value @@ -64,7 +65,7 @@ public function __construct($value) throw new InvalidUriComponentException('URI component "host" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZE_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Password.php b/src/Uri/Component/Password.php index 969db1c..f5e9cd3 100644 --- a/src/Uri/Component/Password.php +++ b/src/Uri/Component/Password.php @@ -36,7 +36,8 @@ final class Password implements ComponentInterface * * @var string */ - private const NORMALIZE_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\-\._~\!\$&\'\(\)\*\+,;\=]+)|(.?))/u'; + // phpcs:ignore Generic.Files.LineLength + private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x2e\x30-\x39\x3b\x3d\x41-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; /** * The component value @@ -63,7 +64,7 @@ public function __construct($value) throw new InvalidUriComponentException('URI component "password" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZE_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Path.php b/src/Uri/Component/Path.php index 5bafae1..acceac9 100644 --- a/src/Uri/Component/Path.php +++ b/src/Uri/Component/Path.php @@ -36,7 +36,8 @@ final class Path implements ComponentInterface * * @var string */ - private const NORMALIZE_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\-\._~\!\$&\'\(\)\*\+,;\=\:@\/]+)|(.?))/u'; + // phpcs:ignore Generic.Files.LineLength + private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x3b\x3d\x40-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; /** * The component value @@ -63,7 +64,7 @@ public function __construct($value) throw new InvalidUriComponentException('URI component "path" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZE_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Query.php b/src/Uri/Component/Query.php index 09aeaf3..43d5dad 100644 --- a/src/Uri/Component/Query.php +++ b/src/Uri/Component/Query.php @@ -36,7 +36,8 @@ final class Query implements ComponentInterface * * @var string */ - private const NORMALIZE_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\-\._~\!\$&\'\(\)\*\+,;\=\:@\/\?]+)|(.?))/u'; + // phpcs:ignore Generic.Files.LineLength + private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x3b\x3d\x3f-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; /** * The component value @@ -63,7 +64,7 @@ public function __construct($value) throw new InvalidUriComponentException('URI component "query" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZE_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Scheme.php b/src/Uri/Component/Scheme.php index fb4a24c..5a75657 100644 --- a/src/Uri/Component/Scheme.php +++ b/src/Uri/Component/Scheme.php @@ -36,7 +36,7 @@ final class Scheme implements ComponentInterface * * @var string */ - private const VALIDATE_REGEX = '/^(?:[A-Za-z][0-9A-Za-z\+\-\.]*)?$/'; + private const VALIDATION_REGEX = '/^(?:[A-Za-z][0-9A-Za-z\+\-\.]*)?$/'; /** * The component value @@ -63,7 +63,7 @@ public function __construct($value) throw new InvalidUriComponentException('URI component "scheme" must be a string'); } - if (!preg_match(self::VALIDATE_REGEX, $value)) { + if (!preg_match(self::VALIDATION_REGEX, $value)) { throw new InvalidUriComponentException('Invalid URI component "scheme"'); } diff --git a/src/Uri/Component/User.php b/src/Uri/Component/User.php index 8b15bc9..ffda1b9 100644 --- a/src/Uri/Component/User.php +++ b/src/Uri/Component/User.php @@ -36,7 +36,8 @@ final class User implements ComponentInterface * * @var string */ - private const NORMALIZE_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\-\._~\!\$&\'\(\)\*\+,;\=]+)|(.?))/u'; + // phpcs:ignore Generic.Files.LineLength + private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x2e\x30-\x39\x3b\x3d\x41-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; /** * The component value @@ -63,7 +64,7 @@ public function __construct($value) throw new InvalidUriComponentException('URI component "user" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZE_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/tests/Header/ContentMD5HeaderTest.php b/tests/Header/ContentMD5HeaderTest.php deleted file mode 100644 index 0bca2d8..0000000 --- a/tests/Header/ContentMD5HeaderTest.php +++ /dev/null @@ -1,79 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new ContentMD5Header(self::TEST_MD5_DIGEST); - - $this->assertSame('Content-MD5', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new ContentMD5Header(self::TEST_MD5_DIGEST); - - $this->assertSame(self::TEST_MD5_DIGEST, $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The value "" for the header "Content-MD5" is not valid' - ); - - new ContentMD5Header(''); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The value "=invalid md5 digest=" for the header "Content-MD5" is not valid' - ); - - new ContentMD5Header('=invalid md5 digest='); - } - - public function testBuild() - { - $header = new ContentMD5Header(self::TEST_MD5_DIGEST); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new ContentMD5Header(self::TEST_MD5_DIGEST); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/ContentSecurityPolicyHeaderTest.php b/tests/Header/ContentSecurityPolicyHeaderTest.php deleted file mode 100644 index 44b20d0..0000000 --- a/tests/Header/ContentSecurityPolicyHeaderTest.php +++ /dev/null @@ -1,137 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new ContentSecurityPolicyHeader([]); - - $this->assertSame('Content-Security-Policy', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new ContentSecurityPolicyHeader([]); - - $this->assertSame('', $header->getFieldValue()); - } - - public function testParameterWithoutValue() - { - $header = new ContentSecurityPolicyHeader([ - 'foo' => '', - ]); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testParameterWithValue() - { - $header = new ContentSecurityPolicyHeader([ - 'foo' => 'bar', - ]); - - $this->assertSame('foo bar', $header->getFieldValue()); - } - - public function testParameterWithInteger() - { - $header = new ContentSecurityPolicyHeader([ - 'foo' => 1, - ]); - - $this->assertSame('foo 1', $header->getFieldValue()); - } - - public function testSeveralParameters() - { - $header = new ContentSecurityPolicyHeader([ - 'foo' => '', - 'bar' => 'bat', - 'baz' => 1, - ]); - - $this->assertSame('foo; bar bat; baz 1', $header->getFieldValue()); - } - - public function testInvalidParameterName() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "name=" for the header "Content-Security-Policy" is not valid' - ); - - new ContentSecurityPolicyHeader(['name=' => 'value']); - } - - public function testInvalidParameterNameType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "" for the header "Content-Security-Policy" is not valid' - ); - - new ContentSecurityPolicyHeader([0 => 'value']); - } - - public function testInvalidParameterValue() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value ";value" for the header "Content-Security-Policy" is not valid' - ); - - new ContentSecurityPolicyHeader(['name' => ';value']); - } - - public function testInvalidParameterValueType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value "" for the header "Content-Security-Policy" is not valid' - ); - - new ContentSecurityPolicyHeader(['name' => []]); - } - - public function testBuild() - { - $header = new ContentSecurityPolicyHeader(['foo' => 'bar']); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new ContentSecurityPolicyHeader(['foo' => 'bar']); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/ContentSecurityPolicyReportOnlyHeaderTest.php b/tests/Header/ContentSecurityPolicyReportOnlyHeaderTest.php deleted file mode 100644 index a521715..0000000 --- a/tests/Header/ContentSecurityPolicyReportOnlyHeaderTest.php +++ /dev/null @@ -1,137 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new ContentSecurityPolicyReportOnlyHeader([]); - - $this->assertSame('Content-Security-Policy-Report-Only', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new ContentSecurityPolicyReportOnlyHeader([]); - - $this->assertSame('', $header->getFieldValue()); - } - - public function testParameterWithoutValue() - { - $header = new ContentSecurityPolicyReportOnlyHeader([ - 'foo' => '', - ]); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testParameterWithValue() - { - $header = new ContentSecurityPolicyReportOnlyHeader([ - 'foo' => 'bar', - ]); - - $this->assertSame('foo bar', $header->getFieldValue()); - } - - public function testParameterWithInteger() - { - $header = new ContentSecurityPolicyReportOnlyHeader([ - 'foo' => 1, - ]); - - $this->assertSame('foo 1', $header->getFieldValue()); - } - - public function testSeveralParameters() - { - $header = new ContentSecurityPolicyReportOnlyHeader([ - 'foo' => '', - 'bar' => 'bat', - 'baz' => 1, - ]); - - $this->assertSame('foo; bar bat; baz 1', $header->getFieldValue()); - } - - public function testInvalidParameterName() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "name=" for the header "Content-Security-Policy-Report-Only" is not valid' - ); - - new ContentSecurityPolicyReportOnlyHeader(['name=' => 'value']); - } - - public function testInvalidParameterNameType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "" for the header "Content-Security-Policy-Report-Only" is not valid' - ); - - new ContentSecurityPolicyReportOnlyHeader([0 => 'value']); - } - - public function testInvalidParameterValue() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value ";value" for the header "Content-Security-Policy-Report-Only" is not valid' - ); - - new ContentSecurityPolicyReportOnlyHeader(['name' => ';value']); - } - - public function testInvalidParameterValueType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value "" for the header "Content-Security-Policy-Report-Only" is not valid' - ); - - new ContentSecurityPolicyReportOnlyHeader(['name' => []]); - } - - public function testBuild() - { - $header = new ContentSecurityPolicyReportOnlyHeader(['foo' => 'bar']); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new ContentSecurityPolicyReportOnlyHeader(['foo' => 'bar']); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} From 8b35f25f2bf212a6d18e371ce095d5819e7c5d6c Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 15 Mar 2023 01:33:59 +0100 Subject: [PATCH 32/49] delete --- docs/headers.md | 585 ------------------ src/Exception/InvalidHeaderException.php | 19 - src/Header.php | 184 ------ .../AccessControlAllowCredentialsHeader.php | 40 -- .../AccessControlAllowHeadersHeader.php | 68 -- .../AccessControlAllowMethodsHeader.php | 69 --- src/Header/AccessControlAllowOriginHeader.php | 107 ---- .../AccessControlExposeHeadersHeader.php | 68 -- src/Header/AccessControlMaxAgeHeader.php | 87 --- src/Header/AgeHeader.php | 87 --- src/Header/AllowHeader.php | 69 --- src/Header/CacheControlHeader.php | 85 --- src/Header/ClearSiteDataHeader.php | 74 --- src/Header/ContentDispositionHeader.php | 81 --- src/Header/ContentEncodingHeader.php | 80 --- src/Header/ContentLanguageHeader.php | 68 -- src/Header/ContentLengthHeader.php | 87 --- src/Header/ContentLocationHeader.php | 61 -- src/Header/ContentRangeHeader.php | 114 ---- src/Header/ContentTypeHeader.php | 86 --- src/Header/CookieHeader.php | 65 -- src/Header/DateHeader.php | 58 -- src/Header/EtagHeader.php | 66 -- src/Header/ExpiresHeader.php | 58 -- src/Header/LastModifiedHeader.php | 58 -- src/Header/LinkHeader.php | 84 --- src/Header/LocationHeader.php | 61 -- src/Header/RefreshHeader.php | 102 --- src/Header/RetryAfterHeader.php | 58 -- src/Header/SetCookieHeader.php | 246 -------- src/Header/SunsetHeader.php | 58 -- src/Header/TrailerHeader.php | 61 -- src/Header/TransferEncodingHeader.php | 79 --- src/Header/VaryHeader.php | 68 -- src/Header/WWWAuthenticateHeader.php | 104 ---- src/Header/WarningHeader.php | 131 ---- src/HeaderUtils.php | 43 -- ...ccessControlAllowCredentialsHeaderTest.php | 55 -- .../AccessControlAllowHeadersHeaderTest.php | 96 --- .../AccessControlAllowMethodsHeaderTest.php | 103 --- .../AccessControlAllowOriginHeaderTest.php | 106 ---- .../AccessControlExposeHeadersHeaderTest.php | 96 --- .../Header/AccessControlMaxAgeHeaderTest.php | 90 --- tests/Header/AgeHeaderTest.php | 90 --- tests/Header/AllowHeaderTest.php | 103 --- tests/Header/CacheControlHeaderTest.php | 147 ----- tests/Header/ClearSiteDataHeaderTest.php | 94 --- tests/Header/ContentDispositionHeaderTest.php | 166 ----- tests/Header/ContentEncodingHeaderTest.php | 105 ---- tests/Header/ContentLanguageHeaderTest.php | 114 ---- tests/Header/ContentLengthHeaderTest.php | 63 -- tests/Header/ContentLocationHeaderTest.php | 61 -- tests/Header/ContentRangeHeaderTest.php | 91 --- tests/Header/ContentTypeHeaderTest.php | 168 ----- tests/Header/CookieHeaderTest.php | 61 -- tests/Header/DateHeaderTest.php | 82 --- tests/Header/EtagHeaderTest.php | 71 --- tests/Header/ExpiresHeaderTest.php | 82 --- tests/Header/LastModifiedHeaderTest.php | 82 --- tests/Header/LinkHeaderTest.php | 174 ------ tests/Header/LocationHeaderTest.php | 61 -- tests/Header/RefreshHeaderTest.php | 71 --- tests/Header/RetryAfterHeaderTest.php | 82 --- tests/Header/SetCookieHeaderTest.php | 257 -------- tests/Header/SunsetHeaderTest.php | 82 --- tests/Header/TrailerHeaderTest.php | 78 --- tests/Header/TransferEncodingHeaderTest.php | 104 ---- tests/Header/VaryHeaderTest.php | 96 --- tests/Header/WWWAuthenticateHeaderTest.php | 180 ------ tests/Header/WarningHeaderTest.php | 134 ---- 70 files changed, 6964 deletions(-) delete mode 100644 docs/headers.md delete mode 100644 src/Exception/InvalidHeaderException.php delete mode 100644 src/Header.php delete mode 100644 src/Header/AccessControlAllowCredentialsHeader.php delete mode 100644 src/Header/AccessControlAllowHeadersHeader.php delete mode 100644 src/Header/AccessControlAllowMethodsHeader.php delete mode 100644 src/Header/AccessControlAllowOriginHeader.php delete mode 100644 src/Header/AccessControlExposeHeadersHeader.php delete mode 100644 src/Header/AccessControlMaxAgeHeader.php delete mode 100644 src/Header/AgeHeader.php delete mode 100644 src/Header/AllowHeader.php delete mode 100644 src/Header/CacheControlHeader.php delete mode 100644 src/Header/ClearSiteDataHeader.php delete mode 100644 src/Header/ContentDispositionHeader.php delete mode 100644 src/Header/ContentEncodingHeader.php delete mode 100644 src/Header/ContentLanguageHeader.php delete mode 100644 src/Header/ContentLengthHeader.php delete mode 100644 src/Header/ContentLocationHeader.php delete mode 100644 src/Header/ContentRangeHeader.php delete mode 100644 src/Header/ContentTypeHeader.php delete mode 100644 src/Header/CookieHeader.php delete mode 100644 src/Header/DateHeader.php delete mode 100644 src/Header/EtagHeader.php delete mode 100644 src/Header/ExpiresHeader.php delete mode 100644 src/Header/LastModifiedHeader.php delete mode 100644 src/Header/LinkHeader.php delete mode 100644 src/Header/LocationHeader.php delete mode 100644 src/Header/RefreshHeader.php delete mode 100644 src/Header/RetryAfterHeader.php delete mode 100644 src/Header/SetCookieHeader.php delete mode 100644 src/Header/SunsetHeader.php delete mode 100644 src/Header/TrailerHeader.php delete mode 100644 src/Header/TransferEncodingHeader.php delete mode 100644 src/Header/VaryHeader.php delete mode 100644 src/Header/WWWAuthenticateHeader.php delete mode 100644 src/Header/WarningHeader.php delete mode 100644 src/HeaderUtils.php delete mode 100644 tests/Header/AccessControlAllowCredentialsHeaderTest.php delete mode 100644 tests/Header/AccessControlAllowHeadersHeaderTest.php delete mode 100644 tests/Header/AccessControlAllowMethodsHeaderTest.php delete mode 100644 tests/Header/AccessControlAllowOriginHeaderTest.php delete mode 100644 tests/Header/AccessControlExposeHeadersHeaderTest.php delete mode 100644 tests/Header/AccessControlMaxAgeHeaderTest.php delete mode 100644 tests/Header/AgeHeaderTest.php delete mode 100644 tests/Header/AllowHeaderTest.php delete mode 100644 tests/Header/CacheControlHeaderTest.php delete mode 100644 tests/Header/ClearSiteDataHeaderTest.php delete mode 100644 tests/Header/ContentDispositionHeaderTest.php delete mode 100644 tests/Header/ContentEncodingHeaderTest.php delete mode 100644 tests/Header/ContentLanguageHeaderTest.php delete mode 100644 tests/Header/ContentLengthHeaderTest.php delete mode 100644 tests/Header/ContentLocationHeaderTest.php delete mode 100644 tests/Header/ContentRangeHeaderTest.php delete mode 100644 tests/Header/ContentTypeHeaderTest.php delete mode 100644 tests/Header/CookieHeaderTest.php delete mode 100644 tests/Header/DateHeaderTest.php delete mode 100644 tests/Header/EtagHeaderTest.php delete mode 100644 tests/Header/ExpiresHeaderTest.php delete mode 100644 tests/Header/LastModifiedHeaderTest.php delete mode 100644 tests/Header/LinkHeaderTest.php delete mode 100644 tests/Header/LocationHeaderTest.php delete mode 100644 tests/Header/RefreshHeaderTest.php delete mode 100644 tests/Header/RetryAfterHeaderTest.php delete mode 100644 tests/Header/SetCookieHeaderTest.php delete mode 100644 tests/Header/SunsetHeaderTest.php delete mode 100644 tests/Header/TrailerHeaderTest.php delete mode 100644 tests/Header/TransferEncodingHeaderTest.php delete mode 100644 tests/Header/VaryHeaderTest.php delete mode 100644 tests/Header/WWWAuthenticateHeaderTest.php delete mode 100644 tests/Header/WarningHeaderTest.php diff --git a/docs/headers.md b/docs/headers.md deleted file mode 100644 index 56c2c6c..0000000 --- a/docs/headers.md +++ /dev/null @@ -1,585 +0,0 @@ -### HTTP Headers - -#### Access-Control-Allow-Credentials - -> Usage link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials - -```php -use Sunrise\Http\Message\Header\AccessControlAllowCredentialsHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new AccessControlAllowCredentialsHeader(); -$response = $response->withHeader(...$header); -``` - -#### Access-Control-Allow-Headers - -> Usage link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers - -```php -use Sunrise\Http\Message\Header\AccessControlAllowHeadersHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new AccessControlAllowHeadersHeader('X-Custom-Header', 'Upgrade-Insecure-Requests'); -$response = $response->withHeader(...$header); -``` - -#### Access-Control-Allow-Methods - -> Usage link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods - -```php -use Sunrise\Http\Message\Header\AccessControlAllowMethodsHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new AccessControlAllowMethodsHeader('OPTIONS', 'HEAD', 'GET'); -$response = $response->withHeader(...$header); -``` - -#### Access-Control-Allow-Origin - -> Usage link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin - -```php -use Sunrise\Http\Message\Header\AccessControlAllowOriginHeader; -use Sunrise\Http\Message\ResponseFactory; -use Sunrise\Uri\UriFactory; - -$response = (new ResponseFactory)->createResponse(); - -// A response that tells the browser to allow code from any origin to access -// a resource will include the following: -$header = new AccessControlAllowOriginHeader(null); -$response = $response->withHeader(...$header); - -// A response that tells the browser to allow requesting code from the origin -// https://developer.mozilla.org to access a resource will include the following: -$uri = (new UriFactory)->createUri('https://developer.mozilla.org'); -$header = new AccessControlAllowOriginHeader($uri); -$response = $response->withHeader(...$header); -``` - -#### Access-Control-Expose-Headers - -> Usage link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers - -```php -use Sunrise\Http\Message\Header\AccessControlExposeHeadersHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new AccessControlExposeHeadersHeader('Content-Length', 'X-Kuma-Revision'); -$response = $response->withHeader(...$header); -``` - -#### Access-Control-Max-Age - -> Usage link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age - -```php -use Sunrise\Http\Message\Header\AccessControlMaxAgeHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new AccessControlMaxAgeHeader(600); -$response = $response->withHeader(...$header); -``` - -#### Age - -> Usage link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Age - -```php -use Sunrise\Http\Message\Header\AgeHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new AgeHeader(24); -$response = $response->withHeader(...$header); -``` - -#### Allow - -> Usage link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Allow - -```php -use Sunrise\Http\Message\Header\AllowHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new AllowHeader('OPTIONS', 'HEAD', 'GET'); -$response = $response->withHeader(...$header); -``` - -#### Cache-Control - -> Usage link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control - -```php -use Sunrise\Http\Message\Header\CacheControlHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -// Preventing caching -$header = new CacheControlHeader(['no-cache' => '', 'no-store' => '', 'must-revalidate' => '']); -$response = $response->withHeader(...$header); - -// Caching static assets -$header = new CacheControlHeader(['public' => '', 'max-age' => '31536000']); -$response = $response->withHeader(...$header); -``` - -#### Clear-Site-Data - -> Usage link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data - -```php -use Sunrise\Http\Message\Header\ClearSiteDataHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -// Single directive -$header = new ClearSiteDataHeader(['cache']); -$response = $response->withHeader(...$header); - -// Multiple directives (comma separated) -$header = new ClearSiteDataHeader(['cache', 'cookies']); -$response = $response->withHeader(...$header); - -// Wild card -$header = new ClearSiteDataHeader(['*']); -$response = $response->withHeader(...$header); -``` - -#### Content-Disposition - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition - -```php -use Sunrise\Http\Message\Header\ContentDispositionHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -// As a response header for the main body -$header = new ContentDispositionHeader('attachment', ['filename' => 'filename.jpg']); -$response = $response->withHeader(...$header); - -// As a header for a multipart body -$header = new ContentDispositionHeader('form-data', ['name' => 'fieldName', 'filename' => 'filename.jpg']); -$response = $response->withHeader(...$header); -``` - -#### Content-Encoding - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding - -```php -use Sunrise\Http\Message\Header\ContentEncodingHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new ContentEncodingHeader('gzip'); -$response = $response->withHeader(...$header); -``` - -#### Content-Language - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Language - -```php -use Sunrise\Http\Message\Header\ContentLanguageHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new ContentLanguageHeader('de-DE', 'en-CA'); -$response = $response->withHeader(...$header); -``` - -#### Content-Length - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length - -```php -use Sunrise\Http\Message\Header\ContentLengthHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new ContentLengthHeader(4096); -$response = $response->withHeader(...$header); -``` - -#### Content-Location - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Location - -```php -use Sunrise\Http\Message\Header\ContentLocationHeader; -use Sunrise\Http\Message\ResponseFactory; -use Sunrise\Uri\UriFactory; - -$response = (new ResponseFactory)->createResponse(); - -$uri = (new UriFactory)->createUri('https://example.com/documents/foo'); -$header = new ContentLocationHeader($uri); -$response = $response->withHeader(...$header); -``` - -#### Content-MD5 - -> Useful link: https://tools.ietf.org/html/rfc1864 - -```php -use Sunrise\Http\Message\Header\ContentMD5Header; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new ContentMD5Header('MzAyMWU2OGRmOWE3MjAwMTM1NzI1YzYzMzEzNjlhMjI='); -$response = $response->withHeader(...$header); -``` - -#### Content-Range - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range - -```php -use Sunrise\Http\Message\Header\ContentRangeHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new ContentRangeHeader( - 200, // An integer in the given unit indicating the beginning of the request range. - 1000, // An integer in the given unit indicating the end of the requested range. - 67589 // The total size of the document. -); -$response = $response->withHeader(...$header); -``` - -#### Content-Security-Policy - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy - -```php -use Sunrise\Http\Message\Header\ContentSecurityPolicyHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -// Pre-existing site that uses too much inline code to fix but wants -// to ensure resources are loaded only over https and disable plugins: -$header = new ContentSecurityPolicyHeader(['default-src' => "https: 'unsafe-eval' 'unsafe-inline'", 'object-src' => "'none'"]); -$response = $response->withAddedHeader(...$header); - -// Don't implement the above policy yet; instead just report -// violations that would have occurred: -$header = new ContentSecurityPolicyHeader(['default-src' => 'https:', 'report-uri' => '/csp-violation-report-endpoint/']); -$response = $response->withAddedHeader(...$header); -``` - -#### Content-Security-Policy-Report-Only - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only - -```php -use Sunrise\Http\Message\Header\ContentSecurityPolicyReportOnlyHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -// This header reports violations that would have occurred. -// You can use this to iteratively work on your content security policy. -// You observe how your site behaves, watching for violation reports, -// then choose the desired policy enforced by the Content-Security-Policy header. -$header = new ContentSecurityPolicyReportOnlyHeader(['default-src' => 'https:', 'report-uri' => '/csp-violation-report-endpoint/']); -$response = $response->withAddedHeader(...$header); - -// If you still want to receive reporting, but also want -// to enforce a policy, use the Content-Security-Policy header with the report-uri directive. -$header = new ContentSecurityPolicyReportOnlyHeader(['default-src' => 'https:', 'report-uri' => '/csp-violation-report-endpoint/']); -$response = $response->withAddedHeader(...$header); -``` - -#### Content-Type - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type - -```php -use Sunrise\Http\Message\Header\ContentTypeHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new ContentTypeHeader('application', 'json', ['charset' => 'utf-8']); -$response = $response->withHeader(...$header); -``` - -#### Cookie - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie - -```php -use Sunrise\Http\Message\Header\CookieHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new CookieHeader(['name' => 'value', 'name2' => 'value2', 'name3' => 'value3']); -$response = $response->withHeader(...$header); -``` - -#### Date - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Date - -```php -use DateTime; -use Sunrise\Http\Message\Header\DateHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new DateHeader(new DateTime('now')); -$response = $response->withHeader(...$header); -``` - -#### Etag - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag - -```php -use Sunrise\Http\Message\Header\EtagHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new EtagHeader('33a64df551425fcc55e4d42a148795d9f25f89d4'); -$response = $response->withHeader(...$header); -``` - -#### Expires - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires - -```php -use DateTime; -use Sunrise\Http\Message\Header\ExpiresHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new ExpiresHeader(new DateTime('1 day ago')); -$response = $response->withHeader(...$header); -``` - -#### Last-Modified - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified - -```php -use DateTime; -use Sunrise\Http\Message\Header\LastModifiedHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new LastModifiedHeader(new DateTime('1 year ago')); -$response = $response->withHeader(...$header); -``` - -#### Link - -> Useful link: https://www.w3.org/wiki/LinkHeader - -```php -use Sunrise\Http\Message\Header\LinkHeader; -use Sunrise\Http\Message\ResponseFactory; -use Sunrise\Uri\UriFactory; - -$response = (new ResponseFactory)->createResponse(); - -$uri = (new UriFactory)->createUri('meta.rdf'); -$header = new LinkHeader($uri, ['rel' => 'meta']); -$response = $response->withHeader(...$header); -``` - -#### Location - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location - -```php -use Sunrise\Http\Message\Header\LocationHeader; -use Sunrise\Http\Message\ResponseFactory; -use Sunrise\Uri\UriFactory; - -$response = (new ResponseFactory)->createResponse(); - -$uri = (new UriFactory)->createUri('/'); -$header = new LocationHeader($uri); -$response = $response->withHeader(...$header); -``` - -#### Refresh - -> Useful link: https://en.wikipedia.org/wiki/Meta_refresh - -```php -use Sunrise\Http\Message\Header\RefreshHeader; -use Sunrise\Http\Message\ResponseFactory; -use Sunrise\Uri\UriFactory; - -$response = (new ResponseFactory)->createResponse(); - -$uri = (new UriFactory)->createUri('/login'); -$header = new RefreshHeader(3, $uri); -$response = $response->withHeader(...$header); -``` - -#### Retry-After - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After - -```php -use DateTime; -use Sunrise\Http\Message\Header\RetryAfterHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new RetryAfterHeader(new DateTime('+30 second')); -$response = $response->withHeader(...$header); -``` - -#### Set-Cookie - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie - -```php -use DateTime; -use Sunrise\Http\Message\Header\SetCookieHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -// Session cookie -// Session cookies will get removed when the client is shut down. -// They don't specify the Expires or Max-Age directives. -// Note that web browser have often enabled session restoring. -$header = new SetCookieHeader('sessionid', '38afes7a8', null, ['path' => '/', 'httponly' => true]); -$response = $response->withAddedHeader(...$header); - -// Permanent cookie -// Instead of expiring when the client is closed, permanent cookies expire -// at a specific date (Expires) or after a specific length of time (Max-Age). -$header = new SetCookieHeader('id', 'a3fWa', new DateTime('+1 day'), ['secure' => true, 'httponly' => true]); -$response = $response->withAddedHeader(...$header); - -// Invalid domains -// A cookie belonging to a domain that does not include the origin server -// should be rejected by the user agent. The following cookie will be rejected -// if it was set by a server hosted on originalcompany.com. -$header = new SetCookieHeader('qwerty', '219ffwef9w0f', new DateTime('+1 day'), ['domain' => 'somecompany.co.uk', 'path' => '/']); -$response = $response->withAddedHeader(...$header); -``` - -#### Sunset - -> Useful link: https://tools.ietf.org/id/draft-wilde-sunset-header-03.html - -```php -use DateTime; -use Sunrise\Http\Message\Header\SunsetHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new SunsetHeader(new DateTime('2038-01-19 03:14:07')); -$response = $response->withHeader(...$header); -``` - -#### Trailer - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer - -```php -use Sunrise\Http\Message\Header\TrailerHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new TrailerHeader('Expires', 'X-Streaming-Error'); -$response = $response->withHeader(...$header); -``` - -#### Transfer-Encoding - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding - -```php -use Sunrise\Http\Message\Header\TransferEncodingHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new TransferEncodingHeader('gzip', 'chunked'); -$response = $response->withHeader(...$header); -``` - -#### Vary - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary - -```php -use Sunrise\Http\Message\Header\VaryHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new VaryHeader('User-Agent', 'Content-Language'); -$response = $response->withHeader(...$header); -``` - -#### WWW-Authenticate - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate - -```php -use Sunrise\Http\Message\Header\WWWAuthenticateHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new WWWAuthenticateHeader(WWWAuthenticateHeader::HTTP_AUTHENTICATE_SCHEME_BASIC, ['realm' => 'Access to the staging site', 'charset' => 'UTF-8']); -$response = $response->withHeader(...$header); -``` - -#### Warning - -> Useful link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Warning - -```php -use DateTime; -use Sunrise\Http\Message\Header\WarningHeader; -use Sunrise\Http\Message\ResponseFactory; - -$response = (new ResponseFactory)->createResponse(); - -$header = new WarningHeader(WarningHeader::HTTP_WARNING_CODE_RESPONSE_IS_STALE, 'anderson/1.3.37', 'Response is stale', new DateTime('now')); -$response = $response->withHeader(...$header); -``` diff --git a/src/Exception/InvalidHeaderException.php b/src/Exception/InvalidHeaderException.php deleted file mode 100644 index 5a60100..0000000 --- a/src/Exception/InvalidHeaderException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * InvalidHeaderException - */ -class InvalidHeaderException extends InvalidArgumentException -{ -} diff --git a/src/Header.php b/src/Header.php deleted file mode 100644 index 02755c0..0000000 --- a/src/Header.php +++ /dev/null @@ -1,184 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use DateTime; -use DateTimeImmutable; -use DateTimeInterface; -use DateTimeZone; -use Traversable; - -/** - * Import functions - */ -use function gettype; -use function is_int; -use function is_string; -use function preg_match; -use function sprintf; - -/** - * HTTP Header Field - */ -abstract class Header implements HeaderInterface -{ - - /** - * {@inheritdoc} - */ - final public function getIterator(): Traversable - { - yield $this->getFieldName(); - yield $this->getFieldValue(); - } - - /** - * {@inheritdoc} - */ - final public function __toString(): string - { - return sprintf('%s: %s', $this->getFieldName(), $this->getFieldValue()); - } - - /** - * Checks if the given string is a token - * - * @param string $token - * - * @return bool - */ - final protected function isToken(string $token): bool - { - return preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $token) === 1; - } - - /** - * Validates the given token(s) - * - * @param string ...$tokens - * - * @return void - * - * @throws InvalidHeaderException - * If one of the tokens isn't valid. - */ - final protected function validateToken(string ...$tokens): void - { - $this->validateValueByRegex(HeaderInterface::RFC7230_TOKEN_REGEX, ...$tokens); - } - - /** - * Validates the given quoted string(s) - * - * @param string ...$quotedStrings - * - * @return void - * - * @throws InvalidHeaderException - * If one of the quoted strings isn't valid. - */ - final protected function validateQuotedString(string ...$quotedStrings): void - { - $this->validateValueByRegex(HeaderInterface::RFC7230_QUOTED_STRING_REGEX, ...$quotedStrings); - } - - /** - * Validates and normalizes the given parameters - * - * @param array $parameters - * - * @return array - * The normalized parameters. - * - * @throws InvalidHeaderException - * If one of the parameters isn't valid. - */ - final protected function validateParameters(array $parameters): array - { - return $this->validateParametersByRegex( - $parameters, - HeaderInterface::RFC7230_TOKEN_REGEX, - HeaderInterface::RFC7230_QUOTED_STRING_REGEX - ); - } - - /** - * Validates the given value(s) by the given regular expression - * - * @param string $regex - * @param string ...$values - * - * @return void - * - * @throws InvalidHeaderException - * If one of the values isn't valid. - */ - final protected function validateValueByRegex(string $regex, string ...$values): void - { - foreach ($values as $value) { - if (!preg_match($regex, $value)) { - throw new InvalidHeaderException(sprintf( - 'The value "%2$s" for the header "%1$s" is not valid', - $this->getFieldName(), - $value - )); - } - } - } - - /** - * Validates and normalizes the given parameters by the given regular expressions - * - * @param array $parameters - * @param string $nameRegex - * @param string $valueRegex - * - * @return array - * The normalized parameters. - * - * @throws InvalidHeaderException - * If one of the parameters isn't valid. - */ - final protected function validateParametersByRegex(array $parameters, string $nameRegex, string $valueRegex): array - { - foreach ($parameters as $name => &$value) { - if (!is_string($name) || !preg_match($nameRegex, $name)) { - throw new InvalidHeaderException(sprintf( - 'The parameter name "%2$s" for the header "%1$s" is not valid', - $this->getFieldName(), - (is_string($name) ? $name : ('<' . gettype($name) . '>')) - )); - } - - // e.g. Cache-Control: max-age=31536000 - if (is_int($value)) { - $value = (string) $value; - } - - if (!is_string($value) || !preg_match($valueRegex, $value)) { - throw new InvalidHeaderException(sprintf( - 'The parameter value "%2$s" for the header "%1$s" is not valid', - $this->getFieldName(), - (is_string($value) ? $value : ('<' . gettype($value) . '>')) - )); - } - } - - /** @var array $parameters */ - - return $parameters; - } -} diff --git a/src/Header/AccessControlAllowCredentialsHeader.php b/src/Header/AccessControlAllowCredentialsHeader.php deleted file mode 100644 index c96232a..0000000 --- a/src/Header/AccessControlAllowCredentialsHeader.php +++ /dev/null @@ -1,40 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Header; - -/** - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials - */ -class AccessControlAllowCredentialsHeader extends Header -{ - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Access-Control-Allow-Credentials'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return 'true'; - } -} diff --git a/src/Header/AccessControlAllowHeadersHeader.php b/src/Header/AccessControlAllowHeadersHeader.php deleted file mode 100644 index 46af826..0000000 --- a/src/Header/AccessControlAllowHeadersHeader.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; - -/** - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers - */ -class AccessControlAllowHeadersHeader extends Header -{ - - /** - * @var list - */ - private array $headers = []; - - /** - * Constructor of the class - * - * @param string ...$headers - * - * @throws InvalidHeaderException - * If one of the header names isn't valid. - */ - public function __construct(string ...$headers) - { - $this->validateToken(...$headers); - - foreach ($headers as $header) { - $this->headers[] = $header; - } - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Access-Control-Allow-Headers'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return implode(', ', $this->headers); - } -} diff --git a/src/Header/AccessControlAllowMethodsHeader.php b/src/Header/AccessControlAllowMethodsHeader.php deleted file mode 100644 index b2020b5..0000000 --- a/src/Header/AccessControlAllowMethodsHeader.php +++ /dev/null @@ -1,69 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; -use function strtoupper; - -/** - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods - */ -class AccessControlAllowMethodsHeader extends Header -{ - - /** - * @var list - */ - private array $methods = []; - - /** - * Constructor of the class - * - * @param string ...$methods - * - * @throws InvalidHeaderException - * If one of the methods isn't valid. - */ - public function __construct(string ...$methods) - { - $this->validateToken(...$methods); - - foreach ($methods as $method) { - $this->methods[] = strtoupper($method); - } - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Access-Control-Allow-Methods'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return implode(', ', $this->methods); - } -} diff --git a/src/Header/AccessControlAllowOriginHeader.php b/src/Header/AccessControlAllowOriginHeader.php deleted file mode 100644 index 78adf10..0000000 --- a/src/Header/AccessControlAllowOriginHeader.php +++ /dev/null @@ -1,107 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Psr\Http\Message\UriInterface; -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Exception\InvalidUriException; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\Uri; - -/** - * Import functions - */ -use function sprintf; - -/** - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin - */ -class AccessControlAllowOriginHeader extends Header -{ - - /** - * @var UriInterface|null - */ - private ?UriInterface $uri = null; - - /** - * Constructor of the class - * - * @param mixed $uri - * - * @throws InvalidUriException - * If the URI isn't valid. - * - * @throws InvalidHeaderException - * If the URI isn't valid. - */ - public function __construct($uri = null) - { - if (isset($uri)) { - $uri = Uri::create($uri); - $this->validateUri($uri); - $this->uri = $uri; - } - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Access-Control-Allow-Origin'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - if (!isset($this->uri)) { - return '*'; - } - - $origin = $this->uri->getScheme() . ':'; - $origin .= '//' . $this->uri->getHost(); - - $port = $this->uri->getPort(); - if (isset($port)) { - $origin .= ':' . $port; - } - - return $origin; - } - - /** - * Validates the given URI - * - * @param UriInterface $uri - * - * @return void - * - * @throws InvalidHeaderException - * If the URI isn't valid. - */ - private function validateUri(UriInterface $uri): void - { - if ($uri->getScheme() === '' || $uri->getHost() === '') { - throw new InvalidHeaderException(sprintf( - 'The URI "%2$s" for the header "%1$s" is not valid', - $this->getFieldName(), - $uri->__toString() - )); - } - } -} diff --git a/src/Header/AccessControlExposeHeadersHeader.php b/src/Header/AccessControlExposeHeadersHeader.php deleted file mode 100644 index 554aea2..0000000 --- a/src/Header/AccessControlExposeHeadersHeader.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; - -/** - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers - */ -class AccessControlExposeHeadersHeader extends Header -{ - - /** - * @var list - */ - private array $headers = []; - - /** - * Constructor of the class - * - * @param string ...$headers - * - * @throws InvalidHeaderException - * If one of the header names isn't valid. - */ - public function __construct(string ...$headers) - { - $this->validateToken(...$headers); - - foreach ($headers as $header) { - $this->headers[] = $header; - } - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Access-Control-Expose-Headers'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return implode(', ', $this->headers); - } -} diff --git a/src/Header/AccessControlMaxAgeHeader.php b/src/Header/AccessControlMaxAgeHeader.php deleted file mode 100644 index 33e3f2a..0000000 --- a/src/Header/AccessControlMaxAgeHeader.php +++ /dev/null @@ -1,87 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function sprintf; - -/** - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age - */ -class AccessControlMaxAgeHeader extends Header -{ - - /** - * @var int - */ - private int $value; - - /** - * Constructor of the class - * - * @param int $value - * - * @throws InvalidHeaderException - * If the value isn't valid. - */ - public function __construct(int $value) - { - $this->validateValue($value); - - $this->value = $value; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Access-Control-Max-Age'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return sprintf('%d', $this->value); - } - - /** - * Validates the given value - * - * @param int $value - * - * @return void - * - * @throws InvalidHeaderException - * If the value isn't valid. - */ - private function validateValue(int $value): void - { - if (! ($value === -1 || $value >= 1)) { - throw new InvalidHeaderException(sprintf( - 'The value "%2$d" for the header "%1$s" is not valid.', - $this->getFieldName(), - $value - )); - } - } -} diff --git a/src/Header/AgeHeader.php b/src/Header/AgeHeader.php deleted file mode 100644 index 4b007d5..0000000 --- a/src/Header/AgeHeader.php +++ /dev/null @@ -1,87 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function sprintf; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.6 - */ -class AgeHeader extends Header -{ - - /** - * @var int - */ - private int $value; - - /** - * Constructor of the class - * - * @param int $value - * - * @throws InvalidHeaderException - * If the value isn't valid. - */ - public function __construct(int $value) - { - $this->validateValue($value); - - $this->value = $value; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Age'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return sprintf('%d', $this->value); - } - - /** - * Validates the given value - * - * @param int $value - * - * @return void - * - * @throws InvalidHeaderException - * If the value isn't valid. - */ - private function validateValue(int $value): void - { - if (! ($value >= 0)) { - throw new InvalidHeaderException(sprintf( - 'The value "%2$d" for the header "%1$s" is not valid', - $this->getFieldName(), - $value - )); - } - } -} diff --git a/src/Header/AllowHeader.php b/src/Header/AllowHeader.php deleted file mode 100644 index c03cfd0..0000000 --- a/src/Header/AllowHeader.php +++ /dev/null @@ -1,69 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; -use function strtoupper; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.7 - */ -class AllowHeader extends Header -{ - - /** - * @var list - */ - private array $methods = []; - - /** - * Constructor of the class - * - * @param string ...$methods - * - * @throws InvalidHeaderException - * If one of the methods isn't valid. - */ - public function __construct(string ...$methods) - { - $this->validateToken(...$methods); - - foreach ($methods as $method) { - $this->methods[] = strtoupper($method); - } - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Allow'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return implode(', ', $this->methods); - } -} diff --git a/src/Header/CacheControlHeader.php b/src/Header/CacheControlHeader.php deleted file mode 100644 index c7ed9c5..0000000 --- a/src/Header/CacheControlHeader.php +++ /dev/null @@ -1,85 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; -use function sprintf; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.9 - */ -class CacheControlHeader extends Header -{ - - /** - * @var array - */ - private array $parameters; - - /** - * Constructor of the class - * - * @param array $parameters - * - * @throws InvalidHeaderException - * If the parameters aren't valid. - */ - public function __construct(array $parameters) - { - $parameters = $this->validateParameters($parameters); - - $this->parameters = $parameters; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Cache-Control'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - $segments = []; - foreach ($this->parameters as $name => $value) { - // e.g., no-cache - if ($value === '') { - $segments[] = $name; - continue; - } - - // e.g., max-age=604800 - if ($this->isToken($value)) { - $segments[] = sprintf('%s=%s', $name, $value); - continue; - } - - // e.g., community="UCI" - $segments[] = sprintf('%s="%s"', $name, $value); - } - - return implode(', ', $segments); - } -} diff --git a/src/Header/ClearSiteDataHeader.php b/src/Header/ClearSiteDataHeader.php deleted file mode 100644 index 865545b..0000000 --- a/src/Header/ClearSiteDataHeader.php +++ /dev/null @@ -1,74 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; -use function sprintf; - -/** - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data - */ -class ClearSiteDataHeader extends Header -{ - - /** - * @var list - */ - private array $directives = []; - - /** - * Constructor of the class - * - * @param string ...$directives - * - * @throws InvalidHeaderException - * If one of the directives isn't valid. - */ - public function __construct(string ...$directives) - { - $this->validateQuotedString(...$directives); - - foreach ($directives as $directive) { - $this->directives[] = $directive; - } - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Clear-Site-Data'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - $segments = []; - foreach ($this->directives as $directive) { - $segments[] = sprintf('"%s"', $directive); - } - - return implode(', ', $segments); - } -} diff --git a/src/Header/ContentDispositionHeader.php b/src/Header/ContentDispositionHeader.php deleted file mode 100644 index bdbe9e6..0000000 --- a/src/Header/ContentDispositionHeader.php +++ /dev/null @@ -1,81 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function sprintf; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-19.5.1 - */ -class ContentDispositionHeader extends Header -{ - - /** - * @var string - */ - private string $type; - - /** - * @var array - */ - private array $parameters; - - /** - * Constructor of the class - * - * @param string $type - * @param array $parameters - * - * @throws InvalidHeaderException - * - If the type isn't valid; - * - If the parameters aren't valid. - */ - public function __construct(string $type, array $parameters = []) - { - $this->validateToken($type); - - $parameters = $this->validateParameters($parameters); - - $this->type = $type; - $this->parameters = $parameters; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Content-Disposition'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - $v = $this->type; - foreach ($this->parameters as $name => $value) { - $v .= sprintf('; %s="%s"', $name, $value); - } - - return $v; - } -} diff --git a/src/Header/ContentEncodingHeader.php b/src/Header/ContentEncodingHeader.php deleted file mode 100644 index f397369..0000000 --- a/src/Header/ContentEncodingHeader.php +++ /dev/null @@ -1,80 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Dictionary\Encoding; -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.11 - */ -class ContentEncodingHeader extends Header -{ - - /** - * HTTP Content Encodings - * - * @link https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#content-coding - */ - public const HTTP_CONTENT_ENCODING_AES128GCM = 'aes128gcm'; - public const HTTP_CONTENT_ENCODING_BR = 'br'; - public const HTTP_CONTENT_ENCODING_COMPRESS = 'compress'; - public const HTTP_CONTENT_ENCODING_DEFLATE = 'deflate'; - public const HTTP_CONTENT_ENCODING_GZIP = 'gzip'; - - /** - * @var list - */ - private array $directives = []; - - /** - * Constructor of the class - * - * @param string ...$directives - * - * @throws InvalidHeaderException - * If one of the directives isn't valid. - */ - public function __construct(string ...$directives) - { - $this->validateToken(...$directives); - - foreach ($directives as $directive) { - $this->directives[] = $directive; - } - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Content-Encoding'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return implode(', ', $this->directives); - } -} diff --git a/src/Header/ContentLanguageHeader.php b/src/Header/ContentLanguageHeader.php deleted file mode 100644 index e4cd204..0000000 --- a/src/Header/ContentLanguageHeader.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.12 - */ -class ContentLanguageHeader extends Header -{ - - /** - * @var list - */ - private array $languages = []; - - /** - * Constructor of the class - * - * @param string ...$languages - * - * @throws InvalidHeaderException - * If one of the language codes isn't valid. - */ - public function __construct(string ...$languages) - { - $this->validateToken(...$languages); - - foreach ($languages as $language) { - $this->languages[] = $language; - } - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Content-Language'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return implode(', ', $this->languages); - } -} diff --git a/src/Header/ContentLengthHeader.php b/src/Header/ContentLengthHeader.php deleted file mode 100644 index 673cbbd..0000000 --- a/src/Header/ContentLengthHeader.php +++ /dev/null @@ -1,87 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function sprintf; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.13 - */ -class ContentLengthHeader extends Header -{ - - /** - * @var int - */ - private int $value; - - /** - * Constructor of the class - * - * @param int<0, max> $value - * - * @throws InvalidHeaderException - * If the value isn't valid. - */ - public function __construct(int $value) - { - $this->validateValue($value); - - $this->value = $value; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Content-Length'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return sprintf('%d', $this->value); - } - - /** - * Validates the given value - * - * @param int $value - * - * @return void - * - * @throws InvalidHeaderException - * If the value isn't valid. - */ - private function validateValue(int $value): void - { - if (! ($value >= 0)) { - throw new InvalidHeaderException(sprintf( - 'The value "%2$d" for the header "%1$s" is not valid', - $this->getFieldName(), - $value - )); - } - } -} diff --git a/src/Header/ContentLocationHeader.php b/src/Header/ContentLocationHeader.php deleted file mode 100644 index c7852e8..0000000 --- a/src/Header/ContentLocationHeader.php +++ /dev/null @@ -1,61 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Psr\Http\Message\UriInterface; -use Sunrise\Http\Message\Exception\InvalidUriException; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\Uri; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.14 - */ -class ContentLocationHeader extends Header -{ - - /** - * @var UriInterface - */ - private UriInterface $uri; - - /** - * Constructor of the class - * - * @param mixed $uri - * - * @throws InvalidUriException - * If the URI isn't valid. - */ - public function __construct($uri) - { - $this->uri = Uri::create($uri); - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Content-Location'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return $this->uri->__toString(); - } -} diff --git a/src/Header/ContentRangeHeader.php b/src/Header/ContentRangeHeader.php deleted file mode 100644 index f90c7be..0000000 --- a/src/Header/ContentRangeHeader.php +++ /dev/null @@ -1,114 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function sprintf; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.16 - */ -class ContentRangeHeader extends Header -{ - - /** - * @var int - */ - private int $firstBytePosition; - - /** - * @var int - */ - private int $lastBytePosition; - - /** - * @var int - */ - private int $instanceLength; - - /** - * Constructor of the class - * - * @param int $firstBytePosition - * @param int $lastBytePosition - * @param int $instanceLength - * - * @throws InvalidHeaderException - * If the range isn't valid. - */ - public function __construct(int $firstBytePosition, int $lastBytePosition, int $instanceLength) - { - $this->validateRange($firstBytePosition, $lastBytePosition, $instanceLength); - - $this->firstBytePosition = $firstBytePosition; - $this->lastBytePosition = $lastBytePosition; - $this->instanceLength = $instanceLength; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Content-Range'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return sprintf( - 'bytes %d-%d/%d', - $this->firstBytePosition, - $this->lastBytePosition, - $this->instanceLength - ); - } - - /** - * Validates the given range - * - * @param int $firstBytePosition - * @param int $lastBytePosition - * @param int $instanceLength - * - * @return void - * - * @throws InvalidHeaderException - * If the range isn't valid. - */ - private function validateRange(int $firstBytePosition, int $lastBytePosition, int $instanceLength): void - { - if (! ($firstBytePosition <= $lastBytePosition)) { - throw new InvalidHeaderException( - 'The "first-byte-pos" value of the content range ' . - 'must be less than or equal to the "last-byte-pos" value' - ); - } - - if (! ($lastBytePosition < $instanceLength)) { - throw new InvalidHeaderException( - 'The "last-byte-pos" value of the content range ' . - 'must be less than the "instance-length" value' - ); - } - } -} diff --git a/src/Header/ContentTypeHeader.php b/src/Header/ContentTypeHeader.php deleted file mode 100644 index bd1e88f..0000000 --- a/src/Header/ContentTypeHeader.php +++ /dev/null @@ -1,86 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function explode; -use function sprintf; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.17 - */ -class ContentTypeHeader extends Header -{ - - /** - * @var string - */ - private string $type; - - /** - * @var array - */ - private array $parameters; - - /** - * Constructor of the class - * - * @param string $type - * @param array $parameters - * - * @throws InvalidHeaderException - * - If the type isn't valid; - * - If the parameters aren't valid. - */ - public function __construct(string $type, array $parameters = []) - { - if (strpos($type, '/') === false) { - $type .= '/*'; - } - - $this->validateToken(...explode('/', $type, 2)); - - $parameters = $this->validateParameters($parameters); - - $this->type = $type; - $this->parameters = $parameters; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Content-Type'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - $v = $this->type; - foreach ($this->parameters as $name => $value) { - $v .= sprintf('; %s="%s"', $name, $value); - } - - return $v; - } -} diff --git a/src/Header/CookieHeader.php b/src/Header/CookieHeader.php deleted file mode 100644 index 511255b..0000000 --- a/src/Header/CookieHeader.php +++ /dev/null @@ -1,65 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function http_build_query; - -/** - * Import constants - */ -use const PHP_QUERY_RFC3986; - -/** - * @link https://tools.ietf.org/html/rfc6265.html#section-5.4 - */ -class CookieHeader extends Header -{ - - /** - * @var array - */ - private array $value; - - /** - * Constructor of the class - * - * @param array $value - */ - public function __construct(array $value = []) - { - $this->value = $value; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Cookie'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return http_build_query($this->value, '', '; ', PHP_QUERY_RFC3986); - } -} diff --git a/src/Header/DateHeader.php b/src/Header/DateHeader.php deleted file mode 100644 index cfa308c..0000000 --- a/src/Header/DateHeader.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use DateTimeInterface; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\HeaderUtils; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.18 - * @link https://tools.ietf.org/html/rfc822#section-5 - */ -class DateHeader extends Header -{ - - /** - * @var DateTimeInterface - */ - private DateTimeInterface $timestamp; - - /** - * Constructor of the class - * - * @param DateTimeInterface $timestamp - */ - public function __construct(DateTimeInterface $timestamp) - { - $this->timestamp = $timestamp; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Date'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return HeaderUtils::formatDate($this->timestamp); - } -} diff --git a/src/Header/EtagHeader.php b/src/Header/EtagHeader.php deleted file mode 100644 index f789388..0000000 --- a/src/Header/EtagHeader.php +++ /dev/null @@ -1,66 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function sprintf; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.19 - */ -class EtagHeader extends Header -{ - - /** - * @var string - */ - private string $value; - - /** - * Constructor of the class - * - * @param string $value - * - * @throws InvalidHeaderException - * If the value isn't valid. - */ - public function __construct(string $value) - { - $this->validateQuotedString($value); - - $this->value = $value; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'ETag'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return sprintf('"%s"', $this->value); - } -} diff --git a/src/Header/ExpiresHeader.php b/src/Header/ExpiresHeader.php deleted file mode 100644 index f0a6384..0000000 --- a/src/Header/ExpiresHeader.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use DateTimeInterface; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\HeaderUtils; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.21 - * @link https://tools.ietf.org/html/rfc822#section-5 - */ -class ExpiresHeader extends Header -{ - - /** - * @var DateTimeInterface - */ - private DateTimeInterface $timestamp; - - /** - * Constructor of the class - * - * @param DateTimeInterface $timestamp - */ - public function __construct(DateTimeInterface $timestamp) - { - $this->timestamp = $timestamp; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Expires'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return HeaderUtils::formatDate($this->timestamp); - } -} diff --git a/src/Header/LastModifiedHeader.php b/src/Header/LastModifiedHeader.php deleted file mode 100644 index 3113410..0000000 --- a/src/Header/LastModifiedHeader.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use DateTimeInterface; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\HeaderUtils; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.29 - * @link https://tools.ietf.org/html/rfc822#section-5 - */ -class LastModifiedHeader extends Header -{ - - /** - * @var DateTimeInterface - */ - private DateTimeInterface $timestamp; - - /** - * Constructor of the class - * - * @param DateTimeInterface $timestamp - */ - public function __construct(DateTimeInterface $timestamp) - { - $this->timestamp = $timestamp; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Last-Modified'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return HeaderUtils::formatDate($this->timestamp); - } -} diff --git a/src/Header/LinkHeader.php b/src/Header/LinkHeader.php deleted file mode 100644 index 924696a..0000000 --- a/src/Header/LinkHeader.php +++ /dev/null @@ -1,84 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Psr\Http\Message\UriInterface; -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Exception\InvalidUriException; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\Uri; - -/** - * Import functions - */ -use function sprintf; - -/** - * @link https://tools.ietf.org/html/rfc5988 - */ -class LinkHeader extends Header -{ - - /** - * @var UriInterface - */ - private UriInterface $uri; - - /** - * @var array - */ - private array $parameters; - - /** - * Constructor of the class - * - * @param mixed $uri - * @param array $parameters - * - * @throws InvalidUriException - * If the URI isn't valid. - * - * @throws InvalidHeaderException - * If the parameters aren't valid. - */ - public function __construct($uri, array $parameters = []) - { - $parameters = $this->validateParameters($parameters); - - $this->uri = Uri::create($uri); - $this->parameters = $parameters; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Link'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - $v = sprintf('<%s>', $this->uri->__toString()); - foreach ($this->parameters as $name => $value) { - $v .= sprintf('; %s="%s"', $name, $value); - } - - return $v; - } -} diff --git a/src/Header/LocationHeader.php b/src/Header/LocationHeader.php deleted file mode 100644 index e7a76c3..0000000 --- a/src/Header/LocationHeader.php +++ /dev/null @@ -1,61 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Psr\Http\Message\UriInterface; -use Sunrise\Http\Message\Exception\InvalidUriException; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\Uri; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.30 - */ -class LocationHeader extends Header -{ - - /** - * @var UriInterface - */ - private UriInterface $uri; - - /** - * Constructor of the class - * - * @param mixed $uri - * - * @throws InvalidUriException - * If the URI isn't valid. - */ - public function __construct($uri) - { - $this->uri = Uri::create($uri); - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Location'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return $this->uri->__toString(); - } -} diff --git a/src/Header/RefreshHeader.php b/src/Header/RefreshHeader.php deleted file mode 100644 index 70089df..0000000 --- a/src/Header/RefreshHeader.php +++ /dev/null @@ -1,102 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Psr\Http\Message\UriInterface; -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Exception\InvalidUriException; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\Uri; - -/** - * Import functions - */ -use function sprintf; - -/** - * @link https://en.wikipedia.org/wiki/Meta_refresh - */ -class RefreshHeader extends Header -{ - - /** - * @var int - */ - private int $delay; - - /** - * @var UriInterface - */ - private UriInterface $uri; - - /** - * Constructor of the class - * - * @param int $delay - * @param mixed $uri - * - * @throws InvalidUriException - * If the URI isn't valid. - * - * @throws InvalidHeaderException - * If the delay isn't valid. - */ - public function __construct(int $delay, $uri) - { - $this->validateDelay($delay); - - $uri = Uri::create($uri); - - $this->delay = $delay; - $this->uri = $uri; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Refresh'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return sprintf('%d; url=%s', $this->delay, $this->uri->__toString()); - } - - /** - * Validates the redirection delay - * - * @param int $delay - * - * @return void - * - * @throws InvalidHeaderException - * If the delay isn't valid. - */ - private function validateDelay(int $delay): void - { - if (! ($delay >= 0)) { - throw new InvalidHeaderException(sprintf( - 'The delay "%2$d" for the header "%1$s" is not valid', - $this->getFieldName(), - $delay - )); - } - } -} diff --git a/src/Header/RetryAfterHeader.php b/src/Header/RetryAfterHeader.php deleted file mode 100644 index 88a9f75..0000000 --- a/src/Header/RetryAfterHeader.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use DateTimeInterface; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\HeaderUtils; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.37 - * @link https://tools.ietf.org/html/rfc822#section-5 - */ -class RetryAfterHeader extends Header -{ - - /** - * @var DateTimeInterface - */ - private DateTimeInterface $timestamp; - - /** - * Constructor of the class - * - * @param DateTimeInterface $timestamp - */ - public function __construct(DateTimeInterface $timestamp) - { - $this->timestamp = $timestamp; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Retry-After'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return HeaderUtils::formatDate($this->timestamp); - } -} diff --git a/src/Header/SetCookieHeader.php b/src/Header/SetCookieHeader.php deleted file mode 100644 index 2c454a0..0000000 --- a/src/Header/SetCookieHeader.php +++ /dev/null @@ -1,246 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use DateTimeImmutable; -use DateTimeInterface; -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\HeaderUtils; - -/** - * Import functions - */ -use function max; -use function rawurlencode; -use function sprintf; -use function strpbrk; -use function time; - -/** - * @link https://tools.ietf.org/html/rfc6265#section-4.1 - * @link https://github.com/php/php-src/blob/master/ext/standard/head.c - */ -class SetCookieHeader extends Header -{ - - /** - * Acceptable the SameSite attribute values - * - * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite - */ - public const SAME_SITE_LAX = 'Lax'; - public const SAME_SITE_STRICT = 'Strict'; - public const SAME_SITE_NONE = 'None'; - - /** - * Cookie option keys - */ - public const OPTION_KEY_PATH = 'path'; - public const OPTION_KEY_DOMAIN = 'domain'; - public const OPTION_KEY_SECURE = 'secure'; - public const OPTION_KEY_HTTP_ONLY = 'httpOnly'; - public const OPTION_KEY_SAMESITE = 'sameSite'; - - /** - * Default cookie options - * - * @var array{path?: ?string, domain?: ?string, secure?: ?bool, httpOnly?: ?bool, sameSite?: ?string} - */ - protected static array $defaultOptions = [ - self::OPTION_KEY_PATH => '/', - self::OPTION_KEY_DOMAIN => null, - self::OPTION_KEY_SECURE => null, - self::OPTION_KEY_HTTP_ONLY => true, - self::OPTION_KEY_SAMESITE => self::SAME_SITE_LAX, - ]; - - /** - * The cookie name - * - * @var string - */ - private string $name; - - /** - * The cookie value - * - * @var string - */ - private string $value; - - /** - * The cookie's expiration date - * - * @var DateTimeInterface|null - */ - private ?DateTimeInterface $expires; - - /** - * The cookie options - * - * @var array{path?: ?string, domain?: ?string, secure?: ?bool, httpOnly?: ?bool, sameSite?: ?string} - */ - private array $options; - - /** - * Constructor of the class - * - * @param string $name - * @param string $value - * @param DateTimeInterface|null $expires - * @param array{path?: ?string, domain?: ?string, secure?: ?bool, httpOnly?: ?bool, sameSite?: ?string} $options - * - * @throws InvalidHeaderException - * If one of the arguments isn't valid. - */ - public function __construct(string $name, string $value, ?DateTimeInterface $expires = null, array $options = []) - { - $this->validateCookieName($name); - - if (isset($options[self::OPTION_KEY_PATH])) { - $this->validateCookieOption( - self::OPTION_KEY_PATH, - $options[self::OPTION_KEY_PATH] - ); - } - - if (isset($options[self::OPTION_KEY_DOMAIN])) { - $this->validateCookieOption( - self::OPTION_KEY_DOMAIN, - $options[self::OPTION_KEY_DOMAIN] - ); - } - - if (isset($options[self::OPTION_KEY_SAMESITE])) { - $this->validateCookieOption( - self::OPTION_KEY_SAMESITE, - $options[self::OPTION_KEY_SAMESITE] - ); - } - - if ($value === '') { - $value = 'deleted'; - $expires = new DateTimeImmutable('1 year ago'); - } - - $options += static::$defaultOptions; - - $this->name = $name; - $this->value = $value; - $this->expires = $expires; - $this->options = $options; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Set-Cookie'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - $name = rawurlencode($this->name); - $value = rawurlencode($this->value); - $result = sprintf('%s=%s', $name, $value); - - if (isset($this->expires)) { - $result .= '; Expires=' . HeaderUtils::formatDate($this->expires); - $result .= '; Max-Age=' . max($this->expires->getTimestamp() - time(), 0); - } - - if (isset($this->options[self::OPTION_KEY_PATH])) { - $result .= '; Path=' . $this->options[self::OPTION_KEY_PATH]; - } - - if (isset($this->options[self::OPTION_KEY_DOMAIN])) { - $result .= '; Domain=' . $this->options[self::OPTION_KEY_DOMAIN]; - } - - if (isset($this->options[self::OPTION_KEY_SECURE]) && $this->options[self::OPTION_KEY_SECURE]) { - $result .= '; Secure'; - } - - if (isset($this->options[self::OPTION_KEY_HTTP_ONLY]) && $this->options[self::OPTION_KEY_HTTP_ONLY]) { - $result .= '; HttpOnly'; - } - - if (isset($this->options[self::OPTION_KEY_SAMESITE])) { - $result .= '; SameSite=' . $this->options[self::OPTION_KEY_SAMESITE]; - } - - return $result; - } - - /** - * Validates the given cookie name - * - * @param string $name - * - * @return void - * - * @throws InvalidHeaderException - * If the cookie name isn't valid. - */ - private function validateCookieName(string $name): void - { - if ('' === $name) { - throw new InvalidHeaderException('Cookie name cannot be empty'); - } - - // https://github.com/php/php-src/blob/02a5335b710aa36cd0c3108bfb9c6f7a57d40000/ext/standard/head.c#L93 - if (strpbrk($name, "=,; \t\r\n\013\014") !== false) { - throw new InvalidHeaderException(sprintf( - 'The cookie name "%s" contains prohibited characters', - $name - )); - } - } - - /** - * Validates the given cookie option - * - * @param string $key - * @param mixed $value - * - * @return void - * - * @throws InvalidHeaderException - * If the cookie option isn't valid. - */ - private function validateCookieOption(string $key, $value): void - { - if (!is_string($value)) { - throw new InvalidHeaderException(sprintf( - 'The cookie option "%s" must be a string', - $key - )); - } - - // https://github.com/php/php-src/blob/02a5335b710aa36cd0c3108bfb9c6f7a57d40000/ext/standard/head.c#L103 - // https://github.com/php/php-src/blob/02a5335b710aa36cd0c3108bfb9c6f7a57d40000/ext/standard/head.c#L108 - if (strpbrk($value, ",; \t\r\n\013\014") !== false) { - throw new InvalidHeaderException(sprintf( - 'The cookie option "%s" contains prohibited characters', - $key - )); - } - } -} diff --git a/src/Header/SunsetHeader.php b/src/Header/SunsetHeader.php deleted file mode 100644 index b1b379d..0000000 --- a/src/Header/SunsetHeader.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use DateTimeInterface; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\HeaderUtils; - -/** - * @link https://tools.ietf.org/id/draft-wilde-sunset-header-03.html - * @link https://github.com/sunrise-php/http-header-kit/issues/1#issuecomment-457043527 - */ -class SunsetHeader extends Header -{ - - /** - * @var DateTimeInterface - */ - private DateTimeInterface $timestamp; - - /** - * Constructor of the class - * - * @param DateTimeInterface $timestamp - */ - public function __construct(DateTimeInterface $timestamp) - { - $this->timestamp = $timestamp; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Sunset'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return HeaderUtils::formatDate($this->timestamp); - } -} diff --git a/src/Header/TrailerHeader.php b/src/Header/TrailerHeader.php deleted file mode 100644 index 779a59b..0000000 --- a/src/Header/TrailerHeader.php +++ /dev/null @@ -1,61 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.40 - */ -class TrailerHeader extends Header -{ - - /** - * @var string - */ - private string $value; - - /** - * Constructor of the class - * - * @param string $value - * - * @throws InvalidHeaderException - * If the value isn't valid. - */ - public function __construct(string $value) - { - $this->validateToken($value); - - $this->value = $value; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Trailer'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return $this->value; - } -} diff --git a/src/Header/TransferEncodingHeader.php b/src/Header/TransferEncodingHeader.php deleted file mode 100644 index 6800084..0000000 --- a/src/Header/TransferEncodingHeader.php +++ /dev/null @@ -1,79 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Dictionary\Encoding; -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.41 - */ -class TransferEncodingHeader extends Header -{ - - /** - * HTTP Transfer Encodings - * - * @link https://www.iana.org/assignments/http-parameters/http-parameters.xhtml#transfer-coding - */ - public const HTTP_TRANSFER_ENCODING_CHUNKED = 'chunked'; - public const HTTP_TRANSFER_ENCODING_COMPRESS = 'compress'; - public const HTTP_TRANSFER_ENCODING_DEFLATE = 'deflate'; - public const HTTP_TRANSFER_ENCODING_GZIP = 'gzip'; - - /** - * @var list - */ - private array $directives = []; - - /** - * Constructor of the class - * - * @param string ...$directives - * - * @throws InvalidHeaderException - * If one of the directives isn't valid. - */ - public function __construct(string ...$directives) - { - $this->validateToken(...$directives); - - foreach ($directives as $directive) { - $this->directives[] = $directive; - } - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Transfer-Encoding'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return implode(', ', $this->directives); - } -} diff --git a/src/Header/VaryHeader.php b/src/Header/VaryHeader.php deleted file mode 100644 index 1d56444..0000000 --- a/src/Header/VaryHeader.php +++ /dev/null @@ -1,68 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.44 - */ -class VaryHeader extends Header -{ - - /** - * @var list - */ - private array $value = []; - - /** - * Constructor of the class - * - * @param string ...$value - * - * @throws InvalidHeaderException - * If one of the values isn't valid. - */ - public function __construct(string ...$value) - { - $this->validateToken(...$value); - - foreach ($value as $item) { - $this->value[] = $item; - } - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Vary'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - return implode(', ', $this->value); - } -} diff --git a/src/Header/WWWAuthenticateHeader.php b/src/Header/WWWAuthenticateHeader.php deleted file mode 100644 index ed9f67a..0000000 --- a/src/Header/WWWAuthenticateHeader.php +++ /dev/null @@ -1,104 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; - -/** - * Import functions - */ -use function implode; -use function sprintf; - -/** - * @link https://tools.ietf.org/html/rfc7235#section-4.1 - */ -class WWWAuthenticateHeader extends Header -{ - - /** - * HTTP Authentication Schemes - * - * @link https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml - */ - public const HTTP_AUTHENTICATE_SCHEME_BASIC = 'Basic'; - public const HTTP_AUTHENTICATE_SCHEME_BEARER = 'Bearer'; - public const HTTP_AUTHENTICATE_SCHEME_DIGEST = 'Digest'; - public const HTTP_AUTHENTICATE_SCHEME_HOBA = 'HOBA'; - public const HTTP_AUTHENTICATE_SCHEME_MUTUAL = 'Mutual'; - public const HTTP_AUTHENTICATE_SCHEME_NEGOTIATE = 'Negotiate'; - public const HTTP_AUTHENTICATE_SCHEME_OAUTH = 'OAuth'; - public const HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_1 = 'SCRAM-SHA-1'; - public const HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_256 = 'SCRAM-SHA-256'; - public const HTTP_AUTHENTICATE_SCHEME_VAPID = 'vapid'; - - /** - * @var string - */ - private string $scheme; - - /** - * @var array - */ - private array $parameters; - - /** - * Constructor of the class - * - * @param string $scheme - * @param array $parameters - * - * @throws InvalidHeaderException - * - If the scheme isn't valid; - * - If the parameters aren't valid. - */ - public function __construct(string $scheme, array $parameters = []) - { - $this->validateToken($scheme); - - $parameters = $this->validateParameters($parameters); - - $this->scheme = $scheme; - $this->parameters = $parameters; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'WWW-Authenticate'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - $result = $this->scheme; - - $challenge = []; - foreach ($this->parameters as $name => $value) { - $challenge[] = sprintf(' %s="%s"', $name, $value); - } - - if (!empty($challenge)) { - $result .= implode(',', $challenge); - } - - return $result; - } -} diff --git a/src/Header/WarningHeader.php b/src/Header/WarningHeader.php deleted file mode 100644 index d23c59b..0000000 --- a/src/Header/WarningHeader.php +++ /dev/null @@ -1,131 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Header; - -/** - * Import classes - */ -use DateTimeInterface; -use Sunrise\Http\Message\Exception\InvalidHeaderException; -use Sunrise\Http\Message\Header; -use Sunrise\Http\Message\HeaderUtils; - -/** - * Import functions - */ -use function sprintf; - -/** - * @link https://tools.ietf.org/html/rfc2616#section-14.46 - */ -class WarningHeader extends Header -{ - - /** - * HTTP Warning Codes - * - * @link https://www.iana.org/assignments/http-warn-codes/http-warn-codes.xhtml - */ - public const HTTP_WARNING_CODE_RESPONSE_IS_STALE = 110; - public const HTTP_WARNING_CODE_REVALIDATION_FAILED = 111; - public const HTTP_WARNING_CODE_DISCONNECTED_OPERATION = 112; - public const HTTP_WARNING_CODE_HEURISTIC_EXPIRATION = 113; - public const HTTP_WARNING_CODE_MISCELLANEOUS_WARNING = 199; - public const HTTP_WARNING_CODE_TRANSFORMATION_APPLIED = 214; - public const HTTP_WARNING_CODE_MISCELLANEOUS_PERSISTENT_WARNING = 299; - - /** - * @var int - */ - private int $code; - - /** - * @var string - */ - private string $agent; - - /** - * @var string - */ - private string $text; - - /** - * @var DateTimeInterface|null - */ - private ?DateTimeInterface $date; - - /** - * Constructor of the class - * - * @param int $code - * @param string $agent - * @param string $text - * @param DateTimeInterface|null $date - * - * @throws InvalidHeaderException - * If one of arguments isn't valid. - */ - public function __construct(int $code, string $agent, string $text, ?DateTimeInterface $date = null) - { - $this->validateCode($code); - $this->validateToken($agent); - $this->validateQuotedString($text); - - $this->code = $code; - $this->agent = $agent; - $this->text = $text; - $this->date = $date; - } - - /** - * {@inheritdoc} - */ - public function getFieldName(): string - { - return 'Warning'; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(): string - { - $value = sprintf('%s %s "%s"', $this->code, $this->agent, $this->text); - - if (isset($this->date)) { - $value .= sprintf(' "%s"', HeaderUtils::formatDate($this->date)); - } - - return $value; - } - - /** - * Validates the given code - * - * @param int $code - * - * @return void - * - * @throws InvalidHeaderException - * If the code isn't valid. - */ - private function validateCode(int $code): void - { - if (! ($code >= 100 && $code <= 999)) { - throw new InvalidHeaderException(sprintf( - 'The code "%2$d" for the header "%1$s" is not valid', - $this->getFieldName(), - $code - )); - } - } -} diff --git a/src/HeaderUtils.php b/src/HeaderUtils.php deleted file mode 100644 index 3e1f9fd..0000000 --- a/src/HeaderUtils.php +++ /dev/null @@ -1,43 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message; - -/** - * Import classes - */ -use DateTime; -use DateTimeInterface; -use DateTimeZone; - -/** - * HeaderUtils - */ -final class HeaderUtils -{ - - /** - * Formats the given date according to RFC-822 - * - * @param DateTimeInterface $date - * - * @return string - */ - public static function formatDate(DateTimeInterface $date): string - { - if ($date instanceof DateTime) { - $data = clone $date; - } - - return $date->setTimezone(new DateTimeZone('GMT')) - ->format(HeaderInterface::RFC822_DATE_FORMAT); - } -} diff --git a/tests/Header/AccessControlAllowCredentialsHeaderTest.php b/tests/Header/AccessControlAllowCredentialsHeaderTest.php deleted file mode 100644 index b195b5b..0000000 --- a/tests/Header/AccessControlAllowCredentialsHeaderTest.php +++ /dev/null @@ -1,55 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new AccessControlAllowCredentialsHeader(); - - $this->assertSame('Access-Control-Allow-Credentials', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new AccessControlAllowCredentialsHeader(); - - $this->assertSame('true', $header->getFieldValue()); - } - - public function testBuild() - { - $header = new AccessControlAllowCredentialsHeader(); - - $expected = 'Access-Control-Allow-Credentials: true'; - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new AccessControlAllowCredentialsHeader(); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/AccessControlAllowHeadersHeaderTest.php b/tests/Header/AccessControlAllowHeadersHeaderTest.php deleted file mode 100644 index 3881b77..0000000 --- a/tests/Header/AccessControlAllowHeadersHeaderTest.php +++ /dev/null @@ -1,96 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new AccessControlAllowHeadersHeader('x-foo'); - - $this->assertSame('Access-Control-Allow-Headers', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new AccessControlAllowHeadersHeader('x-foo'); - - $this->assertSame('x-foo', $header->getFieldValue()); - } - - public function testSeveralValues() - { - $header = new AccessControlAllowHeadersHeader('x-foo', 'x-bar', 'x-baz'); - - $this->assertSame('x-foo, x-bar, x-baz', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Access-Control-Allow-Headers" is not valid'); - - new AccessControlAllowHeadersHeader(''); - } - - public function testEmptyValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Access-Control-Allow-Headers" is not valid'); - - new AccessControlAllowHeadersHeader('x-foo', '', 'x-bar'); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "x-foo=" for the header "Access-Control-Allow-Headers" is not valid'); - - // a token cannot contain the "=" character... - new AccessControlAllowHeadersHeader('x-foo='); - } - - public function testInvalidValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "x-bar=" for the header "Access-Control-Allow-Headers" is not valid'); - - // a token cannot contain the "=" character... - new AccessControlAllowHeadersHeader('x-foo', 'x-bar=', 'x-bar'); - } - - public function testBuild() - { - $header = new AccessControlAllowHeadersHeader('x-foo'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new AccessControlAllowHeadersHeader('x-foo'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/AccessControlAllowMethodsHeaderTest.php b/tests/Header/AccessControlAllowMethodsHeaderTest.php deleted file mode 100644 index fdb63e4..0000000 --- a/tests/Header/AccessControlAllowMethodsHeaderTest.php +++ /dev/null @@ -1,103 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new AccessControlAllowMethodsHeader('GET'); - - $this->assertSame('Access-Control-Allow-Methods', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new AccessControlAllowMethodsHeader('GET'); - - $this->assertSame('GET', $header->getFieldValue()); - } - - public function testSeveralValues() - { - $header = new AccessControlAllowMethodsHeader('HEAD', 'GET', 'POST'); - - $this->assertSame('HEAD, GET, POST', $header->getFieldValue()); - } - - public function testValueCapitalizing() - { - $header = new AccessControlAllowMethodsHeader('head', 'get', 'post'); - - $this->assertSame('HEAD, GET, POST', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Access-Control-Allow-Methods" is not valid'); - - new AccessControlAllowMethodsHeader(''); - } - - public function testEmptyValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Access-Control-Allow-Methods" is not valid'); - - new AccessControlAllowMethodsHeader('head', '', 'post'); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Access-Control-Allow-Methods" is not valid'); - - // isn't a token... - new AccessControlAllowMethodsHeader('@'); - } - - public function testInvalidValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Access-Control-Allow-Methods" is not valid'); - - // isn't a token... - new AccessControlAllowMethodsHeader('head', '@', 'post'); - } - - public function testBuild() - { - $header = new AccessControlAllowMethodsHeader('GET'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new AccessControlAllowMethodsHeader('GET'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/AccessControlAllowOriginHeaderTest.php b/tests/Header/AccessControlAllowOriginHeaderTest.php deleted file mode 100644 index c75c181..0000000 --- a/tests/Header/AccessControlAllowOriginHeaderTest.php +++ /dev/null @@ -1,106 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $uri = new Uri('http://localhost'); - $header = new AccessControlAllowOriginHeader($uri); - - $this->assertSame('Access-Control-Allow-Origin', $header->getFieldName()); - } - - public function testFieldValue() - { - $uri = new Uri('http://localhost'); - $header = new AccessControlAllowOriginHeader($uri); - - $this->assertSame('http://localhost', $header->getFieldValue()); - } - - public function testFieldValueWithoutUri() - { - $header = new AccessControlAllowOriginHeader(null); - - $this->assertSame('*', $header->getFieldValue()); - } - - public function testIgnoringUnnecessaryUriComponents() - { - $uri = new Uri('http://user:pass@localhost:3000/index.php?q#h'); - $header = new AccessControlAllowOriginHeader($uri); - - $this->assertSame('http://localhost:3000', $header->getFieldValue()); - } - - public function testUriWithPort() - { - $uri = new Uri('http://localhost:3000'); - $header = new AccessControlAllowOriginHeader($uri); - - $this->assertSame('http://localhost:3000', $header->getFieldValue()); - } - - public function testUriWithoutScheme() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The URI "//localhost" for the header "Access-Control-Allow-Origin" is not valid' - ); - - new AccessControlAllowOriginHeader(new Uri('//localhost')); - } - - public function testUriWithoutHost() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The URI "http:" for the header "Access-Control-Allow-Origin" is not valid' - ); - - new AccessControlAllowOriginHeader(new Uri('http:')); - } - - public function testBuild() - { - $uri = new Uri('http://localhost'); - $header = new AccessControlAllowOriginHeader($uri); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $uri = new Uri('http://localhost'); - $header = new AccessControlAllowOriginHeader($uri); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/AccessControlExposeHeadersHeaderTest.php b/tests/Header/AccessControlExposeHeadersHeaderTest.php deleted file mode 100644 index 271a1db..0000000 --- a/tests/Header/AccessControlExposeHeadersHeaderTest.php +++ /dev/null @@ -1,96 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new AccessControlExposeHeadersHeader('x-foo'); - - $this->assertSame('Access-Control-Expose-Headers', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new AccessControlExposeHeadersHeader('x-foo'); - - $this->assertSame('x-foo', $header->getFieldValue()); - } - - public function testSeveralValues() - { - $header = new AccessControlExposeHeadersHeader('x-foo', 'x-bar', 'x-baz'); - - $this->assertSame('x-foo, x-bar, x-baz', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Access-Control-Expose-Headers" is not valid'); - - new AccessControlExposeHeadersHeader(''); - } - - public function testEmptyValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Access-Control-Expose-Headers" is not valid'); - - new AccessControlExposeHeadersHeader('x-foo', '', 'x-baz'); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Access-Control-Expose-Headers" is not valid'); - - // isn't a token... - new AccessControlExposeHeadersHeader('@'); - } - - public function testInvalidValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Access-Control-Expose-Headers" is not valid'); - - // isn't a token... - new AccessControlExposeHeadersHeader('x-foo', '@', 'x-baz'); - } - - public function testBuild() - { - $header = new AccessControlExposeHeadersHeader('x-foo'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new AccessControlExposeHeadersHeader('x-foo'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/AccessControlMaxAgeHeaderTest.php b/tests/Header/AccessControlMaxAgeHeaderTest.php deleted file mode 100644 index 7520624..0000000 --- a/tests/Header/AccessControlMaxAgeHeaderTest.php +++ /dev/null @@ -1,90 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new AccessControlMaxAgeHeader(-1); - - $this->assertSame('Access-Control-Max-Age', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new AccessControlMaxAgeHeader(-1); - - $this->assertSame('-1', $header->getFieldValue()); - } - - /** - * @dataProvider validValueDataProvider - */ - public function testValidValue(int $validValue) - { - $header = new AccessControlMaxAgeHeader($validValue); - - $this->assertEquals($validValue, $header->getFieldValue()); - } - - public function validValueDataProvider(): array - { - return [[-1], [1], [2]]; - } - - /** - * @dataProvider invalidValueDataProvider - */ - public function testInvalidValue(int $invalidValue) - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage(sprintf( - 'The value "%d" for the header "Access-Control-Max-Age" is not valid', - $invalidValue - )); - - new AccessControlMaxAgeHeader($invalidValue); - } - - public function invalidValueDataProvider(): array - { - return [[-3], [-2], [0]]; - } - - public function testBuild() - { - $header = new AccessControlMaxAgeHeader(-1); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new AccessControlMaxAgeHeader(-1); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/AgeHeaderTest.php b/tests/Header/AgeHeaderTest.php deleted file mode 100644 index f3289fb..0000000 --- a/tests/Header/AgeHeaderTest.php +++ /dev/null @@ -1,90 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new AgeHeader(0); - - $this->assertSame('Age', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new AgeHeader(0); - - $this->assertSame('0', $header->getFieldValue()); - } - - /** - * @dataProvider validValueDataProvider - */ - public function testValidValue(int $validValue) - { - $header = new AgeHeader($validValue); - - $this->assertEquals($validValue, $header->getFieldValue()); - } - - public function validValueDataProvider(): array - { - return [[0], [1], [2]]; - } - - /** - * @dataProvider invalidValueDataProvider - */ - public function testInvalidValue(int $invalidValue) - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage(sprintf( - 'The value "%d" for the header "Age" is not valid', - $invalidValue - )); - - new AgeHeader($invalidValue); - } - - public function invalidValueDataProvider(): array - { - return [[-3], [-2], [-1]]; - } - - public function testBuild() - { - $header = new AgeHeader(0); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new AgeHeader(0); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/AllowHeaderTest.php b/tests/Header/AllowHeaderTest.php deleted file mode 100644 index 9c325be..0000000 --- a/tests/Header/AllowHeaderTest.php +++ /dev/null @@ -1,103 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new AllowHeader('GET'); - - $this->assertSame('Allow', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new AllowHeader('GET'); - - $this->assertSame('GET', $header->getFieldValue()); - } - - public function testSeveralValues() - { - $header = new AllowHeader('HEAD', 'GET', 'POST'); - - $this->assertSame('HEAD, GET, POST', $header->getFieldValue()); - } - - public function testValueCapitalizing() - { - $header = new AllowHeader('head', 'get', 'post'); - - $this->assertSame('HEAD, GET, POST', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Allow" is not valid'); - - new AllowHeader(''); - } - - public function testEmptyValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Allow" is not valid'); - - new AllowHeader('head', '', 'post'); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Allow" is not valid'); - - // isn't a token... - new AllowHeader('@'); - } - - public function testInvalidValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Allow" is not valid'); - - // isn't a token... - new AllowHeader('head', '@', 'post'); - } - - public function testBuild() - { - $header = new AllowHeader('GET'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new AllowHeader('GET'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/CacheControlHeaderTest.php b/tests/Header/CacheControlHeaderTest.php deleted file mode 100644 index 7d83961..0000000 --- a/tests/Header/CacheControlHeaderTest.php +++ /dev/null @@ -1,147 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new CacheControlHeader([]); - - $this->assertSame('Cache-Control', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new CacheControlHeader([]); - - $this->assertSame('', $header->getFieldValue()); - } - - public function testParameterWithEmptyValue() - { - $header = new CacheControlHeader([ - 'foo' => '', - ]); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testParameterWithToken() - { - $header = new CacheControlHeader([ - 'foo' => 'token', - ]); - - $this->assertSame('foo=token', $header->getFieldValue()); - } - - public function testParameterWithQuotedString() - { - $header = new CacheControlHeader([ - 'foo' => 'quoted string', - ]); - - $this->assertSame('foo="quoted string"', $header->getFieldValue()); - } - - public function testParameterWithInteger() - { - $header = new CacheControlHeader([ - 'foo' => 1, - ]); - - $this->assertSame('foo=1', $header->getFieldValue()); - } - - public function testSeveralParameters() - { - $header = new CacheControlHeader([ - 'foo' => '', - 'bar' => 'token', - 'baz' => 'quoted string', - 'qux' => 1, - ]); - - $this->assertSame('foo, bar=token, baz="quoted string", qux=1', $header->getFieldValue()); - } - - public function testInvalidParameterName() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "invalid name" for the header "Cache-Control" is not valid' - ); - - new CacheControlHeader(['invalid name' => 'value']); - } - - public function testInvalidParameterValue() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value ""invalid value"" for the header "Cache-Control" is not valid' - ); - - new CacheControlHeader(['name' => '"invalid value"']); - } - - public function testInvalidParameterNameType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "" for the header "Cache-Control" is not valid' - ); - - new CacheControlHeader([0 => 'value']); - } - - public function testInvalidParameterValueType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value "" for the header "Cache-Control" is not valid' - ); - - new CacheControlHeader(['name' => []]); - } - - public function testBuild() - { - $header = new CacheControlHeader(['foo' => 'bar']); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new CacheControlHeader(['foo' => 'bar']); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/ClearSiteDataHeaderTest.php b/tests/Header/ClearSiteDataHeaderTest.php deleted file mode 100644 index 5e7d508..0000000 --- a/tests/Header/ClearSiteDataHeaderTest.php +++ /dev/null @@ -1,94 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new ClearSiteDataHeader('foo'); - - $this->assertSame('Clear-Site-Data', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new ClearSiteDataHeader('foo'); - - $this->assertSame('"foo"', $header->getFieldValue()); - } - - public function testSeveralValues() - { - $header = new ClearSiteDataHeader('foo', 'bar', 'baz'); - - $this->assertSame('"foo", "bar", "baz"', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $header = new ClearSiteDataHeader(''); - - $this->assertSame('""', $header->getFieldValue()); - } - - public function testEmptyValueAmongOthers() - { - $header = new ClearSiteDataHeader('foo', '', 'baz'); - - $this->assertSame('"foo", "", "baz"', $header->getFieldValue()); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value ""invalid value"" for the header "Clear-Site-Data" is not valid'); - - // cannot contain quotes... - new ClearSiteDataHeader('"invalid value"'); - } - - public function testInvalidValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value ""bar"" for the header "Clear-Site-Data" is not valid'); - - // cannot contain quotes... - new ClearSiteDataHeader('foo', '"bar"', 'baz'); - } - - public function testBuild() - { - $header = new ClearSiteDataHeader('foo'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new ClearSiteDataHeader('foo'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/ContentDispositionHeaderTest.php b/tests/Header/ContentDispositionHeaderTest.php deleted file mode 100644 index 17ff90f..0000000 --- a/tests/Header/ContentDispositionHeaderTest.php +++ /dev/null @@ -1,166 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new ContentDispositionHeader('foo'); - - $this->assertSame('Content-Disposition', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new ContentDispositionHeader('foo'); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testParameterWithEmptyValue() - { - $header = new ContentDispositionHeader('foo', [ - 'bar' => '', - ]); - - $this->assertSame('foo; bar=""', $header->getFieldValue()); - } - - public function testParameterWithToken() - { - $header = new ContentDispositionHeader('foo', [ - 'bar' => 'token', - ]); - - $this->assertSame('foo; bar="token"', $header->getFieldValue()); - } - - public function testParameterWithQuotedString() - { - $header = new ContentDispositionHeader('foo', [ - 'bar' => 'quoted string', - ]); - - $this->assertSame('foo; bar="quoted string"', $header->getFieldValue()); - } - - public function testParameterWithInteger() - { - $header = new ContentDispositionHeader('foo', [ - 'bar' => 1, - ]); - - $this->assertSame('foo; bar="1"', $header->getFieldValue()); - } - - public function testSeveralParameters() - { - $header = new ContentDispositionHeader('foo', [ - 'bar' => '', - 'baz' => 'token', - 'bat' => 'quoted string', - 'qux' => 1, - ]); - - $this->assertSame('foo; bar=""; baz="token"; bat="quoted string"; qux="1"', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Content-Disposition" is not valid'); - - new ContentDispositionHeader(''); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Content-Disposition" is not valid'); - - // isn't a token... - new ContentDispositionHeader('@'); - } - - public function testInvalidParameterName() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "invalid name" for the header "Content-Disposition" is not valid' - ); - - // cannot contain spaces... - new ContentDispositionHeader('foo', ['invalid name' => 'value']); - } - - public function testInvalidParameterNameType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "" for the header "Content-Disposition" is not valid' - ); - - new ContentDispositionHeader('foo', [0 => 'value']); - } - - public function testInvalidParameterValue() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value ""invalid value"" for the header "Content-Disposition" is not valid' - ); - - // cannot contain quotes... - new ContentDispositionHeader('foo', ['name' => '"invalid value"']); - } - - public function testInvalidParameterValueType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value "" for the header "Content-Disposition" is not valid' - ); - - new ContentDispositionHeader('foo', ['name' => []]); - } - - public function testBuild() - { - $header = new ContentDispositionHeader('foo', ['bar' => 'baz']); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new ContentDispositionHeader('foo', ['bar' => 'baz']); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/ContentEncodingHeaderTest.php b/tests/Header/ContentEncodingHeaderTest.php deleted file mode 100644 index ff3305a..0000000 --- a/tests/Header/ContentEncodingHeaderTest.php +++ /dev/null @@ -1,105 +0,0 @@ -assertSame('aes128gcm', ContentEncodingHeader::HTTP_CONTENT_ENCODING_AES128GCM); - $this->assertSame('br', ContentEncodingHeader::HTTP_CONTENT_ENCODING_BR); - $this->assertSame('compress', ContentEncodingHeader::HTTP_CONTENT_ENCODING_COMPRESS); - $this->assertSame('deflate', ContentEncodingHeader::HTTP_CONTENT_ENCODING_DEFLATE); - $this->assertSame('gzip', ContentEncodingHeader::HTTP_CONTENT_ENCODING_GZIP); - } - - public function testContracts() - { - $header = new ContentEncodingHeader('foo'); - - $this->assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new ContentEncodingHeader('foo'); - - $this->assertSame('Content-Encoding', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new ContentEncodingHeader('foo'); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testSeveralValues() - { - $header = new ContentEncodingHeader('foo', 'bar', 'baz'); - - $this->assertSame('foo, bar, baz', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Content-Encoding" is not valid'); - - new ContentEncodingHeader(''); - } - - public function testEmptyValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Content-Encoding" is not valid'); - - new ContentEncodingHeader('foo', '', 'bar'); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "foo=" for the header "Content-Encoding" is not valid'); - - // a token cannot contain the "=" character... - new ContentEncodingHeader('foo='); - } - - public function testInvalidValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "bar=" for the header "Content-Encoding" is not valid'); - - // a token cannot contain the "=" character... - new ContentEncodingHeader('foo', 'bar=', 'bar'); - } - - public function testBuild() - { - $header = new ContentEncodingHeader('foo'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new ContentEncodingHeader('foo'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/ContentLanguageHeaderTest.php b/tests/Header/ContentLanguageHeaderTest.php deleted file mode 100644 index e789827..0000000 --- a/tests/Header/ContentLanguageHeaderTest.php +++ /dev/null @@ -1,114 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new ContentLanguageHeader('foo'); - - $this->assertSame('Content-Language', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new ContentLanguageHeader('foo'); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testSeveralValues() - { - $header = new ContentLanguageHeader('foo', 'bar', 'baz'); - - $this->assertSame('foo, bar, baz', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Content-Language" is not valid'); - - new ContentLanguageHeader(''); - } - - public function testEmptyValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Content-Language" is not valid'); - - new ContentLanguageHeader('foo', '', 'baz'); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Content-Language" is not valid'); - - // isn't a token... - new ContentLanguageHeader('@'); - } - - public function testInvalidValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Content-Language" is not valid'); - - // isn't a token... - new ContentLanguageHeader('foo', '@', 'baz'); - } - - public function testInvalidValueLength() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "VERYLONGWORD" for the header "Content-Language" is not valid'); - - // isn't a token... - new ContentLanguageHeader('VERYLONGWORD'); - } - - public function testInvalidValueLengthAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "VERYLONGWORD" for the header "Content-Language" is not valid'); - - // isn't a token... - new ContentLanguageHeader('foo', 'VERYLONGWORD', 'baz'); - } - - public function testBuild() - { - $header = new ContentLanguageHeader('foo'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new ContentLanguageHeader('foo'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/ContentLengthHeaderTest.php b/tests/Header/ContentLengthHeaderTest.php deleted file mode 100644 index 7eb9b1f..0000000 --- a/tests/Header/ContentLengthHeaderTest.php +++ /dev/null @@ -1,63 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new ContentLengthHeader(0); - - $this->assertSame('Content-Length', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new ContentLengthHeader(0); - - $this->assertSame('0', $header->getFieldValue()); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "-1" for the header "Content-Length" is not valid'); - - new ContentLengthHeader(-1); - } - - public function testBuild() - { - $header = new ContentLengthHeader(0); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new ContentLengthHeader(0); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/ContentLocationHeaderTest.php b/tests/Header/ContentLocationHeaderTest.php deleted file mode 100644 index 0234399..0000000 --- a/tests/Header/ContentLocationHeaderTest.php +++ /dev/null @@ -1,61 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $uri = new Uri('/'); - $header = new ContentLocationHeader($uri); - - $this->assertSame('Content-Location', $header->getFieldName()); - } - - public function testFieldValue() - { - $uri = new Uri('/'); - $header = new ContentLocationHeader($uri); - - $this->assertSame('/', $header->getFieldValue()); - } - - public function testBuild() - { - $uri = new Uri('/'); - $header = new ContentLocationHeader($uri); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $uri = new Uri('/'); - $header = new ContentLocationHeader($uri); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/ContentRangeHeaderTest.php b/tests/Header/ContentRangeHeaderTest.php deleted file mode 100644 index cb04bd5..0000000 --- a/tests/Header/ContentRangeHeaderTest.php +++ /dev/null @@ -1,91 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new ContentRangeHeader(0, 1, 2); - - $this->assertSame('Content-Range', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new ContentRangeHeader(0, 1, 2); - - $this->assertSame('bytes 0-1/2', $header->getFieldValue()); - } - - public function testInvalidFirstBytePosition() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The "first-byte-pos" value of the content range ' . - 'must be less than or equal to the "last-byte-pos" value' - ); - - new ContentRangeHeader(2, 1, 2); - } - - public function testInvalidLastBytePosition() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The "last-byte-pos" value of the content range ' . - 'must be less than the "instance-length" value' - ); - - new ContentRangeHeader(0, 2, 2); - } - - public function testInvalidInstanceLength() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The "last-byte-pos" value of the content range ' . - 'must be less than the "instance-length" value' - ); - - new ContentRangeHeader(0, 1, 1); - } - - public function testBuild() - { - $header = new ContentRangeHeader(0, 1, 2); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new ContentRangeHeader(0, 1, 2); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/ContentTypeHeaderTest.php b/tests/Header/ContentTypeHeaderTest.php deleted file mode 100644 index 3a617e7..0000000 --- a/tests/Header/ContentTypeHeaderTest.php +++ /dev/null @@ -1,168 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new ContentTypeHeader('foo'); - - $this->assertSame('Content-Type', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new ContentTypeHeader('foo'); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testParameterWithEmptyValue() - { - $header = new ContentTypeHeader('foo', [ - 'bar' => '', - ]); - - $this->assertSame('foo; bar=""', $header->getFieldValue()); - } - - public function testParameterWithToken() - { - $header = new ContentTypeHeader('foo', [ - 'bar' => 'token', - ]); - - $this->assertSame('foo; bar="token"', $header->getFieldValue()); - } - - public function testParameterWithQuotedString() - { - $header = new ContentTypeHeader('foo', [ - 'bar' => 'quoted string', - ]); - - $this->assertSame('foo; bar="quoted string"', $header->getFieldValue()); - } - - public function testParameterWithInteger() - { - $header = new ContentTypeHeader('foo', [ - 'bar' => 1, - ]); - - $this->assertSame('foo; bar="1"', $header->getFieldValue()); - } - - public function testSeveralParameters() - { - $header = new ContentTypeHeader('foo', [ - 'bar' => '', - 'baz' => 'token', - 'bat' => 'quoted string', - 'qux' => 1, - ]); - - $this->assertSame('foo; bar=""; baz="token"; bat="quoted string"; qux="1"', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Content-Type" is not valid'); - - new ContentTypeHeader(''); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Content-Type" is not valid'); - - // isn't a token... - new ContentTypeHeader('@'); - } - - public function testInvalidParameterName() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "invalid name" for the header "Content-Type" is not valid' - ); - - // cannot contain spaces... - new ContentTypeHeader('foo', ['invalid name' => 'value']); - } - - public function testInvalidParameterNameType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "" for the header "Content-Type" is not valid' - ); - - // cannot contain spaces... - new ContentTypeHeader('foo', [0 => 'value']); - } - - public function testInvalidParameterValue() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value ""invalid value"" for the header "Content-Type" is not valid' - ); - - // cannot contain quotes... - new ContentTypeHeader('foo', ['name' => '"invalid value"']); - } - - public function testInvalidParameterValueType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value "" for the header "Content-Type" is not valid' - ); - - // cannot contain quotes... - new ContentTypeHeader('foo', ['name' => []]); - } - - public function testBuild() - { - $header = new ContentTypeHeader('foo', ['bar' => 'baz']); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new ContentTypeHeader('foo', ['bar' => 'baz']); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/CookieHeaderTest.php b/tests/Header/CookieHeaderTest.php deleted file mode 100644 index 870edf4..0000000 --- a/tests/Header/CookieHeaderTest.php +++ /dev/null @@ -1,61 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new CookieHeader(); - - $this->assertSame('Cookie', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new CookieHeader([ - 'foo' => 'bar', - 'bar' => 'baz', - 'baz' => [ - 'qux', - ], - ]); - - $this->assertSame('foo=bar; bar=baz; baz%5B0%5D=qux', $header->getFieldValue()); - } - - public function testBuild() - { - $header = new CookieHeader(['foo' => 'bar']); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new CookieHeader(['foo' => 'bar']); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/DateHeaderTest.php b/tests/Header/DateHeaderTest.php deleted file mode 100644 index 0b95d4c..0000000 --- a/tests/Header/DateHeaderTest.php +++ /dev/null @@ -1,82 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $utc = new \DateTime('utc'); - $header = new DateHeader($utc); - - $this->assertSame('Date', $header->getFieldName()); - } - - public function testFieldValue() - { - $utc = new \DateTime('utc'); - $header = new DateHeader($utc); - - $this->assertSame($utc->format(\DateTime::RFC822), $header->getFieldValue()); - } - - public function testFieldValueWithMutableDateTime() - { - $now = new \DateTime('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTime('now', new \DateTimeZone('UTC')); - - $header = new DateHeader($now); - - $this->assertSame($utc->format(\DateTime::RFC822), $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testFieldValueWithImmutableDateTime() - { - $now = new \DateTimeImmutable('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); - - $header = new DateHeader($now); - - $this->assertSame($utc->format(\DateTimeImmutable::RFC822), $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testBuild() - { - $now = new \DateTime('now'); - $header = new DateHeader($now); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $now = new \DateTime('now'); - $header = new DateHeader($now); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/EtagHeaderTest.php b/tests/Header/EtagHeaderTest.php deleted file mode 100644 index 71fa8b7..0000000 --- a/tests/Header/EtagHeaderTest.php +++ /dev/null @@ -1,71 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new EtagHeader('foo'); - - $this->assertSame('ETag', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new EtagHeader('foo'); - - $this->assertSame('"foo"', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $header = new EtagHeader(''); - - $this->assertSame('""', $header->getFieldValue()); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value ""invalid value"" for the header "ETag" is not valid'); - - // cannot contain quotes... - new EtagHeader('"invalid value"'); - } - - public function testBuild() - { - $header = new EtagHeader('foo'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new EtagHeader('foo'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/ExpiresHeaderTest.php b/tests/Header/ExpiresHeaderTest.php deleted file mode 100644 index f06a795..0000000 --- a/tests/Header/ExpiresHeaderTest.php +++ /dev/null @@ -1,82 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $utc = new \DateTime('utc'); - $header = new ExpiresHeader($utc); - - $this->assertSame('Expires', $header->getFieldName()); - } - - public function testFieldValue() - { - $utc = new \DateTime('utc'); - $header = new ExpiresHeader($utc); - - $this->assertSame($utc->format(\DateTime::RFC822), $header->getFieldValue()); - } - - public function testFieldValueWithMutableDateTime() - { - $now = new \DateTime('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTime('now', new \DateTimeZone('UTC')); - - $header = new ExpiresHeader($now); - - $this->assertSame($utc->format(\DateTime::RFC822), $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testFieldValueWithImmutableDateTime() - { - $now = new \DateTimeImmutable('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); - - $header = new ExpiresHeader($now); - - $this->assertSame($utc->format(\DateTimeImmutable::RFC822), $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testBuild() - { - $now = new \DateTime('now'); - $header = new ExpiresHeader($now); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $now = new \DateTime('now'); - $header = new ExpiresHeader($now); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/LastModifiedHeaderTest.php b/tests/Header/LastModifiedHeaderTest.php deleted file mode 100644 index a85ec4c..0000000 --- a/tests/Header/LastModifiedHeaderTest.php +++ /dev/null @@ -1,82 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $utc = new \DateTime('utc'); - $header = new LastModifiedHeader($utc); - - $this->assertSame('Last-Modified', $header->getFieldName()); - } - - public function testFieldValue() - { - $utc = new \DateTime('utc'); - $header = new LastModifiedHeader($utc); - - $this->assertSame($utc->format(\DateTime::RFC822), $header->getFieldValue()); - } - - public function testFieldValueWithMutableDateTime() - { - $now = new \DateTime('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTime('now', new \DateTimeZone('UTC')); - - $header = new LastModifiedHeader($now); - - $this->assertSame($utc->format(\DateTime::RFC822), $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testFieldValueWithImmutableDateTime() - { - $now = new \DateTimeImmutable('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); - - $header = new LastModifiedHeader($now); - - $this->assertSame($utc->format(\DateTimeImmutable::RFC822), $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testBuild() - { - $now = new \DateTime('now'); - $header = new LastModifiedHeader($now); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $now = new \DateTime('now'); - $header = new LastModifiedHeader($now); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/LinkHeaderTest.php b/tests/Header/LinkHeaderTest.php deleted file mode 100644 index 57d86aa..0000000 --- a/tests/Header/LinkHeaderTest.php +++ /dev/null @@ -1,174 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $uri = new Uri('/'); - $header = new LinkHeader($uri); - - $this->assertSame('Link', $header->getFieldName()); - } - - public function testFieldValue() - { - $uri = new Uri('/'); - $header = new LinkHeader($uri); - - $this->assertSame('', $header->getFieldValue()); - } - - public function testParameterWithEmptyValue() - { - $uri = new Uri('/'); - - $header = new LinkHeader($uri, [ - 'foo' => '', - ]); - - $this->assertSame('; foo=""', $header->getFieldValue()); - } - - public function testParameterWithToken() - { - $uri = new Uri('/'); - - $header = new LinkHeader($uri, [ - 'foo' => 'token', - ]); - - $this->assertSame('; foo="token"', $header->getFieldValue()); - } - - public function testParameterWithQuotedString() - { - $uri = new Uri('/'); - - $header = new LinkHeader($uri, [ - 'foo' => 'quoted string', - ]); - - $this->assertSame('; foo="quoted string"', $header->getFieldValue()); - } - - public function testParameterWithInteger() - { - $uri = new Uri('/'); - - $header = new LinkHeader($uri, [ - 'foo' => 1, - ]); - - $this->assertSame('; foo="1"', $header->getFieldValue()); - } - - public function testSeveralParameters() - { - $uri = new Uri('/'); - - $header = new LinkHeader($uri, [ - 'foo' => '', - 'bar' => 'token', - 'baz' => 'quoted string', - 'qux' => 1, - ]); - - $this->assertSame('; foo=""; bar="token"; baz="quoted string"; qux="1"', $header->getFieldValue()); - } - - public function testInvalidParameterName() - { - $uri = new Uri('/'); - - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "invalid name" for the header "Link" is not valid' - ); - - // cannot contain spaces... - new LinkHeader($uri, ['invalid name' => 'value']); - } - - public function testInvalidParameterNameType() - { - $uri = new Uri('/'); - - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "" for the header "Link" is not valid' - ); - - new LinkHeader($uri, [0 => 'value']); - } - - public function testInvalidParameterValue() - { - $uri = new Uri('/'); - - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value ""invalid value"" for the header "Link" is not valid' - ); - - // cannot contain quotes... - new LinkHeader($uri, ['name' => '"invalid value"']); - } - - public function testInvalidParameterValueType() - { - $uri = new Uri('/'); - - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value "" for the header "Link" is not valid' - ); - - // cannot contain quotes... - new LinkHeader($uri, ['name' => []]); - } - - public function testBuild() - { - $uri = new Uri('/'); - $header = new LinkHeader($uri, ['foo' => 'bar']); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $uri = new Uri('/'); - $header = new LinkHeader($uri, ['foo' => 'bar']); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/LocationHeaderTest.php b/tests/Header/LocationHeaderTest.php deleted file mode 100644 index c684f26..0000000 --- a/tests/Header/LocationHeaderTest.php +++ /dev/null @@ -1,61 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $uri = new Uri('/'); - $header = new LocationHeader($uri); - - $this->assertSame('Location', $header->getFieldName()); - } - - public function testFieldValue() - { - $uri = new Uri('/'); - $header = new LocationHeader($uri); - - $this->assertSame('/', $header->getFieldValue()); - } - - public function testBuild() - { - $uri = new Uri('/'); - $header = new LocationHeader($uri); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $uri = new Uri('/'); - $header = new LocationHeader($uri); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/RefreshHeaderTest.php b/tests/Header/RefreshHeaderTest.php deleted file mode 100644 index fa9ad0c..0000000 --- a/tests/Header/RefreshHeaderTest.php +++ /dev/null @@ -1,71 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $uri = new Uri('/'); - $header = new RefreshHeader(0, $uri); - - $this->assertSame('Refresh', $header->getFieldName()); - } - - public function testFieldValue() - { - $uri = new Uri('/'); - $header = new RefreshHeader(0, $uri); - - $this->assertSame('0; url=/', $header->getFieldValue()); - } - - public function testInvalidDelay() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The delay "-1" for the header "Refresh" is not valid'); - - $uri = new Uri('/'); - - new RefreshHeader(-1, $uri); - } - - public function testBuild() - { - $uri = new Uri('/'); - $header = new RefreshHeader(0, $uri); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $uri = new Uri('/'); - $header = new RefreshHeader(0, $uri); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/RetryAfterHeaderTest.php b/tests/Header/RetryAfterHeaderTest.php deleted file mode 100644 index 643f180..0000000 --- a/tests/Header/RetryAfterHeaderTest.php +++ /dev/null @@ -1,82 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $utc = new \DateTime('utc'); - $header = new RetryAfterHeader($utc); - - $this->assertSame('Retry-After', $header->getFieldName()); - } - - public function testFieldValue() - { - $utc = new \DateTime('utc'); - $header = new RetryAfterHeader($utc); - - $this->assertSame($utc->format(\DateTime::RFC822), $header->getFieldValue()); - } - - public function testFieldValueWithMutableDateTime() - { - $now = new \DateTime('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTime('now', new \DateTimeZone('UTC')); - - $header = new RetryAfterHeader($now); - - $this->assertSame($utc->format(\DateTime::RFC822), $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testFieldValueWithImmutableDateTime() - { - $now = new \DateTimeImmutable('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); - - $header = new RetryAfterHeader($now); - - $this->assertSame($utc->format(\DateTimeImmutable::RFC822), $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testBuild() - { - $now = new \DateTime('now'); - $header = new RetryAfterHeader($now); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $now = new \DateTime('now'); - $header = new RetryAfterHeader($now); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/SetCookieHeaderTest.php b/tests/Header/SetCookieHeaderTest.php deleted file mode 100644 index 9abfa85..0000000 --- a/tests/Header/SetCookieHeaderTest.php +++ /dev/null @@ -1,257 +0,0 @@ -assertSame('Lax', SetCookieHeader::SAME_SITE_LAX); - $this->assertSame('Strict', SetCookieHeader::SAME_SITE_STRICT); - $this->assertSame('None', SetCookieHeader::SAME_SITE_NONE); - } - - public function testContracts() - { - $header = new SetCookieHeader('name', 'value'); - - $this->assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new SetCookieHeader('name', 'value'); - - $this->assertSame('Set-Cookie', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new SetCookieHeader('name', 'value'); - - $this->assertSame('name=value; Path=/; HttpOnly; SameSite=Lax', $header->getFieldValue()); - } - - public function testEncodingName() - { - $header = new SetCookieHeader('@foo', 'bar'); - - $this->assertSame('%40foo=bar; Path=/; HttpOnly; SameSite=Lax', $header->getFieldValue()); - } - - public function testEncodingValue() - { - $header = new SetCookieHeader('foo', '@bar'); - - $this->assertSame('foo=%40bar; Path=/; HttpOnly; SameSite=Lax', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $dt = new \DateTime('-1 year', new \DateTimeZone('UTC')); - $header = new SetCookieHeader('name', ''); - - $expected = \sprintf( - 'name=deleted; Expires=%s; Max-Age=0; Path=/; HttpOnly; SameSite=Lax', - $dt->format(\DateTime::RFC822) - ); - - $this->assertSame($expected, $header->getFieldValue()); - } - - public function testExpiresWithMutableDateTime() - { - $now = new \DateTime('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTime('now', new \DateTimeZone('UTC')); - - $header = new SetCookieHeader('name', 'value', $now); - - $expected = \sprintf( - 'name=value; Expires=%s; Max-Age=0; Path=/; HttpOnly; SameSite=Lax', - $utc->format(\DateTime::RFC822) - ); - - $this->assertSame($expected, $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testExpiresWithImmutableDateTime() - { - $now = new \DateTimeImmutable('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); - - $header = new SetCookieHeader('name', 'value', $now); - - $expected = \sprintf( - 'name=value; Expires=%s; Max-Age=0; Path=/; HttpOnly; SameSite=Lax', - $utc->format(\DateTimeImmutable::RFC822) - ); - - $this->assertSame($expected, $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testNegativeExpires() - { - $utc = new \DateTime('-30 seconds', new \DateTimeZone('UTC')); - $header = new SetCookieHeader('name', 'value', $utc); - - // the max-age attribute cannot be negative... - $expected = \sprintf( - 'name=value; Expires=%s; Max-Age=0; Path=/; HttpOnly; SameSite=Lax', - $utc->format(\DateTime::RFC822) - ); - - $this->assertSame($expected, $header->getFieldValue()); - } - - public function testPositiveExpires() - { - $utc = new \DateTime('+30 seconds', new \DateTimeZone('UTC')); - $header = new SetCookieHeader('name', 'value', $utc); - - $expected = \sprintf( - 'name=value; Expires=%s; Max-Age=30; Path=/; HttpOnly; SameSite=Lax', - $utc->format(\DateTime::RFC822) - ); - - $this->assertSame($expected, $header->getFieldValue()); - } - - public function testPath() - { - $header = new SetCookieHeader('name', 'value', null, [ - 'path' => '/assets/', - ]); - - $this->assertSame('name=value; Path=/assets/; HttpOnly; SameSite=Lax', $header->getFieldValue()); - } - - public function testDomain() - { - $header = new SetCookieHeader('name', 'value', null, [ - 'domain' => 'acme.com', - ]); - - $this->assertSame('name=value; Path=/; Domain=acme.com; HttpOnly; SameSite=Lax', $header->getFieldValue()); - } - - public function testSecure() - { - $header = new SetCookieHeader('name', 'value', null, [ - 'secure' => true, - ]); - - $this->assertSame('name=value; Path=/; Secure; HttpOnly; SameSite=Lax', $header->getFieldValue()); - } - - public function testHttpOnly() - { - $header = new SetCookieHeader('name', 'value', null, [ - 'httpOnly' => false, - ]); - - $this->assertSame('name=value; Path=/; SameSite=Lax', $header->getFieldValue()); - } - - public function testSameSite() - { - $header = new SetCookieHeader('name', 'value', null, [ - 'sameSite' => 'Strict', - ]); - - $this->assertSame('name=value; Path=/; HttpOnly; SameSite=Strict', $header->getFieldValue()); - } - - public function testEmptyName() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('Cookie name cannot be empty'); - - new SetCookieHeader('', 'value'); - } - - public function testInvalidName() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The cookie name "name=" contains prohibited characters'); - - new SetCookieHeader('name=', 'value'); - } - - public function testInvalidPath() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The cookie option "path" contains prohibited characters'); - - new SetCookieHeader('name', 'value', null, ['path' => ';']); - } - - public function testInvalidPathDataType() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The cookie option "path" must be a string'); - - new SetCookieHeader('name', 'value', null, ['path' => []]); - } - - public function testInvalidDomain() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The cookie option "domain" contains prohibited characters'); - - new SetCookieHeader('name', 'value', null, ['domain' => ';']); - } - - public function testInvalidDomainDataType() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The cookie option "domain" must be a string'); - - new SetCookieHeader('name', 'value', null, ['domain' => []]); - } - - public function testInvalidSamesite() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The cookie option "sameSite" contains prohibited characters'); - - new SetCookieHeader('name', 'value', null, ['sameSite' => ';']); - } - - public function testInvalidSamesiteDataType() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The cookie option "sameSite" must be a string'); - - new SetCookieHeader('name', 'value', null, ['sameSite' => []]); - } - - public function testBuild() - { - $header = new SetCookieHeader('name', 'value'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new SetCookieHeader('name', 'value'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/SunsetHeaderTest.php b/tests/Header/SunsetHeaderTest.php deleted file mode 100644 index 02df8bf..0000000 --- a/tests/Header/SunsetHeaderTest.php +++ /dev/null @@ -1,82 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $utc = new \DateTime('utc'); - $header = new SunsetHeader($utc); - - $this->assertSame('Sunset', $header->getFieldName()); - } - - public function testFieldValue() - { - $utc = new \DateTime('utc'); - $header = new SunsetHeader($utc); - - $this->assertSame($utc->format(\DateTime::RFC822), $header->getFieldValue()); - } - - public function testFieldValueWithMutableDateTime() - { - $now = new \DateTime('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTime('now', new \DateTimeZone('UTC')); - - $header = new SunsetHeader($now); - - $this->assertSame($utc->format(\DateTime::RFC822), $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testFieldValueWithImmutableDateTime() - { - $now = new \DateTimeImmutable('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); - - $header = new SunsetHeader($now); - - $this->assertSame($utc->format(\DateTimeImmutable::RFC822), $header->getFieldValue()); - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testBuild() - { - $now = new \DateTime('now'); - $header = new SunsetHeader($now); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $now = new \DateTime('now'); - $header = new SunsetHeader($now); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/TrailerHeaderTest.php b/tests/Header/TrailerHeaderTest.php deleted file mode 100644 index 1614191..0000000 --- a/tests/Header/TrailerHeaderTest.php +++ /dev/null @@ -1,78 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new TrailerHeader('foo'); - - $this->assertSame('Trailer', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new TrailerHeader('foo'); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The value "" for the header "Trailer" is not valid' - ); - - new TrailerHeader(''); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The value "@" for the header "Trailer" is not valid' - ); - - // isn't a token... - new TrailerHeader('@'); - } - - public function testBuild() - { - $header = new TrailerHeader('foo'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new TrailerHeader('foo'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/TransferEncodingHeaderTest.php b/tests/Header/TransferEncodingHeaderTest.php deleted file mode 100644 index ea39a43..0000000 --- a/tests/Header/TransferEncodingHeaderTest.php +++ /dev/null @@ -1,104 +0,0 @@ -assertSame('chunked', TransferEncodingHeader::HTTP_TRANSFER_ENCODING_CHUNKED); - $this->assertSame('compress', TransferEncodingHeader::HTTP_TRANSFER_ENCODING_COMPRESS); - $this->assertSame('deflate', TransferEncodingHeader::HTTP_TRANSFER_ENCODING_DEFLATE); - $this->assertSame('gzip', TransferEncodingHeader::HTTP_TRANSFER_ENCODING_GZIP); - } - - public function testContracts() - { - $header = new TransferEncodingHeader('foo'); - - $this->assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new TransferEncodingHeader('foo'); - - $this->assertSame('Transfer-Encoding', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new TransferEncodingHeader('foo'); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testSeveralValues() - { - $header = new TransferEncodingHeader('foo', 'bar', 'baz'); - - $this->assertSame('foo, bar, baz', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Transfer-Encoding" is not valid'); - - new TransferEncodingHeader(''); - } - - public function testEmptyValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Transfer-Encoding" is not valid'); - - new TransferEncodingHeader('foo', '', 'baz'); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Transfer-Encoding" is not valid'); - - // isn't a token... - new TransferEncodingHeader('@'); - } - - public function testInvalidValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Transfer-Encoding" is not valid'); - - // isn't a token... - new TransferEncodingHeader('foo', '@', 'baz'); - } - - public function testBuild() - { - $header = new TransferEncodingHeader('foo'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new TransferEncodingHeader('foo'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/VaryHeaderTest.php b/tests/Header/VaryHeaderTest.php deleted file mode 100644 index 8e37012..0000000 --- a/tests/Header/VaryHeaderTest.php +++ /dev/null @@ -1,96 +0,0 @@ -assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new VaryHeader('foo'); - - $this->assertSame('Vary', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new VaryHeader('foo'); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testSeveralValues() - { - $header = new VaryHeader('foo', 'bar', 'baz'); - - $this->assertSame('foo, bar, baz', $header->getFieldValue()); - } - - public function testEmptyValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Vary" is not valid'); - - new VaryHeader(''); - } - - public function testEmptyValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Vary" is not valid'); - - new VaryHeader('foo', '', 'baz'); - } - - public function testInvalidValue() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Vary" is not valid'); - - // isn't a token... - new VaryHeader('@'); - } - - public function testInvalidValueAmongOthers() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Vary" is not valid'); - - // isn't a token... - new VaryHeader('foo', '@', 'baz'); - } - - public function testBuild() - { - $header = new VaryHeader('foo'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new VaryHeader('foo'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/WWWAuthenticateHeaderTest.php b/tests/Header/WWWAuthenticateHeaderTest.php deleted file mode 100644 index 388dc82..0000000 --- a/tests/Header/WWWAuthenticateHeaderTest.php +++ /dev/null @@ -1,180 +0,0 @@ -assertSame('Basic', WWWAuthenticateHeader::HTTP_AUTHENTICATE_SCHEME_BASIC); - $this->assertSame('Bearer', WWWAuthenticateHeader::HTTP_AUTHENTICATE_SCHEME_BEARER); - $this->assertSame('Digest', WWWAuthenticateHeader::HTTP_AUTHENTICATE_SCHEME_DIGEST); - $this->assertSame('HOBA', WWWAuthenticateHeader::HTTP_AUTHENTICATE_SCHEME_HOBA); - $this->assertSame('Mutual', WWWAuthenticateHeader::HTTP_AUTHENTICATE_SCHEME_MUTUAL); - $this->assertSame('Negotiate', WWWAuthenticateHeader::HTTP_AUTHENTICATE_SCHEME_NEGOTIATE); - $this->assertSame('OAuth', WWWAuthenticateHeader::HTTP_AUTHENTICATE_SCHEME_OAUTH); - $this->assertSame('SCRAM-SHA-1', WWWAuthenticateHeader::HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_1); - $this->assertSame('SCRAM-SHA-256', WWWAuthenticateHeader::HTTP_AUTHENTICATE_SCHEME_SCRAM_SHA_256); - $this->assertSame('vapid', WWWAuthenticateHeader::HTTP_AUTHENTICATE_SCHEME_VAPID); - } - - public function testContracts() - { - $header = new WWWAuthenticateHeader('foo'); - - $this->assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new WWWAuthenticateHeader('foo'); - - $this->assertSame('WWW-Authenticate', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new WWWAuthenticateHeader('foo'); - - $this->assertSame('foo', $header->getFieldValue()); - } - - public function testParameterWithEmptyValue() - { - $header = new WWWAuthenticateHeader('foo', [ - 'bar' => '', - ]); - - $this->assertSame('foo bar=""', $header->getFieldValue()); - } - - public function testParameterWithToken() - { - $header = new WWWAuthenticateHeader('foo', [ - 'bar' => 'token', - ]); - - $this->assertSame('foo bar="token"', $header->getFieldValue()); - } - - public function testParameterWithQuotedString() - { - $header = new WWWAuthenticateHeader('foo', [ - 'bar' => 'quoted string', - ]); - - $this->assertSame('foo bar="quoted string"', $header->getFieldValue()); - } - - public function testParameterWithInteger() - { - $header = new WWWAuthenticateHeader('foo', [ - 'bar' => 1, - ]); - - $this->assertSame('foo bar="1"', $header->getFieldValue()); - } - - public function testSeveralParameters() - { - $header = new WWWAuthenticateHeader('foo', [ - 'bar' => '', - 'baz' => 'token', - 'bat' => 'quoted string', - 'qux' => 1, - ]); - - $this->assertSame('foo bar="", baz="token", bat="quoted string", qux="1"', $header->getFieldValue()); - } - - public function testEmptyScheme() - { - $this->expectException(\InvalidArgumentException::class); - - new WWWAuthenticateHeader(''); - } - - public function testInvalidScheme() - { - $this->expectException(\InvalidArgumentException::class); - - // isn't a token... - new WWWAuthenticateHeader('@'); - } - - public function testInvalidParameterName() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "invalid name" for the header "WWW-Authenticate" is not valid' - ); - - // cannot contain spaces... - new WWWAuthenticateHeader('foo', ['invalid name' => 'value']); - } - - public function testInvalidParameterNameType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter name "" for the header "WWW-Authenticate" is not valid' - ); - - // cannot contain spaces... - new WWWAuthenticateHeader('foo', [0 => 'value']); - } - - public function testInvalidParameterValue() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value ""invalid value"" for the header "WWW-Authenticate" is not valid' - ); - - // cannot contain quotes... - new WWWAuthenticateHeader('foo', ['name' => '"invalid value"']); - } - - public function testInvalidParameterValueType() - { - $this->expectException(\InvalidArgumentException::class); - - $this->expectExceptionMessage( - 'The parameter value "" for the header "WWW-Authenticate" is not valid' - ); - - // cannot contain quotes... - new WWWAuthenticateHeader('foo', ['name' => []]); - } - - public function testBuild() - { - $header = new WWWAuthenticateHeader('foo', ['bar' => 'baz']); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new WWWAuthenticateHeader('foo', ['bar' => 'baz']); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} diff --git a/tests/Header/WarningHeaderTest.php b/tests/Header/WarningHeaderTest.php deleted file mode 100644 index fb884f5..0000000 --- a/tests/Header/WarningHeaderTest.php +++ /dev/null @@ -1,134 +0,0 @@ -assertSame(110, WarningHeader::HTTP_WARNING_CODE_RESPONSE_IS_STALE); - $this->assertSame(111, WarningHeader::HTTP_WARNING_CODE_REVALIDATION_FAILED); - $this->assertSame(112, WarningHeader::HTTP_WARNING_CODE_DISCONNECTED_OPERATION); - $this->assertSame(113, WarningHeader::HTTP_WARNING_CODE_HEURISTIC_EXPIRATION); - $this->assertSame(199, WarningHeader::HTTP_WARNING_CODE_MISCELLANEOUS_WARNING); - $this->assertSame(214, WarningHeader::HTTP_WARNING_CODE_TRANSFORMATION_APPLIED); - $this->assertSame(299, WarningHeader::HTTP_WARNING_CODE_MISCELLANEOUS_PERSISTENT_WARNING); - } - - public function testContracts() - { - $header = new WarningHeader(199, 'agent', 'text'); - - $this->assertInstanceOf(HeaderInterface::class, $header); - } - - public function testFieldName() - { - $header = new WarningHeader(199, 'agent', 'text'); - - $this->assertSame('Warning', $header->getFieldName()); - } - - public function testFieldValue() - { - $header = new WarningHeader(199, 'agent', 'text'); - - $this->assertSame('199 agent "text"', $header->getFieldValue()); - } - - public function testFieldValueWithDate() - { - $now = new \DateTime('now', new \DateTimeZone('Europe/Moscow')); - $utc = new \DateTime('now', new \DateTimeZone('UTC')); - - $header = new WarningHeader(199, 'agent', 'text', $now); - - $this->assertSame( - \sprintf( - '199 agent "text" "%s"', - $utc->format(\DateTime::RFC822) - ), - $header->getFieldValue() - ); - - // cannot be modified... - $this->assertSame('Europe/Moscow', $now->getTimezone()->getName()); - } - - public function testCodeLessThat100() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The code "99" for the header "Warning" is not valid'); - - new WarningHeader(99, 'agent', 'text'); - } - - public function testCodeGreaterThat999() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The code "1000" for the header "Warning" is not valid'); - - new WarningHeader(1000, 'agent', 'text'); - } - - public function testEmptyAgent() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "" for the header "Warning" is not valid'); - - new WarningHeader(199, '', 'text'); - } - - public function testInvalidAgent() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value "@" for the header "Warning" is not valid'); - - // isn't a token... - new WarningHeader(199, '@', 'text'); - } - - public function testEmptyText() - { - $header = new WarningHeader(199, 'agent', ''); - - $this->assertSame('199 agent ""', $header->getFieldValue()); - } - - public function testInvalidText() - { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage('The value ""text"" for the header "Warning" is not valid'); - - // cannot contain quotes... - new WarningHeader(199, 'agent', '"text"'); - } - - public function testBuild() - { - $header = new WarningHeader(199, 'agent', 'text'); - - $expected = \sprintf('%s: %s', $header->getFieldName(), $header->getFieldValue()); - - $this->assertSame($expected, $header->__toString()); - } - - public function testIterator() - { - $header = new WarningHeader(199, 'agent', 'text'); - - $this->assertSame( - [ - $header->getFieldName(), - $header->getFieldValue(), - ], - \iterator_to_array($header->getIterator()) - ); - } -} From b5688e562129e4adf20d6fefcdd37dc67ebac80e Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 15 Mar 2023 01:34:55 +0100 Subject: [PATCH 33/49] delete --- src/ServerRequestHelper.php | 619 ------------------ .../ServerRequestHelperIntegrationTest.php | 36 - tests/ServerRequestHelperTest.php | 17 - 3 files changed, 672 deletions(-) delete mode 100644 src/ServerRequestHelper.php delete mode 100644 tests/Integration/ServerRequestHelperIntegrationTest.php delete mode 100644 tests/ServerRequestHelperTest.php diff --git a/src/ServerRequestHelper.php b/src/ServerRequestHelper.php deleted file mode 100644 index fa5da26..0000000 --- a/src/ServerRequestHelper.php +++ /dev/null @@ -1,619 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message; - -/** - * Import classes - */ -use Fig\Http\Message\RequestMethodInterface; -use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Message\StreamInterface; -use Psr\Http\Message\UriInterface; -use Sunrise\Http\Message\Entity\IpAddress; - -/** - * Import functions - */ -use function explode; -use function key; -use function reset; -use function strncmp; -use function strpos; -use function strstr; -use function strtolower; -use function trim; - -/** - * ServerRequestHelper - */ -final class ServerRequestHelper implements ServerRequestInterface, RequestMethodInterface -{ - - /** - * @var ServerRequestInterface - */ - private ServerRequestInterface $request; - - /** - * Constructor of the class - * - * @param ServerRequestInterface $request - */ - public function __construct(ServerRequestInterface $request) - { - $this->request = $request; - } - - /** - * Creates the proxy from the given object - * - * @param ServerRequestInterface $request - * - * @return self - */ - public static function create(ServerRequestInterface $request): self - { - if ($request instanceof self) { - return $request; - } - - return new self($request); - } - - /** - * Checks if the request is JSON - * - * @link https://tools.ietf.org/html/rfc4627 - * - * @return bool - */ - public function isJson(): bool - { - return $this->clientProducesMediaType([ - 'application/json', - ]); - } - - /** - * Checks if the request is XML - * - * @link https://tools.ietf.org/html/rfc2376 - * - * @return bool - */ - public function isXml(): bool - { - return $this->clientProducesMediaType([ - 'application/xml', - 'text/xml', - ]); - } - - /** - * Gets the client's IP address - * - * @param array $proxyChain - * - * @return IpAddress - */ - public function getClientIpAddress(array $proxyChain = []): IpAddress - { - $env = $this->request->getServerParams(); - - /** @var string */ - $client = $env['REMOTE_ADDR'] ?? '::1'; - - /** @var list */ - $proxies = []; - - while (isset($proxyChain[$client])) { - $proxyHeader = $proxyChain[$client]; - unset($proxyChain[$client]); - - $header = $this->request->getHeaderLine($proxyHeader); - if ($header === '') { - break; - } - - $addresses = explode(',', $header); - foreach ($addresses as $i => $address) { - $addresses[$i] = trim($address); - if ($addresses[$i] === '') { - unset($addresses[$i]); - } - } - - if ($addresses === []) { - break; - } - - $client = reset($addresses); - unset($addresses[key($addresses)]); - - foreach ($addresses as $address) { - $proxies[] = $address; - } - } - - return new IpAddress($client, $proxies); - } - - /** - * Gets the client's produced media type - * - * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.1 - * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.5 - * - * @return string - */ - public function getClientProducedMediaType(): string - { - $header = $this->request->getHeaderLine('Content-Type'); - if ($header === '') { - return ''; - } - - $result = strstr($header, ';', true); - if ($result === false) { - $result = $header; - } - - $result = trim($result); - if ($result === '') { - return ''; - } - - return strtolower($result); - } - - /** - * Gets the client's consumed media types - * - * @link https://tools.ietf.org/html/rfc7231#section-1.2 - * @link https://tools.ietf.org/html/rfc7231#section-3.1.1.1 - * @link https://tools.ietf.org/html/rfc7231#section-5.3.2 - * - * @return array> - */ - public function getClientConsumedMediaTypes(): array - { - $header = $this->request->getHeaderLine('Accept'); - if ($header === '') { - return []; - } - - $accepts = header_accept_like_parse($header); - if (empty($accepts)) { - return []; - } - - $result = []; - foreach ($accepts as $type => $params) { - $result[strtolower($type)] = $params; - } - - return $result; - } - - /** - * Gets the client's consumed encodings - * - * @return array> - */ - public function getClientConsumedEncodings(): array - { - $header = $this->request->getHeaderLine('Accept-Encoding'); - if ($header === '') { - return []; - } - - $accepts = header_accept_like_parse($header); - if (empty($accepts)) { - return []; - } - - return $accepts; - } - - /** - * Gets the client's consumed languages - * - * @return array> - */ - public function getClientConsumedLanguages(): array - { - $header = $this->request->getHeaderLine('Accept-Language'); - if ($header === '') { - return []; - } - - $accepts = header_accept_like_parse($header); - if (empty($accepts)) { - return []; - } - - return $accepts; - } - - /** - * Checks if the client produces one of the given media types - * - * @param list $consumes - * - * @return bool - */ - public function clientProducesMediaType(array $consumes): bool - { - if ($consumes === []) { - return true; - } - - $produced = $this->getClientProducedMediaType(); - if ($produced === '') { - return false; - } - - foreach ($consumes as $consumed) { - if (media_types_compare($consumed, $produced)) { - return true; - } - } - - return false; - } - - /** - * Checks if the client consumes one of the given media types - * - * @param list $produces - * - * @return bool - */ - public function clientConsumesMediaType(array $produces): bool - { - if ($produces === []) { - return true; - } - - $consumes = $this->getClientConsumedMediaTypes(); - if ($consumes === []) { - return true; - } - - if (isset($consumes['*/*'])) { - return true; - } - - foreach ($produces as $a) { - foreach ($consumes as $b => $_) { - if (media_types_compare($a, $b)) { - return true; - } - } - } - - return false; - } - - /** - * Checks if the given media types are equal - * - * @param string $a - * @param string $b - * - * @return bool - */ - public function equalsMediaTypes(string $a, string $b): bool - { - if ($a === $b) { - return true; - } - - $slash = strpos($a, '/'); - if ($slash === false || !isset($b[$slash]) || $b[$slash] !== '/') { - return false; - } - - $star = $slash + 1; - if (!isset($a[$star], $b[$star])) { - return false; - } - - if (!($a[$star] === '*' || $b[$star] === '*')) { - return false; - } - - return strncmp($a, $b, $star) === 0; - } - - /** - * {@inheritdoc} - */ - public function getProtocolVersion(): string - { - return $this->request->getProtocolVersion(); - } - - /** - * {@inheritdoc} - */ - public function withProtocolVersion($version) - { - $clone = clone $this; - $clone->request = $clone->request->withProtocolVersion($version); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function getHeaders(): array - { - return $this->request->getHeaders(); - } - - /** - * {@inheritdoc} - */ - public function hasHeader($name): bool - { - return $this->request->hasHeader($name); - } - - /** - * {@inheritdoc} - */ - public function getHeader($name): array - { - return $this->request->getHeader($name); - } - - /** - * {@inheritdoc} - */ - public function getHeaderLine($name): string - { - return $this->request->getHeaderLine($name); - } - - /** - * {@inheritdoc} - */ - public function withHeader($name, $value) - { - $clone = clone $this; - $clone->request = $clone->request->withHeader($name, $value); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function withAddedHeader($name, $value) - { - $clone = clone $this; - $clone->request = $clone->request->withAddedHeader($name, $value); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function withoutHeader($name) - { - $clone = clone $this; - $clone->request = $clone->request->withoutHeader($name); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function getBody(): StreamInterface - { - return $this->request->getBody(); - } - - /** - * {@inheritdoc} - */ - public function withBody(StreamInterface $body) - { - $clone = clone $this; - $clone->request = $clone->request->withBody($body); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function getMethod(): string - { - return $this->request->getMethod(); - } - - /** - * {@inheritdoc} - */ - public function withMethod($method) - { - $clone = clone $this; - $clone->request = $clone->request->withMethod($method); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function getUri(): UriInterface - { - return $this->request->getUri(); - } - - /** - * {@inheritdoc} - */ - public function withUri(UriInterface $uri, $preserveHost = false) - { - $clone = clone $this; - $clone->request = $clone->request->withUri($uri, $preserveHost); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function getRequestTarget(): string - { - return $this->request->getRequestTarget(); - } - - /** - * {@inheritdoc} - */ - public function withRequestTarget($requestTarget) - { - $clone = clone $this; - $clone->request = $clone->request->withRequestTarget($requestTarget); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function getServerParams(): array - { - return $this->request->getServerParams(); - } - - /** - * {@inheritdoc} - */ - public function getQueryParams(): array - { - return $this->request->getQueryParams(); - } - - /** - * {@inheritdoc} - */ - public function withQueryParams(array $query) - { - $clone = clone $this; - $clone->request = $clone->request->withQueryParams($query); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function getCookieParams(): array - { - return $this->request->getCookieParams(); - } - - /** - * {@inheritdoc} - */ - public function withCookieParams(array $cookies) - { - $clone = clone $this; - $clone->request = $clone->request->withCookieParams($cookies); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function getUploadedFiles(): array - { - return $this->request->getUploadedFiles(); - } - - /** - * {@inheritdoc} - */ - public function withUploadedFiles(array $uploadedFiles) - { - $clone = clone $this; - $clone->request = $clone->request->withUploadedFiles($uploadedFiles); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function getParsedBody() - { - return $this->request->getParsedBody(); - } - - /** - * {@inheritdoc} - */ - public function withParsedBody($data) - { - $clone = clone $this; - $clone->request = $clone->request->withParsedBody($data); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function getAttributes(): array - { - return $this->request->getAttributes(); - } - - /** - * {@inheritdoc} - */ - public function getAttribute($name, $default = null) - { - return $this->request->getAttribute($name, $default); - } - - /** - * {@inheritdoc} - */ - public function withAttribute($name, $value) - { - $clone = clone $this; - $clone->request = $clone->request->withAttribute($name, $value); - - return $clone; - } - - /** - * {@inheritdoc} - */ - public function withoutAttribute($name) - { - $clone = clone $this; - $clone->request = $clone->request->withoutAttribute($name); - - return $clone; - } -} diff --git a/tests/Integration/ServerRequestHelperIntegrationTest.php b/tests/Integration/ServerRequestHelperIntegrationTest.php deleted file mode 100644 index a0cb718..0000000 --- a/tests/Integration/ServerRequestHelperIntegrationTest.php +++ /dev/null @@ -1,36 +0,0 @@ - Date: Wed, 15 Mar 2023 01:36:04 +0100 Subject: [PATCH 34/49] delete --- functions/header_accept_like_parse.php | 201 ------------------------- 1 file changed, 201 deletions(-) delete mode 100644 functions/header_accept_like_parse.php diff --git a/functions/header_accept_like_parse.php b/functions/header_accept_like_parse.php deleted file mode 100644 index 7c155ed..0000000 --- a/functions/header_accept_like_parse.php +++ /dev/null @@ -1,201 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message; - -/** - * Import functions - */ -use function uasort; - -/** - * Parses the given accept-like header - * - * Returns null if the header isn't valid. - * - * @param string $header - * - * @return ?array> - */ -function header_accept_like_parse(string $header): ?array -{ - // OWS according to RFC-7230 - static $ows = [ - "\x09" => 1, "\x20" => 1, - ]; - - // token according to RFC-7230 - static $token = [ - "\x21" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1, "\x2a" => 1, "\x2b" => 1, - "\x2d" => 1, "\x2e" => 1, "\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, - "\x36" => 1, "\x37" => 1, "\x38" => 1, "\x39" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, - "\x45" => 1, "\x46" => 1, "\x47" => 1, "\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, - "\x4d" => 1, "\x4e" => 1, "\x4f" => 1, "\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, - "\x55" => 1, "\x56" => 1, "\x57" => 1, "\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5e" => 1, "\x5f" => 1, - "\x60" => 1, "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1, - "\x68" => 1, "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1, - "\x70" => 1, "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1, - "\x78" => 1, "\x79" => 1, "\x7a" => 1, "\x7c" => 1, "\x7e" => 1, - ]; - - // quoted-string according to RFC-7230 - static $quotedString = [ - "\x09" => 1, "\x20" => 1, "\x21" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1, - "\x28" => 1, "\x29" => 1, "\x2a" => 1, "\x2b" => 1, "\x2c" => 1, "\x2d" => 1, "\x2e" => 1, "\x2f" => 1, - "\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, "\x36" => 1, "\x37" => 1, - "\x38" => 1, "\x39" => 1, "\x3a" => 1, "\x3b" => 1, "\x3c" => 1, "\x3d" => 1, "\x3e" => 1, "\x3f" => 1, - "\x40" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, "\x45" => 1, "\x46" => 1, "\x47" => 1, - "\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, "\x4d" => 1, "\x4e" => 1, "\x4f" => 1, - "\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, "\x55" => 1, "\x56" => 1, "\x57" => 1, - "\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5b" => 1, "\x5d" => 1, "\x5e" => 1, "\x5f" => 1, "\x60" => 1, - "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1, "\x68" => 1, - "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1, "\x70" => 1, - "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1, "\x78" => 1, - "\x79" => 1, "\x7a" => 1, "\x7b" => 1, "\x7c" => 1, "\x7d" => 1, "\x7e" => 1, "\x80" => 1, "\x81" => 1, - "\x82" => 1, "\x83" => 1, "\x84" => 1, "\x85" => 1, "\x86" => 1, "\x87" => 1, "\x88" => 1, "\x89" => 1, - "\x8a" => 1, "\x8b" => 1, "\x8c" => 1, "\x8d" => 1, "\x8e" => 1, "\x8f" => 1, "\x90" => 1, "\x91" => 1, - "\x92" => 1, "\x93" => 1, "\x94" => 1, "\x95" => 1, "\x96" => 1, "\x97" => 1, "\x98" => 1, "\x99" => 1, - "\x9a" => 1, "\x9b" => 1, "\x9c" => 1, "\x9d" => 1, "\x9e" => 1, "\x9f" => 1, "\xa0" => 1, "\xa1" => 1, - "\xa2" => 1, "\xa3" => 1, "\xa4" => 1, "\xa5" => 1, "\xa6" => 1, "\xa7" => 1, "\xa8" => 1, "\xa9" => 1, - "\xaa" => 1, "\xab" => 1, "\xac" => 1, "\xad" => 1, "\xae" => 1, "\xaf" => 1, "\xb0" => 1, "\xb1" => 1, - "\xb2" => 1, "\xb3" => 1, "\xb4" => 1, "\xb5" => 1, "\xb6" => 1, "\xb7" => 1, "\xb8" => 1, "\xb9" => 1, - "\xba" => 1, "\xbb" => 1, "\xbc" => 1, "\xbd" => 1, "\xbe" => 1, "\xbf" => 1, "\xc0" => 1, "\xc1" => 1, - "\xc2" => 1, "\xc3" => 1, "\xc4" => 1, "\xc5" => 1, "\xc6" => 1, "\xc7" => 1, "\xc8" => 1, "\xc9" => 1, - "\xca" => 1, "\xcb" => 1, "\xcc" => 1, "\xcd" => 1, "\xce" => 1, "\xcf" => 1, "\xd0" => 1, "\xd1" => 1, - "\xd2" => 1, "\xd3" => 1, "\xd4" => 1, "\xd5" => 1, "\xd6" => 1, "\xd7" => 1, "\xd8" => 1, "\xd9" => 1, - "\xda" => 1, "\xdb" => 1, "\xdc" => 1, "\xdd" => 1, "\xde" => 1, "\xdf" => 1, "\xe0" => 1, "\xe1" => 1, - "\xe2" => 1, "\xe3" => 1, "\xe4" => 1, "\xe5" => 1, "\xe6" => 1, "\xe7" => 1, "\xe8" => 1, "\xe9" => 1, - "\xea" => 1, "\xeb" => 1, "\xec" => 1, "\xed" => 1, "\xee" => 1, "\xef" => 1, "\xf0" => 1, "\xf1" => 1, - "\xf2" => 1, "\xf3" => 1, "\xf4" => 1, "\xf5" => 1, "\xf6" => 1, "\xf7" => 1, "\xf8" => 1, "\xf9" => 1, - "\xfa" => 1, "\xfb" => 1, "\xfc" => 1, "\xfd" => 1, "\xfe" => 1, "\xff" => 1, - ]; - - $offset = -1; - - $inToken = true; - $inParamName = false; - $inParamValue = false; - $inQuotes = false; - - $data = []; - - $i = 0; - $p = 0; - - while (true) { - $char = $header[++$offset] ?? null; - - if (!isset($char)) { - break; - } - - $prev = $header[$offset-1] ?? null; - $next = $header[$offset+1] ?? null; - - if ($char === ',' && !$inQuotes) { - $inToken = true; - $inParamName = false; - $inParamValue = false; - $i++; - $p = 0; - continue; - } - if ($char === ';' && !$inQuotes) { - $inToken = false; - $inParamName = true; - $inParamValue = false; - $p++; - continue; - } - if ($char === '=' && !$inQuotes) { - $inToken = false; - $inParamName = false; - $inParamValue = true; - continue; - } - - // ignoring whitespaces around tokens... - if (isset($ows[$char]) && !$inQuotes) { - // en-GB[ ], ... - // ~~~~~~^~~~~~~ - if ($inToken && isset($data[$i][0])) { - $inToken = false; - } - // en-GB; q[ ]= 0.8, ... - // ~~~~~~~~~^~~~~~~~~~~~ - if ($inParamName && isset($data[$i][1][$p][0])) { - $inParamName = false; - } - // en-GB; q = 0.8[ ], ... - // ~~~~~~~~~~~~~~~^~~~~~~ - if ($inParamValue && isset($data[$i][1][$p][1])) { - $inParamValue = false; - } - - continue; - } - - // ignoring backslashes before double quotes in the quoted parameter value... - if ($char === '\\' && $next === '"' && $inQuotes) { - continue; - } - - if ($char === '"' && $inParamValue && !$inQuotes && !isset($data[$i][1][$p][1])) { - $inQuotes = true; - continue; - } - if ($char === '"' && $prev !== '\\' && $inQuotes) { - $inParamValue = false; - $inQuotes = false; - continue; - } - - // [en-GB]; q=0.8, ... - // ~^^^^^~~~~~~~~~~~~~ - if ($inToken && (isset($token[$char]) || $char === '/')) { - $data[$i][0] ??= ''; - $data[$i][0] .= $char; - continue; - } - // en-GB; [q]=0.8, ... - // ~~~~~~~~^~~~~~~~~~~ - if ($inParamName && isset($token[$char]) && isset($data[$i][0])) { - $data[$i][1][$p][0] ??= ''; - $data[$i][1][$p][0] .= $char; - continue; - } - // en-GB; q=[0.8], ... - // ~~~~~~~~~~^^^~~~~~~ - // phpcs:ignore Generic.Files.LineLength - if ($inParamValue && (isset($token[$char]) || ($inQuotes && (isset($quotedString[$char]) || ($prev . $char) === '\"'))) && isset($data[$i][1][$p][0])) { - $data[$i][1][$p][1] ??= ''; - $data[$i][1][$p][1] .= $char; - continue; - } - - // the header is invalid... - return null; - } - - $result = []; - foreach ($data as $item) { - $result[$item[0]] = []; - if (isset($item[1])) { - foreach ($item[1] as $param) { - $result[$item[0]][$param[0]] = $param[1] ?? ''; - } - } - } - - uasort($result, fn(array $a, array $b): int => ($b['q'] ?? 1) <=> ($a['q'] ?? 1)); - - return $result; -} From b258404642df4bed3f7ddf1505ecac20a54f5f8b Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 15 Mar 2023 01:36:11 +0100 Subject: [PATCH 35/49] Update composer.json --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 3ba002d..48ba469 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,6 @@ }, "autoload": { "files": [ - "functions/header_accept_like_parse.php", "functions/server_request_files.php", "functions/server_request_headers.php", "functions/server_request_method.php", From 2688f42380a8265e8daeb5e567701cbe1dcf8169 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 15 Mar 2023 01:37:27 +0100 Subject: [PATCH 36/49] insignificant changes --- src/HeaderInterface.php | 12 ++++-------- src/Request.php | 11 ++--------- src/Response.php | 11 ++--------- src/Response/HtmlResponse.php | 2 +- src/Stream/PhpTempStream.php | 9 +-------- src/Stream/TempFileStream.php | 6 ++---- src/UploadedFile.php | 4 +--- src/Uri/Component/Fragment.php | 4 ++-- src/Uri/Component/Host.php | 4 ++-- src/Uri/Component/Password.php | 4 ++-- src/Uri/Component/Path.php | 4 ++-- src/Uri/Component/Query.php | 4 ++-- src/Uri/Component/Scheme.php | 4 ++-- src/Uri/Component/User.php | 4 ++-- 14 files changed, 27 insertions(+), 56 deletions(-) diff --git a/src/HeaderInterface.php b/src/HeaderInterface.php index 54408a2..ac5cc54 100644 --- a/src/HeaderInterface.php +++ b/src/HeaderInterface.php @@ -17,10 +17,6 @@ use IteratorAggregate; /** - * - * $response->withHeader(...new SetCookieHeader('foo', 'bar')) - * - * * @extends IteratorAggregate */ interface HeaderInterface extends IteratorAggregate @@ -34,21 +30,21 @@ interface HeaderInterface extends IteratorAggregate public const RFC822_DATE_FORMAT = 'D, d M y H:i:s O'; /** - * Regular Expression for a token validation according to RFC-7230 + * Regular Expression used for a token validation according to RFC-7230 * * @var string */ public const RFC7230_TOKEN_REGEX = '/^[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+$/'; /** - * Regular Expression for a field-value validation according to RFC-7230 + * Regular Expression used for a field-value validation according to RFC-7230 * * @var string */ public const RFC7230_FIELD_VALUE_REGEX = '/^[\x09\x20-\x7E\x80-\xFF]*$/'; /** - * Regular Expression for a quoted-string validation according to RFC-7230 + * Regular Expression used for a quoted-string validation according to RFC-7230 * * @var string */ @@ -69,7 +65,7 @@ public function getFieldName(): string; public function getFieldValue(): string; /** - * Converts the header field to a string + * Converts the header to a field * * @return string */ diff --git a/src/Request.php b/src/Request.php index 00ebeeb..c86b5b5 100644 --- a/src/Request.php +++ b/src/Request.php @@ -37,25 +37,18 @@ class Request extends Message implements RequestInterface, RequestMethodInterfac { /** - * Regular Expression for a request target validation + * Regular Expression used for a request target validation * * @var string */ public const RFC7230_REQUEST_TARGET_REGEX = '/^[\x21-\x7E\x80-\xFF]+$/'; - /** - * Default request method - * - * @var string - */ - public const DEFAULT_METHOD = self::METHOD_GET; - /** * The request method (aka verb) * * @var string */ - private string $method = self::DEFAULT_METHOD; + private string $method = self::METHOD_GET; /** * The request URI diff --git a/src/Response.php b/src/Response.php index 9798c39..501cb36 100644 --- a/src/Response.php +++ b/src/Response.php @@ -116,26 +116,19 @@ class Response extends Message implements ResponseInterface, StatusCodeInterface 511 => 'Network Authentication Required', ]; - /** - * Default response status code - * - * @var int - */ - public const DEFAULT_STATUS_CODE = self::STATUS_OK; - /** * The response's status code * * @var int */ - private int $statusCode = self::DEFAULT_STATUS_CODE; + private int $statusCode = self::STATUS_OK; /** * The response's reason phrase * * @var string */ - private string $reasonPhrase = self::REASON_PHRASES[self::DEFAULT_STATUS_CODE]; + private string $reasonPhrase = self::REASON_PHRASES[self::STATUS_OK]; /** * Constrictor of the class diff --git a/src/Response/HtmlResponse.php b/src/Response/HtmlResponse.php index 955967d..3f7cead 100644 --- a/src/Response/HtmlResponse.php +++ b/src/Response/HtmlResponse.php @@ -50,7 +50,7 @@ public function __construct(int $statusCode, $html) } /** - * Creates the response body from the given HTML + * Creates the response body from the given HTML data * * @param mixed $html * diff --git a/src/Stream/PhpTempStream.php b/src/Stream/PhpTempStream.php index d3618a0..74dd59e 100644 --- a/src/Stream/PhpTempStream.php +++ b/src/Stream/PhpTempStream.php @@ -14,7 +14,6 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidArgumentException; use Sunrise\Http\Message\Stream; /** @@ -33,16 +32,10 @@ final class PhpTempStream extends Stream * Constructor of the class * * @param string $mode - * @param int $maxmemory - * - * @throws InvalidArgumentException + * @param int<0, max> $maxmemory */ public function __construct(string $mode = 'r+b', int $maxmemory = 2097152) { - if ($maxmemory < 0) { - throw new InvalidArgumentException('Argument #2 ($maxmemory) must be greater than or equal to 0'); - } - parent::__construct(fopen(sprintf('php://temp/maxmemory:%d', $maxmemory), $mode)); } } diff --git a/src/Stream/TempFileStream.php b/src/Stream/TempFileStream.php index de6c4c9..7de5bb4 100644 --- a/src/Stream/TempFileStream.php +++ b/src/Stream/TempFileStream.php @@ -35,14 +35,12 @@ final class TempFileStream extends Stream /** * Constructor of the class * - * @param string|null $prefix + * @param string $prefix * * @throws RuntimeException */ - public function __construct(?string $prefix = null) + public function __construct(string $prefix = '') { - $prefix ??= 'sunrisephp'; - $dirname = sys_get_temp_dir(); if (!is_writable($dirname)) { throw new RuntimeException('Temporary files directory is not writable'); diff --git a/src/UploadedFile.php b/src/UploadedFile.php index 259b9f1..0b065a9 100644 --- a/src/UploadedFile.php +++ b/src/UploadedFile.php @@ -129,11 +129,9 @@ public function __construct( $this->stream = $stream; } - $message = self::UPLOAD_ERRORS[$error] ?? 'Unknown error'; - $this->size = $size; $this->errorCode = $error; - $this->errorMessage = $message; + $this->errorMessage = self::UPLOAD_ERRORS[$error] ?? 'Unknown error'; $this->clientFilename = $clientFilename; $this->clientMediaType = $clientMediaType; } diff --git a/src/Uri/Component/Fragment.php b/src/Uri/Component/Fragment.php index cd87a20..886e30a 100644 --- a/src/Uri/Component/Fragment.php +++ b/src/Uri/Component/Fragment.php @@ -32,12 +32,12 @@ final class Fragment implements ComponentInterface { /** - * Regular expression to normalize the component value + * Regular expression used for the component normalization * * @var string */ // phpcs:ignore Generic.Files.LineLength - private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x3b\x3d\x3f-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; + private const NORMALIZATION_REGEX = '/(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x3b\x3d\x3f-\x5a\x5f\x61-\x7a\x7e])*|(.?)/u'; /** * The component value diff --git a/src/Uri/Component/Host.php b/src/Uri/Component/Host.php index e42ce24..4c3fb73 100644 --- a/src/Uri/Component/Host.php +++ b/src/Uri/Component/Host.php @@ -33,12 +33,12 @@ final class Host implements ComponentInterface { /** - * Regular expression to normalize the component value + * Regular expression used for the component normalization * * @var string */ // phpcs:ignore Generic.Files.LineLength - private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x2e\x30-\x39\x3b\x3d\x41-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; + private const NORMALIZATION_REGEX = '/(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x2e\x30-\x39\x3b\x3d\x41-\x5a\x5f\x61-\x7a\x7e]+)|(.?)/u'; /** * The component value diff --git a/src/Uri/Component/Password.php b/src/Uri/Component/Password.php index f5e9cd3..e1da2ea 100644 --- a/src/Uri/Component/Password.php +++ b/src/Uri/Component/Password.php @@ -32,12 +32,12 @@ final class Password implements ComponentInterface { /** - * Regular expression to normalize the component value + * Regular expression used for the component normalization * * @var string */ // phpcs:ignore Generic.Files.LineLength - private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x2e\x30-\x39\x3b\x3d\x41-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; + private const NORMALIZATION_REGEX = '/(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x2e\x30-\x39\x3b\x3d\x41-\x5a\x5f\x61-\x7a\x7e]+)|(.?)/u'; /** * The component value diff --git a/src/Uri/Component/Path.php b/src/Uri/Component/Path.php index acceac9..0660ea3 100644 --- a/src/Uri/Component/Path.php +++ b/src/Uri/Component/Path.php @@ -32,12 +32,12 @@ final class Path implements ComponentInterface { /** - * Regular expression to normalize the component value + * Regular expression used for the component normalization * * @var string */ // phpcs:ignore Generic.Files.LineLength - private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x3b\x3d\x40-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; + private const NORMALIZATION_REGEX = '/(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x3b\x3d\x40-\x5a\x5f\x61-\x7a\x7e]+)|(.?)/u'; /** * The component value diff --git a/src/Uri/Component/Query.php b/src/Uri/Component/Query.php index 43d5dad..1f33fdc 100644 --- a/src/Uri/Component/Query.php +++ b/src/Uri/Component/Query.php @@ -32,12 +32,12 @@ final class Query implements ComponentInterface { /** - * Regular expression to normalize the component value + * Regular expression used for the component normalization * * @var string */ // phpcs:ignore Generic.Files.LineLength - private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x3b\x3d\x3f-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; + private const NORMALIZATION_REGEX = '/(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x3b\x3d\x3f-\x5a\x5f\x61-\x7a\x7e]+)|(.?)/u'; /** * The component value diff --git a/src/Uri/Component/Scheme.php b/src/Uri/Component/Scheme.php index 5a75657..c594101 100644 --- a/src/Uri/Component/Scheme.php +++ b/src/Uri/Component/Scheme.php @@ -32,11 +32,11 @@ final class Scheme implements ComponentInterface { /** - * Regular expression to validate the component value + * Regular expression used for the component validation * * @var string */ - private const VALIDATION_REGEX = '/^(?:[A-Za-z][0-9A-Za-z\+\-\.]*)?$/'; + private const VALIDATION_REGEX = '/^(?:[A-Za-z][0-9A-Za-z\x2b\x2d\x2e]*)?$/'; /** * The component value diff --git a/src/Uri/Component/User.php b/src/Uri/Component/User.php index ffda1b9..9a5cb7a 100644 --- a/src/Uri/Component/User.php +++ b/src/Uri/Component/User.php @@ -32,12 +32,12 @@ final class User implements ComponentInterface { /** - * Regular expression to normalize the component value + * Regular expression used for the component normalization * * @var string */ // phpcs:ignore Generic.Files.LineLength - private const NORMALIZATION_REGEX = '/(?:(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x2e\x30-\x39\x3b\x3d\x41-\x5a\x5f\x61-\x7a\x7e]+)|(.?))/u'; + private const NORMALIZATION_REGEX = '/(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x2e\x30-\x39\x3b\x3d\x41-\x5a\x5f\x61-\x7a\x7e]+)|(.?)/u'; /** * The component value From 7dd4749f8af378a6865e36f57a3450f9e9e770bb Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 15 Mar 2023 01:41:35 +0100 Subject: [PATCH 37/49] Update README.md --- README.md | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/README.md b/README.md index 676318d..a3e5c06 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,6 @@ composer require sunrise/http-message ## Documentation navigation -- [Headers as objects](#headers-as-objects) -- - [Implemented headers](https://github.com/sunrise-php/http-message/blob/master/docs/headers.md) - [Server request from global environment](#server-request-from-global-environment) - [HTML and JSON responses](#html-and-json-responses) - - [HTML response](#html-response) @@ -36,46 +34,6 @@ composer require sunrise/http-message ⚠️ We highly recommend that you study [PSR-7](https://www.php-fig.org/psr/psr-7/) and [PSR-17](https://www.php-fig.org/psr/psr-17/) because only superficial examples will be presented below. -### Headers as objects - -If you want to use headers as objects, then follow the example below: - -```php -use Sunrise\Http\Message\HeaderInterface; - -final class SomeHeader implements HeaderInterface -{ - // some code... -} - -$message->withHeader(...new SomeHeader()); -``` - -... or you can extend your header from the base header which contains the necessary methods for validation and formatting: - -```php -use Sunrise\Http\Message\Header; - -final class SomeHeader extends Header -{ - // some code... -} - -$message->withHeader(...new SomeHeader()); -``` - -Below is an example of how you can set cookies using the already implemented [Set-Cookie](https://github.com/sunrise-php/http-message/blob/master/docs/headers.md#Set-Cookie) header: - -```php -use Sunrise\Http\Message\Header\SetCookieHeader; - -$cookie = new SetCookieHeader('sessionid', '38afes7a8'); - -$message->withAddedHeader(...$cookie); -``` - -You can see already implemented headers [here](https://github.com/sunrise-php/http-message/blob/master/docs/headers.md). - ### Server request from global environment ```php From e936f264dcf998a7b7ac7d6c6a4077128f4c2b7d Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Wed, 15 Mar 2023 02:04:04 +0100 Subject: [PATCH 38/49] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a3e5c06..529e0e8 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ use Sunrise\Http\Message\Response\JsonResponse; $response = new JsonResponse(200, $data); ``` -You can also specify [encoding flags](https://www.php.net/manual/en/json.constants.php#constant.json-hex-tag) and maximum nesting depth like bellow: +You can also specify [encoding flags](https://www.php.net/manual/en/json.constants.php#constant.json-hex-tag) and maximum nesting depth like below: ```php $response = new JsonResponse(200, $data, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE, 512); @@ -102,7 +102,7 @@ $memoryStream = new PhpMemoryStream('r+b'); #### PHP temporary stream -More details about the stream at [the official page](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory). +More details about the stream at the [official page](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory). ```php use Sunrise\Http\Message\Stream\PhpTempStream; @@ -174,7 +174,7 @@ Any exceptions of this package can be caught through the interface: use Sunrise\Http\Message\Exception\ExceptionInterface; try { - // some code with the package... + // some code of this package... } catch (ExceptionInterface $e) { // the package error... } From 7b1546d84134dd9291cb1a552f6e1310a16ff943 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Fri, 12 May 2023 03:54:54 +0200 Subject: [PATCH 39/49] Update composer.json --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 48ba469..8801647 100644 --- a/composer.json +++ b/composer.json @@ -7,12 +7,12 @@ "fenric", "sunrise", "http", + "header", "message", "request", "response", - "header", - "stream", "uri", + "stream", "upload", "rfc-7230", "psr-7", From 49f74f93b2b600d409ef538649f0bed6e97873d5 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Fri, 12 May 2023 03:55:11 +0200 Subject: [PATCH 40/49] Delete InvalidUriException.php --- src/Exception/InvalidUriException.php | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 src/Exception/InvalidUriException.php diff --git a/src/Exception/InvalidUriException.php b/src/Exception/InvalidUriException.php deleted file mode 100644 index 707d3db..0000000 --- a/src/Exception/InvalidUriException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * InvalidUriException - */ -class InvalidUriException extends InvalidArgumentException -{ -} From 955e18f0615fc54848ae4f343153349b82f7b88a Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Fri, 12 May 2023 03:55:13 +0200 Subject: [PATCH 41/49] Delete InvalidUriComponentException.php --- .../InvalidUriComponentException.php | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 src/Exception/InvalidUriComponentException.php diff --git a/src/Exception/InvalidUriComponentException.php b/src/Exception/InvalidUriComponentException.php deleted file mode 100644 index faa43a6..0000000 --- a/src/Exception/InvalidUriComponentException.php +++ /dev/null @@ -1,19 +0,0 @@ - - * @copyright Copyright (c) 2018, Anatoly Nekhay - * @license https://github.com/sunrise-php/http-message/blob/master/LICENSE - * @link https://github.com/sunrise-php/http-message - */ - -namespace Sunrise\Http\Message\Exception; - -/** - * InvalidUriComponentException - */ -class InvalidUriComponentException extends InvalidUriException -{ -} From 995ce2716a20b325c95bc2bb1f92f00fc039ea4c Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Fri, 12 May 2023 03:55:36 +0200 Subject: [PATCH 42/49] Improve tests --- tests/BaseServerRequestTest.php | 4 ++-- tests/StreamTest.php | 24 ++++++++++++------------ tests/UriTest.php | 28 +++++++++++++--------------- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/tests/BaseServerRequestTest.php b/tests/BaseServerRequestTest.php index b3304ef..8c87e76 100644 --- a/tests/BaseServerRequestTest.php +++ b/tests/BaseServerRequestTest.php @@ -144,7 +144,7 @@ public function testSetEmptyUploadedFiles(): void public function testSetInvalidUploadedFiles(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid uploaded files'); + $this->expectExceptionMessage('Invalid uploaded file'); $this->createSubject()->withUploadedFiles(['foo' => 'bar']); } @@ -293,7 +293,7 @@ public function testConstructorWithUploadedFiles(): void public function testConstructorWithInvalidUploadedFiles(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid uploaded files'); + $this->expectExceptionMessage('Invalid uploaded file'); $subject = $this->createSubjectWithUploadedFiles(['foo' => 'bar']); diff --git a/tests/StreamTest.php b/tests/StreamTest.php index 014d756..37fd891 100644 --- a/tests/StreamTest.php +++ b/tests/StreamTest.php @@ -149,7 +149,7 @@ public function testTellAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->tell(); } @@ -159,7 +159,7 @@ public function testTellAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->tell(); } @@ -210,7 +210,7 @@ public function testRewindAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->rewind(); } @@ -220,7 +220,7 @@ public function testRewindAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->rewind(); } @@ -247,7 +247,7 @@ public function testSeekAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->seek(0); } @@ -257,7 +257,7 @@ public function testSeekAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->seek(0); } @@ -315,7 +315,7 @@ public function testWriteAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->write('foo'); } @@ -325,7 +325,7 @@ public function testWriteAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->write('foo'); } @@ -375,7 +375,7 @@ public function testReadAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->read(1); } @@ -385,7 +385,7 @@ public function testReadAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->read(1); } @@ -412,7 +412,7 @@ public function testGetContentsAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->getContents(); } @@ -422,7 +422,7 @@ public function testGetContentsAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream without a resource so the operation is not possible'); + $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); $this->testStream->getContents(); } diff --git a/tests/UriTest.php b/tests/UriTest.php index a870730..9fe42ff 100644 --- a/tests/UriTest.php +++ b/tests/UriTest.php @@ -7,8 +7,6 @@ use PHPUnit\Framework\TestCase; use Psr\Http\Message\UriInterface; use Sunrise\Http\Message\Exception\InvalidArgumentException; -use Sunrise\Http\Message\Exception\InvalidUriComponentException; -use Sunrise\Http\Message\Exception\InvalidUriException; use Sunrise\Http\Message\Uri; use function strtolower; @@ -49,7 +47,7 @@ public function testConstructorWithEmptyUri(): void public function testConstructorWithInvalidUri(): void { - $this->expectException(InvalidUriException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Unable to parse URI'); new Uri(':'); @@ -272,7 +270,7 @@ public function testWithEmptyFragment(): void public function testWithInvalidScheme(): void { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid URI component "scheme"'); (new Uri(self::TEST_URI))->withScheme('scheme:'); @@ -297,7 +295,7 @@ public function testWithInvalidHost(): void public function testWithPortLessThanZero(): void { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid URI component "port"'); (new Uri(self::TEST_URI))->withPort(-1); @@ -305,7 +303,7 @@ public function testWithPortLessThanZero(): void public function testWithPortEqualsZero(): void { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid URI component "port"'); (new Uri(self::TEST_URI))->withPort(0); @@ -313,7 +311,7 @@ public function testWithPortEqualsZero(): void public function testWithPortGreaterThan65535(): void { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid URI component "port"'); (new Uri(self::TEST_URI))->withPort(2 ** 16); @@ -347,7 +345,7 @@ public function testWithInvalidFragment(): void */ public function testWithInvalidDataTypeForScheme($value) { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('URI component "scheme" must be a string'); (new Uri)->withScheme($value); @@ -358,7 +356,7 @@ public function testWithInvalidDataTypeForScheme($value) */ public function testWithInvalidDataTypeForUser($value) { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('URI component "user" must be a string'); (new Uri)->withUserInfo($value); @@ -369,7 +367,7 @@ public function testWithInvalidDataTypeForUser($value) */ public function testWithInvalidDataTypeForPass($value) { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('URI component "password" must be a string'); (new Uri)->withUserInfo('user', $value); @@ -380,7 +378,7 @@ public function testWithInvalidDataTypeForPass($value) */ public function testWithInvalidDataTypeForHost($value) { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('URI component "host" must be a string'); (new Uri)->withHost($value); @@ -391,7 +389,7 @@ public function testWithInvalidDataTypeForHost($value) */ public function testWithInvalidDataTypeForPort($value) { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('URI component "port" must be an integer'); (new Uri)->withPort($value); @@ -402,7 +400,7 @@ public function testWithInvalidDataTypeForPort($value) */ public function testWithInvalidDataTypeForPath($value) { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('URI component "path" must be a string'); (new Uri)->withPath($value); @@ -413,7 +411,7 @@ public function testWithInvalidDataTypeForPath($value) */ public function testWithInvalidDataTypeForQuery($value) { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('URI component "query" must be a string'); (new Uri)->withQuery($value); @@ -424,7 +422,7 @@ public function testWithInvalidDataTypeForQuery($value) */ public function testWithInvalidDataTypeForFragment($value) { - $this->expectException(InvalidUriComponentException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('URI component "fragment" must be a string'); (new Uri)->withFragment($value); From 73f07ed561e8140d13edca93a478e4d8c43e8db2 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Fri, 12 May 2023 03:57:08 +0200 Subject: [PATCH 43/49] Insignificant changes --- functions/server_request_files.php | 18 +++------------ functions/server_request_method.php | 7 +++++- src/Message.php | 11 +++++----- src/Request.php | 2 +- src/Response/JsonResponse.php | 8 +++---- src/ServerRequest.php | 24 ++++++++++---------- src/Stream.php | 12 +++++----- src/UploadedFile.php | 3 ++- src/Uri.php | 34 ++++++++++++++--------------- src/Uri/Component/Fragment.php | 8 +++---- src/Uri/Component/Host.php | 8 +++---- src/Uri/Component/Password.php | 10 ++++----- src/Uri/Component/Path.php | 8 +++---- src/Uri/Component/Port.php | 8 +++---- src/Uri/Component/Query.php | 8 +++---- src/Uri/Component/Scheme.php | 8 +++---- src/Uri/Component/User.php | 10 ++++----- src/Uri/Component/UserInfo.php | 8 +++++++ 18 files changed, 97 insertions(+), 98 deletions(-) diff --git a/functions/server_request_files.php b/functions/server_request_files.php index 3e9763d..64e345a 100644 --- a/functions/server_request_files.php +++ b/functions/server_request_files.php @@ -30,7 +30,7 @@ /** * Gets the request's uploaded files * - * Note that not sent files will not be handled. + * Please note that unsent files will not be handled. * * @param array|null $files * @@ -45,24 +45,12 @@ function server_request_files(?array $files = null): array { $files ??= $_FILES; - $walker = function ( - $path, - $size, - $error, - $name, - $type - ) use (&$walker) { + $walker = static function ($path, $size, $error, $name, $type) use (&$walker) { if (!is_array($path)) { // It makes no sense to create a stream if the file has not been successfully uploaded. $stream = UPLOAD_ERR_OK <> $error ? null : new FileStream($path, 'rb'); - return new UploadedFile( - $stream, - $size, - $error, - $name, - $type - ); + return new UploadedFile($stream, $size, $error, $name, $type); } $result = []; diff --git a/functions/server_request_method.php b/functions/server_request_method.php index 8ebcb21..34c544a 100644 --- a/functions/server_request_method.php +++ b/functions/server_request_method.php @@ -11,6 +11,11 @@ namespace Sunrise\Http\Message; +/** + * Import classes + */ +use Fig\Http\Message\RequestMethodInterface; + /** * Gets the request method * @@ -25,5 +30,5 @@ function server_request_method(?array $serverParams = null): string { $serverParams ??= $_SERVER; - return $serverParams['REQUEST_METHOD'] ?? Request::METHOD_GET; + return $serverParams['REQUEST_METHOD'] ?? RequestMethodInterface::METHOD_GET; } diff --git a/src/Message.php b/src/Message.php index fee01a1..3756fda 100644 --- a/src/Message.php +++ b/src/Message.php @@ -134,7 +134,7 @@ public function hasHeader($name): bool } /** - * Gets a header value from the message by the given name + * Gets a header value(s) from the message by the given name * * @param string $name * @@ -144,7 +144,7 @@ public function getHeader($name): array { $key = strtolower($name); - if (empty($this->headerNames[$key])) { + if (!isset($this->headerNames[$key])) { return []; } @@ -160,12 +160,13 @@ public function getHeader($name): array */ public function getHeaderLine($name): string { - $value = $this->getHeader($name); - if ([] === $value) { + $key = strtolower($name); + + if (!isset($this->headerNames[$key])) { return ''; } - return implode(',', $value); + return implode(',', $this->headers[$this->headerNames[$key]]); } /** diff --git a/src/Request.php b/src/Request.php index c86b5b5..345d1b9 100644 --- a/src/Request.php +++ b/src/Request.php @@ -41,7 +41,7 @@ class Request extends Message implements RequestInterface, RequestMethodInterfac * * @var string */ - public const RFC7230_REQUEST_TARGET_REGEX = '/^[\x21-\x7E\x80-\xFF]+$/'; + private const RFC7230_REQUEST_TARGET_REGEX = '/^[\x21-\x7E\x80-\xFF]+$/'; /** * The request method (aka verb) diff --git a/src/Response/JsonResponse.php b/src/Response/JsonResponse.php index fd5c227..95c01f1 100644 --- a/src/Response/JsonResponse.php +++ b/src/Response/JsonResponse.php @@ -42,7 +42,7 @@ final class JsonResponse extends Response * @param int $statusCode * @param mixed $data * @param int $flags - * @param int $depth + * @param int<1, max> $depth * * @throws InvalidArgumentException */ @@ -60,7 +60,7 @@ public function __construct(int $statusCode, $data, int $flags = 0, int $depth = * * @param mixed $data * @param int $flags - * @param int $depth + * @param int<1, max> $depth * * @return StreamInterface * @@ -72,10 +72,8 @@ private function createBody($data, int $flags, int $depth): StreamInterface return $data; } - $flags |= JSON_THROW_ON_ERROR; - try { - $payload = json_encode($data, $flags, $depth); + $payload = json_encode($data, $flags | JSON_THROW_ON_ERROR, $depth); } catch (JsonException $e) { throw new InvalidArgumentException(sprintf( 'Unable to create JSON response due to invalid JSON data: %s', diff --git a/src/ServerRequest.php b/src/ServerRequest.php index a7420f4..e4dd518 100644 --- a/src/ServerRequest.php +++ b/src/ServerRequest.php @@ -61,14 +61,14 @@ class ServerRequest extends Request implements ServerRequestInterface * * @var array */ - private array $uploadedFiles; + private array $uploadedFiles = []; /** * The request's parsed body * * @var array|object|null */ - private $parsedBody; + private $parsedBody = null; /** * The request attributes @@ -109,17 +109,23 @@ public function __construct( $parsedBody = null, array $attributes = [] ) { + parent::__construct($method, $uri, $headers, $body); + if (isset($protocolVersion)) { $this->setProtocolVersion($protocolVersion); } - parent::__construct($method, $uri, $headers, $body); + if (!empty($uploadedFiles)) { + $this->setUploadedFiles($uploadedFiles); + } + + if (isset($parsedBody)) { + $this->setParsedBody($parsedBody); + } $this->serverParams = $serverParams; $this->queryParams = $queryParams; $this->cookieParams = $cookieParams; - $this->setUploadedFiles($uploadedFiles); - $this->setParsedBody($parsedBody); $this->attributes = $attributes; } @@ -350,17 +356,11 @@ private function validateUploadedFiles(array $files): void } /** - * @param mixed $file - * - * @return void - * - * @throws InvalidArgumentException - * * @psalm-suppress MissingClosureParamType */ array_walk_recursive($files, static function ($file): void { if (! ($file instanceof UploadedFileInterface)) { - throw new InvalidArgumentException('Invalid uploaded files'); + throw new InvalidArgumentException('Invalid uploaded file'); } }); } diff --git a/src/Stream.php b/src/Stream.php index 56f7783..17bed7e 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -169,7 +169,7 @@ public function eof(): bool public function tell(): int { if (!is_resource($this->resource)) { - throw new RuntimeException('The stream without a resource so the operation is not possible'); + throw new RuntimeException('The stream does not have a resource, so the operation is not possible'); } $result = ftell($this->resource); @@ -224,7 +224,7 @@ public function rewind(): void public function seek($offset, $whence = SEEK_SET): void { if (!is_resource($this->resource)) { - throw new RuntimeException('The stream without a resource so the operation is not possible'); + throw new RuntimeException('The stream does not have a resource, so the operation is not possible'); } if (!$this->isSeekable()) { @@ -270,7 +270,7 @@ public function isWritable(): bool public function write($string): int { if (!is_resource($this->resource)) { - throw new RuntimeException('The stream without a resource so the operation is not possible'); + throw new RuntimeException('The stream does not have a resource, so the operation is not possible'); } if (!$this->isWritable()) { @@ -316,7 +316,7 @@ public function isReadable(): bool public function read($length): string { if (!is_resource($this->resource)) { - throw new RuntimeException('The stream without a resource so the operation is not possible'); + throw new RuntimeException('The stream does not have a resource, so the operation is not possible'); } if (!$this->isReadable()) { @@ -343,7 +343,7 @@ public function read($length): string public function getContents(): string { if (!is_resource($this->resource)) { - throw new RuntimeException('The stream without a resource so the operation is not possible'); + throw new RuntimeException('The stream does not have a resource, so the operation is not possible'); } if (!$this->isReadable()) { @@ -384,7 +384,7 @@ public function getMetadata($key = null) /** * Gets the stream size * - * Returns NULL if the stream without a resource, + * Returns NULL if the stream doesn't have a resource, * or if the stream size cannot be determined. * * @link http://php.net/manual/en/function.fstat.php diff --git a/src/UploadedFile.php b/src/UploadedFile.php index 0b065a9..cb641b2 100644 --- a/src/UploadedFile.php +++ b/src/UploadedFile.php @@ -54,7 +54,7 @@ class UploadedFile implements UploadedFileInterface * * @link https://www.php.net/manual/en/features.file-upload.errors.php * - * @var array + * @var array */ public const UPLOAD_ERRORS = [ UPLOAD_ERR_OK => 'No error', @@ -125,6 +125,7 @@ public function __construct( ?string $clientFilename = null, ?string $clientMediaType = null ) { + // It doesn't make sense to keep the stream if the file wasn't successfully uploaded... if (UPLOAD_ERR_OK === $error) { $this->stream = $stream; } diff --git a/src/Uri.php b/src/Uri.php index 95fa3eb..3dc6c40 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -16,8 +16,6 @@ */ use Psr\Http\Message\UriInterface; use Sunrise\Http\Message\Exception\InvalidArgumentException; -use Sunrise\Http\Message\Exception\InvalidUriComponentException; -use Sunrise\Http\Message\Exception\InvalidUriException; use Sunrise\Http\Message\Uri\Component\Fragment; use Sunrise\Http\Message\Uri\Component\Host; use Sunrise\Http\Message\Uri\Component\Path; @@ -97,7 +95,7 @@ class Uri implements UriInterface * * @param string $uri * - * @throws InvalidUriException + * @throws InvalidArgumentException * If the URI isn't valid. */ public function __construct(string $uri = '') @@ -108,7 +106,7 @@ public function __construct(string $uri = '') $components = parse_url($uri); if ($components === false) { - throw new InvalidUriException('Unable to parse URI'); + throw new InvalidArgumentException('Unable to parse URI'); } if (isset($components['scheme'])) { @@ -169,7 +167,7 @@ public static function create($uri): UriInterface /** * {@inheritdoc} * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the scheme isn't valid. */ public function withScheme($scheme): UriInterface @@ -183,7 +181,7 @@ public function withScheme($scheme): UriInterface /** * {@inheritdoc} * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the user information isn't valid. */ public function withUserInfo($user, $password = null): UriInterface @@ -197,7 +195,7 @@ public function withUserInfo($user, $password = null): UriInterface /** * {@inheritdoc} * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the host isn't valid. */ public function withHost($host): UriInterface @@ -211,7 +209,7 @@ public function withHost($host): UriInterface /** * {@inheritdoc} * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the port isn't valid. */ public function withPort($port): UriInterface @@ -225,7 +223,7 @@ public function withPort($port): UriInterface /** * {@inheritdoc} * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the path isn't valid. */ public function withPath($path): UriInterface @@ -239,7 +237,7 @@ public function withPath($path): UriInterface /** * {@inheritdoc} * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the query isn't valid. */ public function withQuery($query): UriInterface @@ -253,7 +251,7 @@ public function withQuery($query): UriInterface /** * {@inheritdoc} * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the fragment isn't valid. */ public function withFragment($fragment): UriInterface @@ -419,7 +417,7 @@ public function __toString(): string * * @return void * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the scheme isn't valid. */ final protected function setScheme($scheme): void @@ -435,7 +433,7 @@ final protected function setScheme($scheme): void * * @return void * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the user information isn't valid. */ final protected function setUserInfo($user, $password): void @@ -450,7 +448,7 @@ final protected function setUserInfo($user, $password): void * * @return void * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the host isn't valid. */ final protected function setHost($host): void @@ -465,7 +463,7 @@ final protected function setHost($host): void * * @return void * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the port isn't valid. */ final protected function setPort($port): void @@ -480,7 +478,7 @@ final protected function setPort($port): void * * @return void * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the path isn't valid. */ final protected function setPath($path): void @@ -495,7 +493,7 @@ final protected function setPath($path): void * * @return void * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the query isn't valid. */ final protected function setQuery($query): void @@ -510,7 +508,7 @@ final protected function setQuery($query): void * * @return void * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the fragment isn't valid. */ final protected function setFragment($fragment): void diff --git a/src/Uri/Component/Fragment.php b/src/Uri/Component/Fragment.php index 886e30a..104cfc2 100644 --- a/src/Uri/Component/Fragment.php +++ b/src/Uri/Component/Fragment.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidUriComponentException; +use Sunrise\Http\Message\Exception\InvalidArgumentException; /** * Import functions @@ -51,7 +51,7 @@ final class Fragment implements ComponentInterface * * @param mixed $value * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the component isn't valid. */ public function __construct($value) @@ -61,10 +61,10 @@ public function __construct($value) } if (!is_string($value)) { - throw new InvalidUriComponentException('URI component "fragment" must be a string'); + throw new InvalidArgumentException('URI component "fragment" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Host.php b/src/Uri/Component/Host.php index 4c3fb73..ea41f66 100644 --- a/src/Uri/Component/Host.php +++ b/src/Uri/Component/Host.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidUriComponentException; +use Sunrise\Http\Message\Exception\InvalidArgumentException; /** * Import functions @@ -52,7 +52,7 @@ final class Host implements ComponentInterface * * @param mixed $value * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the component isn't valid. */ public function __construct($value) @@ -62,10 +62,10 @@ public function __construct($value) } if (!is_string($value)) { - throw new InvalidUriComponentException('URI component "host" must be a string'); + throw new InvalidArgumentException('URI component "host" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Password.php b/src/Uri/Component/Password.php index e1da2ea..b40add0 100644 --- a/src/Uri/Component/Password.php +++ b/src/Uri/Component/Password.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidUriComponentException; +use Sunrise\Http\Message\Exception\InvalidArgumentException; /** * Import functions @@ -51,7 +51,7 @@ final class Password implements ComponentInterface * * @param mixed $value * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the component isn't valid. */ public function __construct($value) @@ -61,10 +61,10 @@ public function __construct($value) } if (!is_string($value)) { - throw new InvalidUriComponentException('URI component "password" must be a string'); + throw new InvalidArgumentException('URI component "password" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; @@ -78,7 +78,7 @@ public function __construct($value) * * @return Password * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException */ public static function create($password): Password { diff --git a/src/Uri/Component/Path.php b/src/Uri/Component/Path.php index 0660ea3..aecf0dd 100644 --- a/src/Uri/Component/Path.php +++ b/src/Uri/Component/Path.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidUriComponentException; +use Sunrise\Http\Message\Exception\InvalidArgumentException; /** * Import functions @@ -51,7 +51,7 @@ final class Path implements ComponentInterface * * @param mixed $value * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the component isn't valid. */ public function __construct($value) @@ -61,10 +61,10 @@ public function __construct($value) } if (!is_string($value)) { - throw new InvalidUriComponentException('URI component "path" must be a string'); + throw new InvalidArgumentException('URI component "path" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Port.php b/src/Uri/Component/Port.php index d46e981..ec96d9d 100644 --- a/src/Uri/Component/Port.php +++ b/src/Uri/Component/Port.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidUriComponentException; +use Sunrise\Http\Message\Exception\InvalidArgumentException; /** * Import functions @@ -41,7 +41,7 @@ final class Port implements ComponentInterface * * @param mixed $value * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the component isn't valid. */ public function __construct($value) @@ -54,11 +54,11 @@ public function __construct($value) } if (!is_int($value)) { - throw new InvalidUriComponentException('URI component "port" must be an integer'); + throw new InvalidArgumentException('URI component "port" must be an integer'); } if (!($value >= $min && $value <= $max)) { - throw new InvalidUriComponentException('Invalid URI component "port"'); + throw new InvalidArgumentException('Invalid URI component "port"'); } $this->value = $value; diff --git a/src/Uri/Component/Query.php b/src/Uri/Component/Query.php index 1f33fdc..d4f1642 100644 --- a/src/Uri/Component/Query.php +++ b/src/Uri/Component/Query.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidUriComponentException; +use Sunrise\Http\Message\Exception\InvalidArgumentException; /** * Import functions @@ -51,7 +51,7 @@ final class Query implements ComponentInterface * * @param mixed $value * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the component isn't valid. */ public function __construct($value) @@ -61,10 +61,10 @@ public function __construct($value) } if (!is_string($value)) { - throw new InvalidUriComponentException('URI component "query" must be a string'); + throw new InvalidArgumentException('URI component "query" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Scheme.php b/src/Uri/Component/Scheme.php index c594101..abe3aaf 100644 --- a/src/Uri/Component/Scheme.php +++ b/src/Uri/Component/Scheme.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidUriComponentException; +use Sunrise\Http\Message\Exception\InvalidArgumentException; /** * Import functions @@ -50,7 +50,7 @@ final class Scheme implements ComponentInterface * * @param mixed $value * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the component isn't valid. */ public function __construct($value) @@ -60,11 +60,11 @@ public function __construct($value) } if (!is_string($value)) { - throw new InvalidUriComponentException('URI component "scheme" must be a string'); + throw new InvalidArgumentException('URI component "scheme" must be a string'); } if (!preg_match(self::VALIDATION_REGEX, $value)) { - throw new InvalidUriComponentException('Invalid URI component "scheme"'); + throw new InvalidArgumentException('Invalid URI component "scheme"'); } // the component is case-insensitive... diff --git a/src/Uri/Component/User.php b/src/Uri/Component/User.php index 9a5cb7a..1645766 100644 --- a/src/Uri/Component/User.php +++ b/src/Uri/Component/User.php @@ -14,7 +14,7 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\InvalidUriComponentException; +use Sunrise\Http\Message\Exception\InvalidArgumentException; /** * Import functions @@ -51,7 +51,7 @@ final class User implements ComponentInterface * * @param mixed $value * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException * If the component isn't valid. */ public function __construct($value) @@ -61,10 +61,10 @@ public function __construct($value) } if (!is_string($value)) { - throw new InvalidUriComponentException('URI component "user" must be a string'); + throw new InvalidArgumentException('URI component "user" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; @@ -78,7 +78,7 @@ public function __construct($value) * * @return User * - * @throws InvalidUriComponentException + * @throws InvalidArgumentException */ public static function create($user): User { diff --git a/src/Uri/Component/UserInfo.php b/src/Uri/Component/UserInfo.php index 434fc94..0bc570a 100644 --- a/src/Uri/Component/UserInfo.php +++ b/src/Uri/Component/UserInfo.php @@ -11,6 +11,11 @@ namespace Sunrise\Http\Message\Uri\Component; +/** + * Import classes + */ +use Sunrise\Http\Message\Exception\InvalidArgumentException; + /** * URI component "User Information" * @@ -38,6 +43,9 @@ final class UserInfo implements ComponentInterface * * @param mixed $user * @param mixed $password + * + * @throws InvalidArgumentException + * If the user or password aren't valid. */ public function __construct($user, $password = null) { From c8f85ec166057dc5dad209676a9f2a508b33985b Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Fri, 12 May 2023 04:06:42 +0200 Subject: [PATCH 44/49] Insignificant changes --- src/Uri/Component/Fragment.php | 2 +- src/Uri/Component/Host.php | 2 +- src/Uri/Component/Password.php | 2 +- src/Uri/Component/Path.php | 2 +- src/Uri/Component/Query.php | 2 +- src/Uri/Component/User.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Uri/Component/Fragment.php b/src/Uri/Component/Fragment.php index 104cfc2..ca1be83 100644 --- a/src/Uri/Component/Fragment.php +++ b/src/Uri/Component/Fragment.php @@ -64,7 +64,7 @@ public function __construct($value) throw new InvalidArgumentException('URI component "fragment" must be a string'); } - $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Host.php b/src/Uri/Component/Host.php index ea41f66..01e0fb4 100644 --- a/src/Uri/Component/Host.php +++ b/src/Uri/Component/Host.php @@ -65,7 +65,7 @@ public function __construct($value) throw new InvalidArgumentException('URI component "host" must be a string'); } - $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Password.php b/src/Uri/Component/Password.php index b40add0..7a222cb 100644 --- a/src/Uri/Component/Password.php +++ b/src/Uri/Component/Password.php @@ -64,7 +64,7 @@ public function __construct($value) throw new InvalidArgumentException('URI component "password" must be a string'); } - $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Path.php b/src/Uri/Component/Path.php index aecf0dd..d9622b4 100644 --- a/src/Uri/Component/Path.php +++ b/src/Uri/Component/Path.php @@ -64,7 +64,7 @@ public function __construct($value) throw new InvalidArgumentException('URI component "path" must be a string'); } - $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/Query.php b/src/Uri/Component/Query.php index d4f1642..e1790b6 100644 --- a/src/Uri/Component/Query.php +++ b/src/Uri/Component/Query.php @@ -64,7 +64,7 @@ public function __construct($value) throw new InvalidArgumentException('URI component "query" must be a string'); } - $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; diff --git a/src/Uri/Component/User.php b/src/Uri/Component/User.php index 1645766..38c15e6 100644 --- a/src/Uri/Component/User.php +++ b/src/Uri/Component/User.php @@ -64,7 +64,7 @@ public function __construct($value) throw new InvalidArgumentException('URI component "user" must be a string'); } - $this->value = (string) preg_replace_callback(self::NORMALIZATION_REGEX, function (array $match): string { + $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { /** @var array{0: string, 1?: string} $match */ return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; From 134ecb9798284ed7b47dbd873be5430adcd9d931 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sat, 13 May 2023 02:43:25 +0200 Subject: [PATCH 45/49] Update composer.json --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8801647..6600e85 100644 --- a/composer.json +++ b/composer.json @@ -11,8 +11,8 @@ "message", "request", "response", - "uri", "stream", + "uri", "upload", "rfc-7230", "psr-7", @@ -61,6 +61,7 @@ "test": [ "phpcs", "psalm --no-cache", + "phpstan analyse src --level=9", "XDEBUG_MODE=coverage phpunit --coverage-text --colors=always" ], "build": [ From c3d22346c2144f64793222c5e2cab985e829c94a Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sat, 13 May 2023 02:43:44 +0200 Subject: [PATCH 46/49] Insignificant changes --- functions/server_request_files.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/functions/server_request_files.php b/functions/server_request_files.php index 64e345a..2f53581 100644 --- a/functions/server_request_files.php +++ b/functions/server_request_files.php @@ -30,7 +30,9 @@ /** * Gets the request's uploaded files * - * Please note that unsent files will not be handled. + * Please note that unsent files will not be handled, + * also note that if a file fails to upload successfully, + * a stream will not be created for it. * * @param array|null $files * @@ -47,7 +49,8 @@ function server_request_files(?array $files = null): array $walker = static function ($path, $size, $error, $name, $type) use (&$walker) { if (!is_array($path)) { - // It makes no sense to create a stream if the file has not been successfully uploaded. + // It makes no sense to create a stream + // if the file has not been successfully uploaded. $stream = UPLOAD_ERR_OK <> $error ? null : new FileStream($path, 'rb'); return new UploadedFile($stream, $size, $error, $name, $type); From 474704bfe93049b8bfb731fde002c053cb6df703 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sat, 13 May 2023 02:43:46 +0200 Subject: [PATCH 47/49] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 529e0e8..83d468e 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ composer require sunrise/http-message ## How to use -⚠️ We highly recommend that you study [PSR-7](https://www.php-fig.org/psr/psr-7/) and [PSR-17](https://www.php-fig.org/psr/psr-17/) because only superficial examples will be presented below. +We highly recommend that you study [PSR-7](https://www.php-fig.org/psr/psr-7/) and [PSR-17](https://www.php-fig.org/psr/psr-17/) because only superficial examples will be presented below. ### Server request from global environment @@ -174,9 +174,9 @@ Any exceptions of this package can be caught through the interface: use Sunrise\Http\Message\Exception\ExceptionInterface; try { - // some code of this package... + // some code... } catch (ExceptionInterface $e) { - // the package error... + // some logic... } ``` From 59a32668b778c1528482b233b695de2baa066a48 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sat, 13 May 2023 02:44:01 +0200 Subject: [PATCH 48/49] Insignificant changes --- src/Message.php | 12 ++++---- src/ServerRequest.php | 50 +++++++++++++++++----------------- src/ServerRequestFactory.php | 16 +++++++---- src/Stream.php | 14 ++++++---- src/Stream/FileStream.php | 13 +++------ src/Stream/PhpInputStream.php | 9 ++++-- src/UploadedFile.php | 27 +++++++++--------- src/Uri.php | 2 +- src/Uri/Component/Fragment.php | 13 +++++---- src/Uri/Component/Host.php | 13 +++++---- src/Uri/Component/Password.php | 13 +++++---- src/Uri/Component/Path.php | 13 +++++---- src/Uri/Component/Query.php | 13 +++++---- src/Uri/Component/User.php | 13 +++++---- 14 files changed, 122 insertions(+), 99 deletions(-) diff --git a/src/Message.php b/src/Message.php index 3756fda..71fb469 100644 --- a/src/Message.php +++ b/src/Message.php @@ -40,11 +40,11 @@ abstract class Message implements MessageInterface { /** - * Supported HTTP versions + * Allowed HTTP versions * * @var list */ - public const SUPPORTED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2']; + public const ALLOWED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2']; /** * Default HTTP version @@ -68,7 +68,7 @@ abstract class Message implements MessageInterface private array $headers = []; /** - * Original header names (see $headers) + * Original header names * * @var array */ @@ -357,8 +357,8 @@ final protected function setBody(StreamInterface $body): void */ private function validateProtocolVersion($protocolVersion): void { - if (!in_array($protocolVersion, self::SUPPORTED_HTTP_VERSIONS, true)) { - throw new InvalidArgumentException('Invalid or unsupported HTTP version'); + if (!in_array($protocolVersion, self::ALLOWED_HTTP_VERSIONS, true)) { + throw new InvalidArgumentException('Unallowed HTTP version'); } } @@ -391,7 +391,7 @@ private function validateHeaderName($name): void * Validates the given header value * * @param string $name - * @param array $value + * @param array $value * * @return void * diff --git a/src/ServerRequest.php b/src/ServerRequest.php index e4dd518..4685dd5 100644 --- a/src/ServerRequest.php +++ b/src/ServerRequest.php @@ -38,42 +38,42 @@ class ServerRequest extends Request implements ServerRequestInterface /** * The server parameters * - * @var array + * @var array */ private array $serverParams; /** * The request's query parameters * - * @var array + * @var array */ private array $queryParams; /** * The request's cookie parameters * - * @var array + * @var array */ private array $cookieParams; /** * The request's uploaded files * - * @var array + * @var array */ private array $uploadedFiles = []; /** * The request's parsed body * - * @var array|object|null + * @var array|object|null */ private $parsedBody = null; /** * The request attributes * - * @var array + * @var array */ private array $attributes; @@ -86,12 +86,12 @@ class ServerRequest extends Request implements ServerRequestInterface * @param array|null $headers * @param StreamInterface|null $body * - * @param array $serverParams - * @param array $queryParams - * @param array $cookieParams - * @param array $uploadedFiles - * @param array|object|null $parsedBody - * @param array $attributes + * @param array $serverParams + * @param array $queryParams + * @param array $cookieParams + * @param array $uploadedFiles + * @param array|object|null $parsedBody + * @param array $attributes * * @throws InvalidArgumentException * If one of the arguments isn't valid. @@ -132,7 +132,7 @@ public function __construct( /** * Gets the server parameters * - * @return array + * @return array */ public function getServerParams(): array { @@ -142,7 +142,7 @@ public function getServerParams(): array /** * Gets the request's query parameters * - * @return array + * @return array */ public function getQueryParams(): array { @@ -152,7 +152,7 @@ public function getQueryParams(): array /** * Creates a new instance of the request with the given query parameters * - * @param array $query + * @param array $query * * @return static */ @@ -167,7 +167,7 @@ public function withQueryParams(array $query): ServerRequestInterface /** * Gets the request's cookie parameters * - * @return array + * @return array */ public function getCookieParams(): array { @@ -177,7 +177,7 @@ public function getCookieParams(): array /** * Creates a new instance of the request with the given cookie parameters * - * @param array $cookies + * @param array $cookies * * @return static */ @@ -192,7 +192,7 @@ public function withCookieParams(array $cookies): ServerRequestInterface /** * Gets the request's uploaded files * - * @return array + * @return array */ public function getUploadedFiles(): array { @@ -202,7 +202,7 @@ public function getUploadedFiles(): array /** * Creates a new instance of the request with the given uploaded files * - * @param array $uploadedFiles + * @param array $uploadedFiles * * @return static * @@ -220,7 +220,7 @@ public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface /** * Gets the request's parsed body * - * @return array|object|null + * @return array|object|null */ public function getParsedBody() { @@ -230,7 +230,7 @@ public function getParsedBody() /** * Creates a new instance of the request with the given parsed body * - * @param array|object|null $data + * @param array|object|null $data * * @return static * @@ -248,7 +248,7 @@ public function withParsedBody($data): ServerRequestInterface /** * Gets the request attributes * - * @return array + * @return array */ public function getAttributes(): array { @@ -308,7 +308,7 @@ public function withoutAttribute($name): ServerRequestInterface /** * Sets the given uploaded files to the request * - * @param array $files + * @param array $files * * @return void * @@ -325,7 +325,7 @@ final protected function setUploadedFiles(array $files): void /** * Sets the given parsed body to the request * - * @param array|object|null $data + * @param array|object|null $data * * @return void * @@ -342,7 +342,7 @@ final protected function setParsedBody($data): void /** * Validates the given uploaded files * - * @param array $files + * @param array $files * * @return void * diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index 628e8b0..5b9ed0f 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -29,11 +29,11 @@ class ServerRequestFactory implements ServerRequestFactoryInterface /** * Creates a new request from superglobals variables * - * @param array|null $serverParams - * @param array|null $queryParams - * @param array|null $cookieParams - * @param array|null $uploadedFiles - * @param array|null $parsedBody + * @param array|null $serverParams + * @param array|null $queryParams + * @param array|null $cookieParams + * @param array|null $uploadedFiles + * @param array|null $parsedBody * * @return ServerRequestInterface * @@ -68,7 +68,11 @@ public static function fromGlobals( } /** - * {@inheritdoc} + * Creates a new request + * + * @param string $method + * @param mixed $uri + * @param array $serverParams */ public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface { diff --git a/src/Stream.php b/src/Stream.php index 17bed7e..7a52a40 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -59,7 +59,7 @@ class Stream implements StreamInterface * * @var bool */ - private $autoClose; + private bool $autoClose; /** * Constructor of the class @@ -169,7 +169,7 @@ public function eof(): bool public function tell(): int { if (!is_resource($this->resource)) { - throw new RuntimeException('The stream does not have a resource, so the operation is not possible'); + throw new RuntimeException('Stream has no resource'); } $result = ftell($this->resource); @@ -224,7 +224,7 @@ public function rewind(): void public function seek($offset, $whence = SEEK_SET): void { if (!is_resource($this->resource)) { - throw new RuntimeException('The stream does not have a resource, so the operation is not possible'); + throw new RuntimeException('Stream has no resource'); } if (!$this->isSeekable()) { @@ -270,7 +270,7 @@ public function isWritable(): bool public function write($string): int { if (!is_resource($this->resource)) { - throw new RuntimeException('The stream does not have a resource, so the operation is not possible'); + throw new RuntimeException('Stream has no resource'); } if (!$this->isWritable()) { @@ -308,6 +308,8 @@ public function isReadable(): bool * @link http://php.net/manual/en/function.fread.php * * @param int $length + * @psalm-param int $length + * @phpstan-param int<0, max> $length * * @return string * @@ -316,7 +318,7 @@ public function isReadable(): bool public function read($length): string { if (!is_resource($this->resource)) { - throw new RuntimeException('The stream does not have a resource, so the operation is not possible'); + throw new RuntimeException('Stream has no resource'); } if (!$this->isReadable()) { @@ -343,7 +345,7 @@ public function read($length): string public function getContents(): string { if (!is_resource($this->resource)) { - throw new RuntimeException('The stream does not have a resource, so the operation is not possible'); + throw new RuntimeException('Stream has no resource'); } if (!$this->isReadable()) { diff --git a/src/Stream/FileStream.php b/src/Stream/FileStream.php index 7bdc99a..c3ecac1 100644 --- a/src/Stream/FileStream.php +++ b/src/Stream/FileStream.php @@ -14,9 +14,8 @@ /** * Import classes */ -use Sunrise\Http\Message\Exception\RuntimeException; +use Sunrise\Http\Message\Exception\InvalidArgumentException; use Sunrise\Http\Message\Stream; -use Throwable; /** * Import functions @@ -37,18 +36,14 @@ final class FileStream extends Stream * @param string $filename * @param string $mode * - * @throws RuntimeException + * @throws InvalidArgumentException */ public function __construct(string $filename, string $mode) { - try { - $resource = fopen($filename, $mode); - } catch (Throwable $e) { - $resource = false; - } + $resource = @fopen($filename, $mode); if (!is_resource($resource)) { - throw new RuntimeException(sprintf( + throw new InvalidArgumentException(sprintf( 'Unable to open the file "%s" in the mode "%s"', $filename, $mode diff --git a/src/Stream/PhpInputStream.php b/src/Stream/PhpInputStream.php index dff405e..3f37f88 100644 --- a/src/Stream/PhpInputStream.php +++ b/src/Stream/PhpInputStream.php @@ -33,12 +33,15 @@ final class PhpInputStream extends Stream */ public function __construct() { + /** @var resource */ $input = fopen('php://input', 'rb'); - $resource = fopen('php://temp', 'r+b'); - stream_copy_to_stream($input, $resource); + /** @var resource */ + $handle = fopen('php://temp', 'r+b'); - parent::__construct($resource); + stream_copy_to_stream($input, $handle); + + parent::__construct($handle); $this->rewind(); } diff --git a/src/UploadedFile.php b/src/UploadedFile.php index cb641b2..7be2471 100644 --- a/src/UploadedFile.php +++ b/src/UploadedFile.php @@ -16,6 +16,7 @@ */ use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UploadedFileInterface; +use Sunrise\Http\Message\Exception\InvalidArgumentException; use Sunrise\Http\Message\Exception\RuntimeException; use Sunrise\Http\Message\Stream\FileStream; @@ -125,7 +126,8 @@ public function __construct( ?string $clientFilename = null, ?string $clientMediaType = null ) { - // It doesn't make sense to keep the stream if the file wasn't successfully uploaded... + // It doesn't make sense to keep the stream + // if the file wasn't successfully uploaded... if (UPLOAD_ERR_OK === $error) { $this->stream = $stream; } @@ -172,11 +174,13 @@ public function getStream(): StreamInterface * * @return void * + * @throws InvalidArgumentException + * If the target path cannot be used. + * * @throws RuntimeException * - If the file has no a stream due to an error; * - If the file was already moved; - * - If the file cannot be read; - * - If the target path cannot be used. + * - If the file cannot be read. */ public function moveTo($targetPath): void { @@ -200,24 +204,21 @@ public function moveTo($targetPath): void ); } - $targetDir = dirname($targetPath); - if (!is_dir($targetDir) || !is_writable($targetDir)) { - throw new RuntimeException(sprintf( - 'Uploaded file cannot be moved because the directory "%s" is not writable', - $targetDir + try { + $targetStream = new FileStream($targetPath, 'wb'); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf( + 'Uploaded file cannot be moved due to the error: %s', + $e->getMessage() )); } - $targetStream = new FileStream($targetPath, 'wb'); - if ($this->stream->isSeekable()) { $this->stream->rewind(); } while (!$this->stream->eof()) { - $targetStream->write( - $this->stream->read(4096) - ); + $targetStream->write($this->stream->read(4096)); } $targetStream->close(); diff --git a/src/Uri.php b/src/Uri.php index 3dc6c40..f488230 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -106,7 +106,7 @@ public function __construct(string $uri = '') $components = parse_url($uri); if ($components === false) { - throw new InvalidArgumentException('Unable to parse URI'); + throw new InvalidArgumentException('Invalid URI'); } if (isset($components['scheme'])) { diff --git a/src/Uri/Component/Fragment.php b/src/Uri/Component/Fragment.php index ca1be83..198d45b 100644 --- a/src/Uri/Component/Fragment.php +++ b/src/Uri/Component/Fragment.php @@ -64,11 +64,14 @@ public function __construct($value) throw new InvalidArgumentException('URI component "fragment" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { - /** @var array{0: string, 1?: string} $match */ - - return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; - }, $value); + $this->value = (string) preg_replace_callback( + self::NORMALIZATION_REGEX, + static function (array $match): string { + /** @var array{0: string, 1?: string} $match */ + return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; + }, + $value + ); } /** diff --git a/src/Uri/Component/Host.php b/src/Uri/Component/Host.php index 01e0fb4..fefc041 100644 --- a/src/Uri/Component/Host.php +++ b/src/Uri/Component/Host.php @@ -65,11 +65,14 @@ public function __construct($value) throw new InvalidArgumentException('URI component "host" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { - /** @var array{0: string, 1?: string} $match */ - - return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; - }, $value); + $this->value = (string) preg_replace_callback( + self::NORMALIZATION_REGEX, + static function (array $match): string { + /** @var array{0: string, 1?: string} $match */ + return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; + }, + $value + ); // the component is case-insensitive... $this->value = strtolower($this->value); diff --git a/src/Uri/Component/Password.php b/src/Uri/Component/Password.php index 7a222cb..c2461a0 100644 --- a/src/Uri/Component/Password.php +++ b/src/Uri/Component/Password.php @@ -64,11 +64,14 @@ public function __construct($value) throw new InvalidArgumentException('URI component "password" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { - /** @var array{0: string, 1?: string} $match */ - - return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; - }, $value); + $this->value = (string) preg_replace_callback( + self::NORMALIZATION_REGEX, + static function (array $match): string { + /** @var array{0: string, 1?: string} $match */ + return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; + }, + $value + ); } /** diff --git a/src/Uri/Component/Path.php b/src/Uri/Component/Path.php index d9622b4..dbcf91b 100644 --- a/src/Uri/Component/Path.php +++ b/src/Uri/Component/Path.php @@ -64,11 +64,14 @@ public function __construct($value) throw new InvalidArgumentException('URI component "path" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { - /** @var array{0: string, 1?: string} $match */ - - return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; - }, $value); + $this->value = (string) preg_replace_callback( + self::NORMALIZATION_REGEX, + static function (array $match): string { + /** @var array{0: string, 1?: string} $match */ + return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; + }, + $value + ); } /** diff --git a/src/Uri/Component/Query.php b/src/Uri/Component/Query.php index e1790b6..c7322a3 100644 --- a/src/Uri/Component/Query.php +++ b/src/Uri/Component/Query.php @@ -64,11 +64,14 @@ public function __construct($value) throw new InvalidArgumentException('URI component "query" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { - /** @var array{0: string, 1?: string} $match */ - - return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; - }, $value); + $this->value = (string) preg_replace_callback( + self::NORMALIZATION_REGEX, + static function (array $match): string { + /** @var array{0: string, 1?: string} $match */ + return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; + }, + $value + ); } /** diff --git a/src/Uri/Component/User.php b/src/Uri/Component/User.php index 38c15e6..8daff17 100644 --- a/src/Uri/Component/User.php +++ b/src/Uri/Component/User.php @@ -64,11 +64,14 @@ public function __construct($value) throw new InvalidArgumentException('URI component "user" must be a string'); } - $this->value = preg_replace_callback(self::NORMALIZATION_REGEX, static function (array $match): string { - /** @var array{0: string, 1?: string} $match */ - - return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; - }, $value); + $this->value = (string) preg_replace_callback( + self::NORMALIZATION_REGEX, + static function (array $match): string { + /** @var array{0: string, 1?: string} $match */ + return isset($match[1]) ? rawurlencode($match[1]) : $match[0]; + }, + $value + ); } /** From 6252e91831b94d3d9ca0cbf4ca48d8543d9a8423 Mon Sep 17 00:00:00 2001 From: Anatoly Nekhay Date: Sat, 13 May 2023 02:44:10 +0200 Subject: [PATCH 49/49] Improve tests --- tests/BaseMessageTest.php | 8 +++---- tests/BaseRequestTest.php | 2 +- .../UploadedFileIntegrationTest.php | 7 ++++++ tests/RequestFactoryTest.php | 2 +- tests/ServerRequestFactoryTest.php | 8 +++---- tests/StreamFactoryTest.php | 2 +- tests/StreamTest.php | 24 +++++++++---------- tests/UploadedFileTest.php | 7 +++--- tests/UriTest.php | 2 +- 9 files changed, 35 insertions(+), 27 deletions(-) diff --git a/tests/BaseMessageTest.php b/tests/BaseMessageTest.php index 2b75e1f..af6d133 100644 --- a/tests/BaseMessageTest.php +++ b/tests/BaseMessageTest.php @@ -70,7 +70,7 @@ public function testSetProtocolVersion($protocolVersion): void public function testSetProtocolVersionAsNull(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid or unsupported HTTP version'); + $this->expectExceptionMessage('Unallowed HTTP version'); $this->createSubject()->withProtocolVersion(null); } @@ -78,7 +78,7 @@ public function testSetProtocolVersionAsNull(): void public function testSetProtocolVersionAsNumber(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid or unsupported HTTP version'); + $this->expectExceptionMessage('Unallowed HTTP version'); $this->createSubject()->withProtocolVersion(1.1); } @@ -89,7 +89,7 @@ public function testSetProtocolVersionAsNumber(): void public function testSetInvalidProtocolVersion($protocolVersion): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid or unsupported HTTP version'); + $this->expectExceptionMessage('Unallowed HTTP version'); $this->createSubject()->withProtocolVersion($protocolVersion); } @@ -645,7 +645,7 @@ public function testConstructorWithProtocolVersion(string $protocolVersion): voi public function testConstructorWithInvalidProtocolVersion(string $protocolVersion): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid or unsupported HTTP version'); + $this->expectExceptionMessage('Unallowed HTTP version'); $subject = $this->createSubjectWithProtocolVersion($protocolVersion); diff --git a/tests/BaseRequestTest.php b/tests/BaseRequestTest.php index 326ab3a..8d9f6bf 100644 --- a/tests/BaseRequestTest.php +++ b/tests/BaseRequestTest.php @@ -398,7 +398,7 @@ public function testConstructorWithStringUri(): void public function testConstructorWithInvalidUri(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Unable to parse URI'); + $this->expectExceptionMessage('Invalid URI'); $subject = $this->createSubjectWithUri(':'); diff --git a/tests/Integration/UploadedFileIntegrationTest.php b/tests/Integration/UploadedFileIntegrationTest.php index 900b3ea..fe5a859 100644 --- a/tests/Integration/UploadedFileIntegrationTest.php +++ b/tests/Integration/UploadedFileIntegrationTest.php @@ -12,6 +12,13 @@ class UploadedFileIntegrationTest extends BaseUploadedFileIntegrationTest { + /** + * {@inheritdoc} + */ + protected $skippedTests = [ + 'testGetSize' => 'The test does not conform to the required behavior described in PSR-7', + ]; + /** * {@inheritdoc} */ diff --git a/tests/RequestFactoryTest.php b/tests/RequestFactoryTest.php index 8672209..c1cde0b 100644 --- a/tests/RequestFactoryTest.php +++ b/tests/RequestFactoryTest.php @@ -70,7 +70,7 @@ public function testCreateRequestWithStringUri(): void public function testCreateRequestWithInvalidUri(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Unable to parse URI'); + $this->expectExceptionMessage('Invalid URI'); (new RequestFactory)->createRequest('GET', ':'); } diff --git a/tests/ServerRequestFactoryTest.php b/tests/ServerRequestFactoryTest.php index c3f2b71..ad89982 100644 --- a/tests/ServerRequestFactoryTest.php +++ b/tests/ServerRequestFactoryTest.php @@ -74,7 +74,7 @@ public function testCreateServerRequestWithStringUri(): void public function testCreateServerRequestWithInvalidUri(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Unable to parse URI'); + $this->expectExceptionMessage('Invalid URI'); (new ServerRequestFactory)->createServerRequest('GET', ':'); } @@ -101,7 +101,7 @@ public function testCreateServerRequestWithServerParamsWithProtocolVersion( public function testCreateServerRequestWithServerParamsWithUnsupportedProtocolVersion(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid or unsupported HTTP version'); + $this->expectExceptionMessage('Unallowed HTTP version'); (new ServerRequestFactory)->createServerRequest('GET', new Uri(), ['SERVER_PROTOCOL' => 'HTTP/3']); } @@ -195,7 +195,7 @@ public function testCreateServerRequestFromGlobalsWithUnsupportedProtocolVersion $_SERVER = ['SERVER_PROTOCOL' => 'HTTP/3']; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Invalid or unsupported HTTP version'); + $this->expectExceptionMessage('Unallowed HTTP version'); ServerRequestFactory::fromGlobals(); } @@ -267,7 +267,7 @@ public function testCreateServerRequestFromGlobalsWithInvalidUri(): void $_SERVER = ['HTTP_HOST' => 'localhost:65536']; $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Unable to parse URI'); + $this->expectExceptionMessage('Invalid URI'); ServerRequestFactory::fromGlobals(); } diff --git a/tests/StreamFactoryTest.php b/tests/StreamFactoryTest.php index a9d4e24..78bda08 100644 --- a/tests/StreamFactoryTest.php +++ b/tests/StreamFactoryTest.php @@ -55,7 +55,7 @@ public function testCreateStreamFromFileWithMode(): void public function testCreateStreamFromInvalidFile(): void { - $this->expectException(RuntimeException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage( 'Unable to open the file "/55EF8096-7A6A-4C85-9BCD-6A5958376AB8" in the mode "r"' ); diff --git a/tests/StreamTest.php b/tests/StreamTest.php index 37fd891..f3f968b 100644 --- a/tests/StreamTest.php +++ b/tests/StreamTest.php @@ -149,7 +149,7 @@ public function testTellAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->tell(); } @@ -159,7 +159,7 @@ public function testTellAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->tell(); } @@ -210,7 +210,7 @@ public function testRewindAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->rewind(); } @@ -220,7 +220,7 @@ public function testRewindAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->rewind(); } @@ -247,7 +247,7 @@ public function testSeekAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->seek(0); } @@ -257,7 +257,7 @@ public function testSeekAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->seek(0); } @@ -315,7 +315,7 @@ public function testWriteAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->write('foo'); } @@ -325,7 +325,7 @@ public function testWriteAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->write('foo'); } @@ -375,7 +375,7 @@ public function testReadAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->read(1); } @@ -385,7 +385,7 @@ public function testReadAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->read(1); } @@ -412,7 +412,7 @@ public function testGetContentsAfterDetach(): void $this->testStream->detach(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->getContents(); } @@ -422,7 +422,7 @@ public function testGetContentsAfterClose(): void $this->testStream->close(); $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('The stream does not have a resource, so the operation is not possible'); + $this->expectExceptionMessage('Stream has no resource'); $this->testStream->getContents(); } diff --git a/tests/UploadedFileTest.php b/tests/UploadedFileTest.php index ba0c0f3..1a3adc4 100644 --- a/tests/UploadedFileTest.php +++ b/tests/UploadedFileTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\TestCase; use Psr\Http\Message\UploadedFileInterface; +use Sunrise\Http\Message\Exception\InvalidArgumentException; use Sunrise\Http\Message\Exception\RuntimeException; use Sunrise\Http\Message\Stream\FileStream; use Sunrise\Http\Message\Stream\PhpTempStream; @@ -159,10 +160,10 @@ public function testMoveUnwritableDirectory(): void { $file = new UploadedFile(new PhpTempStream()); - $this->expectException(RuntimeException::class); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage( - 'Uploaded file cannot be moved because ' . - 'the directory "/4c32dad5-181f-46b7-a86a-15568e11fdf9" is not writable' + 'Uploaded file cannot be moved due to the error: ' . + 'Unable to open the file "/4c32dad5-181f-46b7-a86a-15568e11fdf9/foo" in the mode "wb"' ); $file->moveTo('/4c32dad5-181f-46b7-a86a-15568e11fdf9/foo'); diff --git a/tests/UriTest.php b/tests/UriTest.php index 9fe42ff..7cf3461 100644 --- a/tests/UriTest.php +++ b/tests/UriTest.php @@ -48,7 +48,7 @@ public function testConstructorWithEmptyUri(): void public function testConstructorWithInvalidUri(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Unable to parse URI'); + $this->expectExceptionMessage('Invalid URI'); new Uri(':'); }