From dcfcfd5d6ca8d28a1584f8a533449634f9e9ab71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Porto=20Mari=C3=B1o?= Date: Tue, 13 Jan 2026 00:37:31 +0100 Subject: [PATCH 1/2] Fix code to run tests --- src/MessageTrait.php | 30 ++-- src/RelativeStream.php | 4 +- src/Request.php | 12 +- src/Request/ArraySerializer.php | 12 +- src/Request/Serializer.php | 10 +- src/RequestFactory.php | 4 +- src/RequestTrait.php | 36 ++-- src/Response.php | 20 +-- src/Response/ArraySerializer.php | 10 +- src/Response/Serializer.php | 10 +- src/ServerRequest.php | 10 +- src/ServerRequestFactory.php | 58 +++---- .../FilterUsingXForwardedHeaders.php | 27 +-- src/Stream.php | 35 ++-- src/UploadedFile.php | 49 +++--- src/Uri.php | 26 +-- src/UriFactory.php | 19 +- src/version.txt | 2 +- tests/MessageTraitTest.php | 162 +++++++++--------- tests/RelativeStreamTest.php | 13 +- tests/Request/SerializerTest.php | 77 ++++----- tests/RequestTest.php | 29 ++-- tests/Response/EmptyResponseTest.php | 16 +- tests/Response/HtmlResponseTest.php | 34 ++-- tests/Response/JsonResponseTest.php | 10 +- tests/Response/RedirectResponseTest.php | 13 +- tests/Response/SerializerTest.php | 13 +- tests/Response/TextResponseTest.php | 15 +- tests/Response/XmlResponseTest.php | 15 +- tests/ResponseTest.php | 61 ++++--- .../MarshalHeadersFromSapiTest.php | 0 .../NormalizeUploadedFilesTest.php | 0 tests/ServerRequestFactoryTest.php | 6 +- .../FilterUsingXForwardedHeadersTest.php | 37 ++-- tests/ServerRequestTest.php | 76 +++----- tests/StreamTest.php | 21 ++- 36 files changed, 482 insertions(+), 490 deletions(-) rename tests/{functions => ServerRequestFactory}/MarshalHeadersFromSapiTest.php (100%) rename tests/{functions => ServerRequestFactory}/NormalizeUploadedFilesTest.php (100%) diff --git a/src/MessageTrait.php b/src/MessageTrait.php index 5a9c7a9..7cd71ce 100644 --- a/src/MessageTrait.php +++ b/src/MessageTrait.php @@ -19,6 +19,7 @@ namespace Rodas\Diactoros; use InvalidArgumentException; +use Override; use Rodas\Psr\Http\Message\MessageInterface; use Rodas\Psr\Http\Message\StreamInterface; @@ -71,7 +72,6 @@ trait MessageTrait { set => $this->body = $value; } - /** * Return an instance with the specified HTTP protocol version. * @@ -85,8 +85,7 @@ trait MessageTrait { * @param string $version HTTP protocol version * @return static */ - public function withProtocolVersion(string $version): MessageInterface - { + public function withProtocolVersion(string $version): MessageInterface { $this->validateProtocolVersion($version); $new = clone $this; $new->protocolVersion = $version; @@ -152,17 +151,16 @@ public function getHeaderLine(string $name): string { public function withHeader(string $name, $value): MessageInterface { $this->assertHeader($name); - $normalized = strtolower($name); - - $new = clone $this; + $normalized = strtolower($name); + $new = clone $this; + $newHeaders = $new->headers; if ($new->hasHeader($name)) { - unset($new->headers[$new->headerNames[$normalized]]); + unset($newHeaders[$new->headerNames[$normalized]]); } - $value = $this->filterHeaderValue($value); - $new->headerNames[$normalized] = $name; - $new->headers[$name] = $value; + $newHeaders[$name] = $this->filterHeaderValue($value); + $new->headers = $newHeaders; return $new; } @@ -193,9 +191,11 @@ public function withAddedHeader(string $name, $value): MessageInterface { $header = $this->headerNames[strtolower($name)]; - $new = clone $this; - $value = $this->filterHeaderValue($value); - $new->headers[$header] = array_merge($this->headers[$header], $value); + $new = clone $this; + $value = $this->filterHeaderValue($value); + $newHeaders = $new->headers; + $newHeaders[$header] = array_merge($this->headers[$header], $value); + $new->headers = $newHeaders; return $new; } @@ -220,7 +220,9 @@ public function withoutHeader(string $name): MessageInterface { $original = $this->headerNames[$normalized]; $new = clone $this; - unset($new->headers[$original], $new->headerNames[$normalized]); + $newHeaders = $new->headers; + unset($newHeaders[$original], $new->headerNames[$normalized]); + $new->headers = $newHeaders; return $new; } diff --git a/src/RelativeStream.php b/src/RelativeStream.php index 4950875..5e1c065 100644 --- a/src/RelativeStream.php +++ b/src/RelativeStream.php @@ -42,7 +42,7 @@ public function __construct(private readonly StreamInterface $decoratedStream, ? */ #[Override] public function __toString(): string { - if ($this->isSeekable()) { + if ($this->isSeekable) { $this->seek(0); } return $this->getContents(); @@ -69,7 +69,7 @@ public function detach() { */ public ?int $size { get { - $size = $this->decoratedStream->getSize(); + $size = $this->decoratedStream->size; if ($size === null) { return null; } diff --git a/src/Request.php b/src/Request.php index 4d318ec..a0f8ae1 100644 --- a/src/Request.php +++ b/src/Request.php @@ -19,9 +19,8 @@ namespace Rodas\Diactoros; use InvalidArgumentException; -use Override; use Rodas\Psr\Http\Message\RequestInterface; -use Rodas\Psr\Http\Message\StatusCode; +use Rodas\Psr\Http\Message\RequestMethod; use Rodas\Psr\Http\Message\StreamInterface; use Rodas\Psr\Http\Message\UriInterface; @@ -44,7 +43,7 @@ class Request implements RequestInterface { * @param array $headers Headers for the message, if any. * @throws InvalidArgumentException For any invalid value. */ - public function __construct($uri = null, ?string $method = null, $body = 'php://temp', array $headers = []) { + public function __construct(UriInterface|string|null $uri = null, RequestMethod|string|null $method = null, $body = 'php://temp', array $headers = []) { $this->initialize($uri, $method, $body, $headers); } @@ -55,13 +54,16 @@ public function __construct($uri = null, ?string $method = null, $body = 'php:// */ public protected(set) array $headers = [] { get { - $headers = $this->headers; if (! $this->hasHeader('host') && $this->uri->host) { + + $headers = $this->headers; $headers['Host'] = [$this->getHostFromUri()]; + $this->headerNames['host'] = 'Host'; + $this->headers = $headers; } - return $headers; + return $this->headers; } set => $this->headers = $value; } diff --git a/src/Request/ArraySerializer.php b/src/Request/ArraySerializer.php index edaf7ea..9017577 100644 --- a/src/Request/ArraySerializer.php +++ b/src/Request/ArraySerializer.php @@ -48,12 +48,12 @@ final class ArraySerializer { */ public static function toArray(RequestInterface $request): array { return [ - 'method' => $request->getMethod(), - 'request_target' => $request->getRequestTarget(), - 'uri' => (string) $request->getUri(), - 'protocol_version' => $request->getProtocolVersion(), - 'headers' => $request->getHeaders(), - 'body' => (string) $request->getBody(), + 'method' => $request->method, + 'request_target' => $request->requestTarget, + 'uri' => (string) $request->uri, + 'protocol_version' => $request->protocolVersion, + 'headers' => $request->headers, + 'body' => (string) $request->body, ]; } diff --git a/src/Request/Serializer.php b/src/Request/Serializer.php index be972ce..66c8e66 100644 --- a/src/Request/Serializer.php +++ b/src/Request/Serializer.php @@ -79,9 +79,9 @@ public static function fromStream(StreamInterface $stream): Request { * Serialize a request message to a string. */ public static function toString(RequestInterface $request): string { - $httpMethod = $request->getMethod(); - $headers = self::serializeHeaders($request->getHeaders()); - $body = (string) $request->getBody(); + $httpMethod = $request->method; + $headers = self::serializeHeaders($request->headers); + $body = (string) $request->body; $format = '%s %s HTTP/%s%s%s'; if (! empty($headers)) { @@ -94,8 +94,8 @@ public static function toString(RequestInterface $request): string { return sprintf( $format, $httpMethod, - $request->getRequestTarget(), - $request->getProtocolVersion(), + $request->requestTarget, + $request->protocolVersion, $headers, $body ); diff --git a/src/RequestFactory.php b/src/RequestFactory.php index 482174f..cc0f141 100644 --- a/src/RequestFactory.php +++ b/src/RequestFactory.php @@ -20,14 +20,14 @@ use Override; use Rodas\Psr\Http\Message\RequestFactoryInterface; -use Rodas\Psr\Http\Message\RequestInterface; +use Rodas\Psr\Http\Message\UriInterface; class RequestFactory implements RequestFactoryInterface { /** * {@inheritDoc} */ #[Override] - public function createRequest(string $method, $uri): RequestInterface { + public function createRequest(string $method, string|UriInterface $uri): Request { return new Request($uri, $method); } } diff --git a/src/RequestTrait.php b/src/RequestTrait.php index 36883f9..6b9fd04 100644 --- a/src/RequestTrait.php +++ b/src/RequestTrait.php @@ -100,11 +100,6 @@ trait RequestTrait { set => $this->requestTarget = $value; } - public private(set) StreamInterface $stream { - get => $this->stream; - set => $this->stream = $value; - } - /** * Gets the URI instance. * @@ -131,7 +126,7 @@ trait RequestTrait { * @throws InvalidArgumentException For any invalid value. */ private function initialize( - $uri = null, + UriInterface|string|null $uri = null, RequestMethod|string|null $method = null, $body = 'php://memory', array $headers = [] @@ -140,16 +135,20 @@ private function initialize( $this->setMethod($method); } - $this->uri = $this->createUri($uri); - $this->stream = $this->getStream($body, 'wb+'); + $this->uri = $this->createUri($uri); + $this->body = $this->getStream($body, 'wb+'); $this->setHeaders($headers); // per PSR-7: attempt to set the Host header from a provided URI if no // Host header is provided - if (! $this->hasHeader('Host') && $this->uri->host) { - $this->headerNames['host'] = 'Host'; - $this->headers['Host'] = [$this->getHostFromUri()]; + if (! $this->hasHeader('Host') && + $this->uri->host) { + + $this->headerNames['host'] = 'Host'; + $headers = $this->headers; + $headers['Host'] = [$this->getHostFromUri()]; + $this->headers = $headers; } } @@ -167,7 +166,7 @@ private function initialize( * * @throws InvalidArgumentException */ - private function createUri(null|string|UriInterface $uri): UriInterface { + private function createUri(UriInterface|string|null $uri): UriInterface { if ($uri instanceof UriInterface) { return $uri; } @@ -279,14 +278,15 @@ public function withUri(UriInterface $uri, bool $preserveHost = false): RequestI // Remove an existing host header if present, regardless of current // de-normalization of the header name. // @see https://github.com/zendframework/zend-diactoros/issues/91 - foreach (array_keys($new->headers) as $header) { + $newHeaders = $new->headers; + foreach (array_keys($newHeaders) as $header) { if (strtolower($header) === 'host') { - unset($new->headers[$header]); + unset($newHeaders[$header]); } } - $new->headers['Host'] = [$host]; - + $newHeaders['Host'] = [$host]; + $new->headers = $newHeaders; return $new; } @@ -314,7 +314,9 @@ private function setMethod(RequestMethod|string $method): void { */ private function getHostFromUri(): string { $host = $this->uri->host; - $host .= $this->uri->port !== null ? ':' . $this->uri->port : ''; + $host .= $this->uri->port !== null + ? ':' . $this->uri->port + : ''; return $host; } } diff --git a/src/Response.php b/src/Response.php index a237ff5..ea62280 100644 --- a/src/Response.php +++ b/src/Response.php @@ -156,7 +156,7 @@ class Response implements ResponseInterface { set => $this->reasonPhrase = $value; } - public private(set) int $status { + public private(set) int $status = 200 { get => $this->status; set(int $value) { if ($value < static::MIN_STATUS_CODE_VALUE || @@ -170,6 +170,7 @@ class Response implements ResponseInterface { } else { $statusCode = StatusCode::tryFrom($value); if ($this->statusCode !== $statusCode) { + $this->statusCode = $statusCode; } } @@ -180,16 +181,15 @@ class Response implements ResponseInterface { /** * {@inheritdoc} */ - public private(set) ?StatusCode $statusCode { + public private(set) ?StatusCode $statusCode = StatusCode::OK { get => $this->statusCode; set(?StatusCode $value) { - if ($value === null) { - throw new InvalidArgumentException('Status code cannot be null'); - } - $this->statusCode = $value; - $code = $statusCode->value; - if ($this->status !== $code) { - $this->status = $code; + $this->statusCode = $value; + if ($value !== null) { + $code = $value->value; + if ($this->status !== $code) { + $this->status = $code; + } } } } @@ -202,7 +202,7 @@ class Response implements ResponseInterface { */ public function __construct($body = 'php://memory', int $status = 200, array $headers = []) { $this->setStatusCode($status); - $this->stream = $this->getStream($body, 'wb+'); + $this->body = $this->getStream($body, 'wb+'); $this->setHeaders($headers); } diff --git a/src/Response/ArraySerializer.php b/src/Response/ArraySerializer.php index 9b65a93..5e7a4fd 100644 --- a/src/Response/ArraySerializer.php +++ b/src/Response/ArraySerializer.php @@ -47,11 +47,11 @@ final class ArraySerializer { */ public static function toArray(ResponseInterface $response): array { return [ - 'status_code' => $response->getStatusCode(), - 'reason_phrase' => $response->getReasonPhrase(), - 'protocol_version' => $response->getProtocolVersion(), - 'headers' => $response->getHeaders(), - 'body' => (string) $response->getBody(), + 'status_code' => $response->status, + 'reason_phrase' => $response->reasonPhrase, + 'protocol_version' => $response->protocolVersion, + 'headers' => $response->headers, + 'body' => (string) $response->body, ]; } diff --git a/src/Response/Serializer.php b/src/Response/Serializer.php index 63854f7..9dad1c9 100644 --- a/src/Response/Serializer.php +++ b/src/Response/Serializer.php @@ -67,9 +67,9 @@ public static function fromStream(StreamInterface $stream): Response { * Create a string representation of a response. */ public static function toString(ResponseInterface $response): string { - $reasonPhrase = $response->getReasonPhrase(); - $headers = self::serializeHeaders($response->getHeaders()); - $body = (string) $response->getBody(); + $reasonPhrase = $response->reasonPhrase; + $headers = self::serializeHeaders($response->headers); + $body = (string) $response->body; $format = 'HTTP/%s %d%s%s%s'; if (! empty($headers)) { @@ -80,8 +80,8 @@ public static function toString(ResponseInterface $response): string { return sprintf( $format, - $response->getProtocolVersion(), - $response->getStatusCode(), + $response->protocolVersion, + $response->status, $reasonPhrase ? ' ' . $reasonPhrase : '', $headers, $body diff --git a/src/ServerRequest.php b/src/ServerRequest.php index 0b76b9b..1e42763 100644 --- a/src/ServerRequest.php +++ b/src/ServerRequest.php @@ -20,6 +20,7 @@ use InvalidArgumentException; use Override; +use Rodas\Psr\Http\Message\RequestMethod; use Rodas\Psr\Http\Message\ServerRequestInterface; use Rodas\Psr\Http\Message\StreamInterface; use Rodas\Psr\Http\Message\UploadedFileInterface; @@ -71,13 +72,15 @@ class ServerRequest implements ServerRequestInterface { */ public protected(set) array $headers = [] { get { - $headers = $this->headers; if (! $this->hasHeader('host') && $this->uri->host) { + $headers = $this->headers; $headers['Host'] = [$this->getHostFromUri()]; + $this->headerNames['host'] = 'Host'; + $this->headers = $headers; } - return $headers; + return $this->headers; } set => $this->headers = $value; } @@ -134,11 +137,12 @@ public function __construct( $body = new Stream($body, 'r'); } - $this->initialize($uri, $method, $body, $headers); $this->cookieParams = $cookieParams; + $this->queryParams = $queryParams; $this->uploadedFiles = $uploadedFiles; $this->serverParams = $serverParams; $this->protocolVersion = $protocol; + $this->initialize($uri, $method, $body, $headers); } /** diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index 4810e18..e40c6d7 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -25,6 +25,7 @@ use Rodas\Psr\Http\Message\RequestMethod; use Rodas\Psr\Http\Message\ServerRequestFactoryInterface; use Rodas\Psr\Http\Message\ServerRequestInterface; +use Rodas\Psr\Http\Message\UploadedFileInterface; use function array_filter; use function array_key_exists; @@ -61,7 +62,7 @@ class ServerRequestFactory implements ServerRequestFactoryInterface { * @throws InvalidArgumentException If one or more of the tmp_name, * size, or error keys are missing from $spec. */ - static function createUploadedFile(array $spec): UploadedFile { + public static function createUploadedFile(array $spec): UploadedFile { if (! isset($spec['tmp_name']) || ! isset($spec['size']) || ! isset($spec['error'])) { @@ -115,7 +116,9 @@ public static function fromGlobals( $server = static::normalizeServer( $server ?? $_SERVER, - is_callable(self::$apacheRequestHeaders) ? self::$apacheRequestHeaders : null + is_callable(self::$apacheRequestHeaders) + ? self::$apacheRequestHeaders + : null ); $files = static::normalizeUploadedFiles($files ?? $_FILES); $headers = static::marshalHeadersFromSapi($server); @@ -142,17 +145,17 @@ public static function fromGlobals( * @param array $server Values obtained from the SAPI (generally `$_SERVER`). * @return array Header/value pairs */ - static function marshalHeadersFromSapi(array $server): array { + public static function marshalHeadersFromSapi(array $server): array { $contentHeaderLookup = isset($server['LAMINAS_DIACTOROS_STRICT_CONTENT_HEADER_LOOKUP']) - ? static function (string $key): bool { - static $contentHeaders = [ - 'CONTENT_TYPE' => true, - 'CONTENT_LENGTH' => true, - 'CONTENT_MD5' => true, - ]; - return isset($contentHeaders[$key]); - } + ? static fn(string $key): bool => in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5']) : static fn(string $key): bool => str_starts_with($key, 'CONTENT_'); +// static $contentHeaders = [ +// 'CONTENT_TYPE' => true, +// 'CONTENT_LENGTH' => true, +// 'CONTENT_MD5' => true, +// ]; +// return isset($contentHeaders[$key]); +// } $headers = []; foreach ($server as $key => $value) { @@ -200,7 +203,7 @@ static function marshalHeadersFromSapi(array $server): array { /** * Retrieve the request method from the SAPI parameters. */ - static function marshalMethodFromSapi(array $server): string { + public static function marshalMethodFromSapi(array $server): string { return $server['REQUEST_METHOD'] ?? 'GET'; } @@ -210,7 +213,7 @@ static function marshalMethodFromSapi(array $server): string { * @throws Exception\UnrecognizedProtocolVersionException If the * $server['SERVER_PROTOCOL'] value is malformed. */ - static function marshalProtocolVersionFromSapi(array $server): string { + public static function marshalProtocolVersionFromSapi(array $server): string { if (! isset($server['SERVER_PROTOCOL'])) { return '1.1'; } @@ -236,7 +239,7 @@ static function marshalProtocolVersionFromSapi(array $server): string { * `apache_request_headers` under the Apache mod_php. * @return array Either $server verbatim, or with an added HTTP_AUTHORIZATION header. */ - static function normalizeServer(array $server, ?callable $apacheRequestHeaderCallback = null): array { + public static function normalizeServer(array $server, ?callable $apacheRequestHeaderCallback = null): array { if (null === $apacheRequestHeaderCallback && is_callable('apache_request_headers')) { @@ -245,22 +248,17 @@ static function normalizeServer(array $server, ?callable $apacheRequestHeaderCal // If the HTTP_AUTHORIZATION value is already set, or the callback is not // callable, we return verbatim - if (isset($server['HTTP_AUTHORIZATION']) || - ! is_callable($apacheRequestHeaderCallback)) { - - return $server; - } - - $apacheRequestHeaders = $apacheRequestHeaderCallback(); - if (isset($apacheRequestHeaders['Authorization'])) { - $server['HTTP_AUTHORIZATION'] = $apacheRequestHeaders['Authorization']; - return $server; + if (! isset($server['HTTP_AUTHORIZATION']) && + is_callable($apacheRequestHeaderCallback)) { + + $apacheRequestHeaders = $apacheRequestHeaderCallback(); + if (isset($apacheRequestHeaders['Authorization'])) { + $server['HTTP_AUTHORIZATION'] = $apacheRequestHeaders['Authorization']; + } elseif (isset($apacheRequestHeaders['authorization'])) { + $server['HTTP_AUTHORIZATION'] = $apacheRequestHeaders['authorization']; + } } - if (isset($apacheRequestHeaders['authorization'])) { - $server['HTTP_AUTHORIZATION'] = $apacheRequestHeaders['authorization']; - return $server; - } return $server; } @@ -274,7 +272,7 @@ static function normalizeServer(array $server, ?callable $apacheRequestHeaderCal * @return UploadedFileInterface[] * @throws InvalidArgumentException For unrecognized values. */ - static function normalizeUploadedFiles(array $files): array { + public static function normalizeUploadedFiles(array $files): array { /** * Traverse a nested tree of uploaded file specifications. * @@ -389,7 +387,7 @@ static function normalizeUploadedFiles(array $files): array { * @param string $cookieHeader A string cookie header value. * @return array key/value cookie pairs. */ - static function parseCookieHeader($cookieHeader): array { + public static function parseCookieHeader($cookieHeader): array { preg_match_all('( (?:^\\n?[ \t]*|;[ ]) (?P[!#$%&\'*+-.0-9A-Z^_`a-z|~]+) diff --git a/src/ServerRequestFilter/FilterUsingXForwardedHeaders.php b/src/ServerRequestFilter/FilterUsingXForwardedHeaders.php index a233511..60bbb2b 100644 --- a/src/ServerRequestFilter/FilterUsingXForwardedHeaders.php +++ b/src/ServerRequestFilter/FilterUsingXForwardedHeaders.php @@ -18,11 +18,12 @@ namespace Rodas\Diactoros\ServerRequestFilter; +use Override; use Rodas\Diactoros\Exception\InvalidForwardedHeaderNameException; use Rodas\Diactoros\Exception\InvalidProxyAddressException; use Rodas\Diactoros\UriFactory; -use Override; use Rodas\Psr\Http\Message\ServerRequestInterface; +use ValueError; use function array_values; use function assert; @@ -70,27 +71,25 @@ private function __construct( #[Override] public function __invoke(ServerRequestInterface $request): ServerRequestInterface { - $remoteAddress = $request->getServerParams()['REMOTE_ADDR'] ?? ''; + $remoteAddress = $request->serverParams['REMOTE_ADDR'] ?? ''; - if ('' === $remoteAddress || ! is_string($remoteAddress)) { - // Should we trigger a warning here? - return $request; - } + if ('' === $remoteAddress || + ! is_string($remoteAddress) || // Should we trigger a warning here? + ! $this->isFromTrustedProxy($remoteAddress)) { // Do nothing - if (! $this->isFromTrustedProxy($remoteAddress)) { - // Do nothing - return $request; + //return $request; } // Update the URI based on the trusted headers - $uri = $originalUri = $request->getUri(); + $uri = $originalUri = $request->uri; foreach ($this->trustedHeaders as $headerName) { $header = $request->getHeaderLine($headerName); - if ('' === $header || str_contains($header, ',')) { + fwrite(STDERR, $headerName . ': ' . $header . PHP_EOL); + if ('' === $header || + str_contains($header, ',')) { // Reject empty headers and/or headers with multiple values continue; } - switch ($headerName) { case self::HEADER_HOST: [$host, $port] = UriFactory::marshalHostAndPortFromHeader($header); @@ -104,7 +103,9 @@ public function __invoke(ServerRequestInterface $request): ServerRequestInterfac $uri = $uri->withPort((int) $header); break; case self::HEADER_PROTO: - $scheme = strtolower($header) === 'https' ? 'https' : 'http'; + $scheme = strtolower($header) === 'https' + ? 'https' + : 'http'; $uri = $uri->withScheme($scheme); break; } diff --git a/src/Stream.php b/src/Stream.php index e7d5fd4..2834f40 100644 --- a/src/Stream.php +++ b/src/Stream.php @@ -20,6 +20,10 @@ use InvalidArgumentException; use Override; +use Rodas\Diactoros\Exception\UnreadableStreamException; +use Rodas\Diactoros\Exception\UnseekableStreamException; +use Rodas\Diactoros\Exception\UntellableStreamException; +use Rodas\Diactoros\Exception\UnwritableStreamException; use Rodas\Psr\Http\Message\StreamInterface; use RuntimeException; use Stringable; @@ -201,12 +205,12 @@ public function attach($resource, string $mode = 'r'): void { #[Override] public function tell(): int { if (! $this->resource) { - throw Exception\UntellableStreamException::dueToMissingResource(); + throw UntellableStreamException::dueToMissingResource(); } $result = ftell($this->resource); if (! is_int($result)) { - throw Exception\UntellableStreamException::dueToPhpError(); + throw UntellableStreamException::dueToPhpError(); } return $result; @@ -230,17 +234,17 @@ public function eof(): bool { #[Override] public function seek(int $offset, int $whence = SEEK_SET): void { if (! $this->resource) { - throw Exception\UnseekableStreamException::dueToMissingResource(); + throw UnseekableStreamException::dueToMissingResource(); } if (! $this->isSeekable) { - throw Exception\UnseekableStreamException::dueToConfiguration(); + throw UnseekableStreamException::dueToConfiguration(); } $result = fseek($this->resource, $offset, $whence); if (0 !== $result) { - throw Exception\UnseekableStreamException::dueToPhpError(); + throw UnseekableStreamException::dueToPhpError(); } } @@ -257,17 +261,17 @@ public function rewind(): void { #[Override] public function write($string): int { if (! $this->resource) { - throw Exception\UnwritableStreamException::dueToMissingResource(); + throw UnwritableStreamException::dueToMissingResource(); } if (! $this->isWritable) { - throw Exception\UnwritableStreamException::dueToConfiguration(); + throw UnwritableStreamException::dueToConfiguration(); } $result = fwrite($this->resource, $string); if (false === $result) { - throw Exception\UnwritableStreamException::dueToPhpError(); + throw UnwritableStreamException::dueToPhpError(); } return $result; @@ -279,17 +283,17 @@ public function write($string): int { #[Override] public function read(int $length): string { if (! $this->resource) { - throw Exception\UnreadableStreamException::dueToMissingResource(); + throw UnreadableStreamException::dueToMissingResource(); } if (! $this->isReadable) { - throw Exception\UnreadableStreamException::dueToConfiguration(); + throw UnreadableStreamException::dueToConfiguration(); } $result = fread($this->resource, $length); if (false === $result) { - throw Exception\UnreadableStreamException::dueToPhpError(); + throw UnreadableStreamException::dueToPhpError(); } return $result; @@ -301,13 +305,13 @@ public function read(int $length): string { #[Override] public function getContents(): string { if (! $this->isReadable) { - throw Exception\UnreadableStreamException::dueToConfiguration(); + throw UnreadableStreamException::dueToConfiguration(); } assert($this->resource !== null, 'Always true condition for psalm type safety'); $result = stream_get_contents($this->resource); if (false === $result) { - throw Exception\UnreadableStreamException::dueToPhpError(); + throw UnreadableStreamException::dueToPhpError(); } return $result; } @@ -346,9 +350,8 @@ private function setStream($stream, string $mode = 'r'): void { if (is_string($stream)) { try { - $resource = fopen($stream, $mode); - } catch (Throwable $error) { - } + $resource = @fopen($stream, $mode); + } catch (Throwable $error) { } if (! is_resource($resource)) { throw new RuntimeException( diff --git a/src/UploadedFile.php b/src/UploadedFile.php index 64001a8..e37937f 100644 --- a/src/UploadedFile.php +++ b/src/UploadedFile.php @@ -146,14 +146,9 @@ public function __construct( if ($errorStatus === UPLOAD_ERR_OK) { if (is_string($streamOrFile)) { $this->file = $streamOrFile; - } - if (is_resource($streamOrFile)) { + } elseif (is_resource($streamOrFile)) { $this->stream = new Stream($streamOrFile); - } - - if ($this->file === null && - ! isset($this->stream)) { - + } else { if (! $streamOrFile instanceof StreamInterface) { throw new InvalidArgumentException('Invalid stream or file provided for UploadedFile'); } @@ -206,27 +201,25 @@ public function moveTo(string $targetPath): void } $sapi = PHP_SAPI; - switch (true) { - case empty($sapi) - || str_starts_with($sapi, 'cli') - || str_starts_with($sapi, 'phpdbg') - || $this->file === null: - // Non-SAPI environment, or no filename present - $this->writeFile($targetPath); - - if (isset($this->stream)) { - $this->stream->close(); - } - if (is_string($this->file) && file_exists($this->file)) { - unlink($this->file); - } - break; - default: - // SAPI environment, with file present - if (false === move_uploaded_file($this->file, $targetPath)) { - throw Exception\UploadedFileErrorException::forUnmovableFile(); - } - break; + if (empty($sapi) + || str_starts_with($sapi, 'cli') + || str_starts_with($sapi, 'phpdbg') + || $this->file === null) { + // Non-SAPI environment, or no filename present + + $this->writeFile($targetPath); + + if (isset($this->stream)) { + $this->stream->close(); + } + if (is_string($this->file) && file_exists($this->file)) { + unlink($this->file); + } + } else { + // SAPI environment, with file present + if (false === move_uploaded_file($this->file, $targetPath)) { + throw Exception\UploadedFileErrorException::forUnmovableFile(); + } } $this->moved = true; diff --git a/src/Uri.php b/src/Uri.php index acd6f21..65eab7f 100644 --- a/src/Uri.php +++ b/src/Uri.php @@ -103,11 +103,9 @@ class Uri implements UriInterface, Stringable { } public private(set) ?int $port = null { - get { - return $this->isNonStandardPort($this->scheme, $this->host, $this->port) - ? $this->port - : null; - } + get => $this->isNonStandardPort($this->scheme, $this->host, $this->port) + ? $this->port + : null; set => $this->port = $value; } @@ -207,7 +205,7 @@ public function __toString(): string { * {@inheritdoc} */ #[Override] - public function withScheme(string $scheme): UriInterface { + public function withScheme(string $scheme): static { $scheme = $this->filterScheme($scheme); if ($scheme === $this->scheme) { @@ -237,7 +235,7 @@ public function withUserInfo( string $user, #[SensitiveParameter] ?string $password = null - ): UriInterface { + ): static { $info = $this->filterUserInfoPart($user); if (null !== $password) { $info .= ':' . $this->filterUserInfoPart($password); @@ -260,7 +258,7 @@ public function withUserInfo( * {@inheritdoc} */ #[Override] - public function withHost(string $host): UriInterface { + public function withHost(string $host): static { if (strtolower($host) === $this->host) { // Do nothing if no change was made. return $this; @@ -276,13 +274,15 @@ public function withHost(string $host): UriInterface { * {@inheritdoc} */ #[Override] - public function withPort(?int $port): UriInterface { + public function withPort(?int $port): static { if ($port === $this->port) { // Do nothing if no change was made. return $this; } - if ($port !== null && ($port < 1 || $port > 65535)) { + if ($port !== null && + ($port < 1 || + $port > 65535)) { throw new InvalidArgumentException(sprintf( 'Invalid port "%d" specified; must be a valid TCP/UDP port', $port @@ -299,7 +299,7 @@ public function withPort(?int $port): UriInterface { * {@inheritdoc} */ #[Override] - public function withPath(string $path): UriInterface { + public function withPath(string $path): static { if (str_contains($path, '?')) { throw new InvalidArgumentException( 'Invalid path provided; must not contain a query string' @@ -329,7 +329,7 @@ public function withPath(string $path): UriInterface { * {@inheritdoc} */ #[Override] - public function withQuery(string $query): UriInterface { + public function withQuery(string $query): static { if (str_contains($query, '#')) { throw new InvalidArgumentException( 'Query string must not include a URI fragment' @@ -353,7 +353,7 @@ public function withQuery(string $query): UriInterface { * {@inheritdoc} */ #[Override] - public function withFragment(string $fragment): UriInterface { + public function withFragment(string $fragment): static { $fragment = $this->filterFragment($fragment); if ($fragment === $this->fragment) { diff --git a/src/UriFactory.php b/src/UriFactory.php index 5bf0315..6a5d45e 100644 --- a/src/UriFactory.php +++ b/src/UriFactory.php @@ -136,16 +136,16 @@ private static function marshalHostAndPort(array $server, array $headers): array static $defaults = ['', null]; $host = self::getHeaderFromArray('host', $headers, false); - if ($host !== false) { + if ($host !== false && + ! preg_match('/[\\t ,]/', $host)) { // Ignore obviously malformed host headers: // - Whitespace is invalid within a hostname and break the URI representation within HTTP. // non-printable characters other than SPACE and TAB are already rejected by HeaderSecurity. // - A comma indicates that multiple host headers have been sent which is not legal // and might be used in an attack where a load balancer sees a different host header // than Diactoros. - if (! preg_match('/[\\t ,]/', $host)) { - return self::marshalHostAndPortFromHeader($host); - } + + return self::marshalHostAndPortFromHeader($host); } if (! isset($server['SERVER_NAME'])) { @@ -153,12 +153,13 @@ private static function marshalHostAndPort(array $server, array $headers): array } $host = (string) $server['SERVER_NAME']; - $port = isset($server['SERVER_PORT']) ? (int) $server['SERVER_PORT'] : null; + $port = isset($server['SERVER_PORT']) + ? (int) $server['SERVER_PORT'] + : null; + + if (! isset($server['SERVER_ADDR']) || + ! preg_match('/^\[[0-9a-fA-F\:]+\]$/', $host)) { - if ( - ! isset($server['SERVER_ADDR']) - || ! preg_match('/^\[[0-9a-fA-F\:]+\]$/', $host) - ) { return [$host, $port]; } diff --git a/src/version.txt b/src/version.txt index eb4b0da..fc6b9fb 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1,2 +1,2 @@ -Commit: ec91d02 +Commit: 89ff700 Version: 0.1.0 diff --git a/tests/MessageTraitTest.php b/tests/MessageTraitTest.php index a969d9a..902d139 100644 --- a/tests/MessageTraitTest.php +++ b/tests/MessageTraitTest.php @@ -7,6 +7,7 @@ use InvalidArgumentException; use Rodas\Diactoros\Request; use Override; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; @@ -27,16 +28,16 @@ protected function setUp(): void $this->message = new Request(null, null, $this->createMock(StreamInterface::class)); } - public function testProtocolHasAcceptableDefault(): void - { - $this->assertSame('1.1', $this->message->getProtocolVersion()); + #[AllowMockObjectsWithoutExpectations] + public function testProtocolHasAcceptableDefault(): void { + $this->assertSame('1.1', $this->message->protocolVersion); } - public function testProtocolMutatorReturnsCloneWithChanges(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testProtocolMutatorReturnsCloneWithChanges(): void { $message = $this->message->withProtocolVersion('1.0'); $this->assertNotSame($this->message, $message); - $this->assertSame('1.0', $message->getProtocolVersion()); + $this->assertSame('1.0', $message->protocolVersion); } /** @return non-empty-array */ @@ -50,16 +51,15 @@ public static function invalidProtocolVersionProvider(): array } #[DataProvider('invalidProtocolVersionProvider')] - public function testWithProtocolVersionRaisesExceptionForInvalidVersion(string $version): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithProtocolVersionRaisesExceptionForInvalidVersion(string $version): void { $request = new Request(); $this->expectException(InvalidArgumentException::class); $request->withProtocolVersion($version); } /** @return non-empty-array */ - public static function validProtocolVersionProvider(): array - { + public static function validProtocolVersionProvider(): array { return [ '1.0' => ['1.0'], '1.1' => ['1.1'], @@ -69,71 +69,71 @@ public static function validProtocolVersionProvider(): array } #[DataProvider('validProtocolVersionProvider')] - public function testWithProtocolVersionDoesntRaiseExceptionForValidVersion(string $version): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithProtocolVersionDoesntRaiseExceptionForValidVersion(string $version): void { $request = (new Request())->withProtocolVersion($version); - $this->assertEquals($version, $request->getProtocolVersion()); + $this->assertEquals($version, $request->protocolVersion); } - public function testUsesStreamProvidedInConstructorAsBody(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testUsesStreamProvidedInConstructorAsBody(): void { $stream = $this->createMock(StreamInterface::class); $message = new Request(null, null, $stream); $this->assertSame($stream, $message->body); } - public function testBodyMutatorReturnsCloneWithChanges(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testBodyMutatorReturnsCloneWithChanges(): void { $stream = $this->createMock(StreamInterface::class); $message = $this->message->withBody($stream); $this->assertNotSame($this->message, $message); $this->assertSame($stream, $message->body); } - public function testGetHeaderReturnsHeaderValueAsArray(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testGetHeaderReturnsHeaderValueAsArray(): void { $message = $this->message->withHeader('X-Foo', ['Foo', 'Bar']); $this->assertNotSame($this->message, $message); $this->assertSame(['Foo', 'Bar'], $message->getHeader('X-Foo')); } - public function testGetHeaderLineReturnsHeaderValueAsCommaConcatenatedString(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testGetHeaderLineReturnsHeaderValueAsCommaConcatenatedString(): void { $message = $this->message->withHeader('X-Foo', ['Foo', 'Bar']); $this->assertNotSame($this->message, $message); $this->assertSame('Foo,Bar', $message->getHeaderLine('X-Foo')); } - public function testGetHeadersKeepsHeaderCaseSensitivity(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testGetHeadersKeepsHeaderCaseSensitivity(): void { $message = $this->message->withHeader('X-Foo', ['Foo', 'Bar']); $this->assertNotSame($this->message, $message); - $this->assertSame(['X-Foo' => ['Foo', 'Bar']], $message->getHeaders()); + $this->assertSame(['X-Foo' => ['Foo', 'Bar']], $message->headers); } - public function testGetHeadersReturnsCaseWithWhichHeaderFirstRegistered(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testGetHeadersReturnsCaseWithWhichHeaderFirstRegistered(): void { $message = $this->message ->withHeader('X-Foo', 'Foo') ->withAddedHeader('x-foo', 'Bar'); $this->assertNotSame($this->message, $message); - $this->assertSame(['X-Foo' => ['Foo', 'Bar']], $message->getHeaders()); + $this->assertSame(['X-Foo' => ['Foo', 'Bar']], $message->headers); } - public function testHasHeaderReturnsFalseIfHeaderIsNotPresent(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testHasHeaderReturnsFalseIfHeaderIsNotPresent(): void { $this->assertFalse($this->message->hasHeader('X-Foo')); } - public function testHasHeaderReturnsTrueIfHeaderIsPresent(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testHasHeaderReturnsTrueIfHeaderIsPresent(): void { $message = $this->message->withHeader('X-Foo', 'Foo'); $this->assertNotSame($this->message, $message); $this->assertTrue($message->hasHeader('X-Foo')); } - public function testAddHeaderAppendsToExistingHeader(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testAddHeaderAppendsToExistingHeader(): void { $message = $this->message->withHeader('X-Foo', 'Foo'); $this->assertNotSame($this->message, $message); $message2 = $message->withAddedHeader('X-Foo', 'Bar'); @@ -141,8 +141,8 @@ public function testAddHeaderAppendsToExistingHeader(): void $this->assertSame('Foo,Bar', $message2->getHeaderLine('X-Foo')); } - public function testCanRemoveHeaders(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testCanRemoveHeaders(): void { $message = $this->message->withHeader('X-Foo', 'Foo'); $this->assertNotSame($this->message, $message); $this->assertTrue($message->hasHeader('x-foo')); @@ -152,8 +152,8 @@ public function testCanRemoveHeaders(): void $this->assertFalse($message2->hasHeader('X-Foo')); } - public function testHeaderRemovalIsCaseInsensitive(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testHeaderRemovalIsCaseInsensitive(): void { $message = $this->message ->withHeader('X-Foo', 'Foo') ->withAddedHeader('x-foo', 'Bar') @@ -166,7 +166,7 @@ public function testHeaderRemovalIsCaseInsensitive(): void $this->assertNotSame($message, $message2); $this->assertFalse($message2->hasHeader('X-Foo')); - $headers = $message2->getHeaders(); + $headers = $message2->headers; $this->assertSame(0, count($headers)); } @@ -183,8 +183,8 @@ public static function invalidGeneralHeaderValues(): array } #[DataProvider('invalidGeneralHeaderValues')] - public function testWithHeaderRaisesExceptionForInvalidNestedHeaderValue(mixed $value): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithHeaderRaisesExceptionForInvalidNestedHeaderValue(mixed $value): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid header value'); @@ -193,8 +193,7 @@ public function testWithHeaderRaisesExceptionForInvalidNestedHeaderValue(mixed $ } /** @return non-empty-array */ - public static function invalidHeaderValues(): array - { + public static function invalidHeaderValues(): array { return [ 'null' => [null], 'true' => [true], @@ -204,8 +203,8 @@ public static function invalidHeaderValues(): array } #[DataProvider('invalidHeaderValues')] - public function testWithHeaderRaisesExceptionForInvalidValueType(mixed $value): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithHeaderRaisesExceptionForInvalidValueType(mixed $value): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Invalid header value'); @@ -213,45 +212,45 @@ public function testWithHeaderRaisesExceptionForInvalidValueType(mixed $value): $this->message->withHeader('X-Foo', $value); } - public function testWithHeaderReplacesDifferentCapitalization(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithHeaderReplacesDifferentCapitalization(): void { $this->message = $this->message->withHeader('X-Foo', ['foo']); $new = $this->message->withHeader('X-foo', ['bar']); $this->assertSame(['bar'], $new->getHeader('x-foo')); - $this->assertSame(['X-foo' => ['bar']], $new->getHeaders()); + $this->assertSame(['X-foo' => ['bar']], $new->headers); } #[DataProvider('invalidGeneralHeaderValues')] - public function testWithAddedHeaderRaisesExceptionForNonStringNonArrayValue(mixed $value): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithAddedHeaderRaisesExceptionForNonStringNonArrayValue(mixed $value): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('must be a string'); $this->message->withAddedHeader('X-Foo', $value); } - public function testWithoutHeaderDoesNothingIfHeaderDoesNotExist(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithoutHeaderDoesNothingIfHeaderDoesNotExist(): void { $this->assertFalse($this->message->hasHeader('X-Foo')); $message = $this->message->withoutHeader('X-Foo'); $this->assertNotSame($this->message, $message); $this->assertFalse($message->hasHeader('X-Foo')); } - public function testHeadersInitialization(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testHeadersInitialization(): void { $headers = ['X-Foo' => ['bar']]; $message = new Request(null, null, 'php://temp', $headers); - $this->assertSame($headers, $message->getHeaders()); + $this->assertSame($headers, $message->headers); } - public function testGetHeaderReturnsAnEmptyArrayWhenHeaderDoesNotExist(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testGetHeaderReturnsAnEmptyArrayWhenHeaderDoesNotExist(): void { $this->assertSame([], $this->message->getHeader('X-Foo-Bar')); } - public function testGetHeaderLineReturnsEmptyStringWhenHeaderDoesNotExist(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testGetHeaderLineReturnsEmptyStringWhenHeaderDoesNotExist(): void { $this->assertEmpty($this->message->getHeaderLine('X-Foo-Bar')); } @@ -284,8 +283,8 @@ public static function headersWithInjectionVectors(): array */ #[DataProvider('headersWithInjectionVectors')] #[Group('ZF2015-04')] - public function testDoesNotAllowCRLFInjectionWhenCallingWithHeader($name, $value): void - { + #[AllowMockObjectsWithoutExpectations] + public function testDoesNotAllowCRLFInjectionWhenCallingWithHeader($name, $value): void { $this->expectException(InvalidArgumentException::class); $this->message->withHeader($name, $value); @@ -297,21 +296,21 @@ public function testDoesNotAllowCRLFInjectionWhenCallingWithHeader($name, $value */ #[DataProvider('headersWithInjectionVectors')] #[Group('ZF2015-04')] - public function testDoesNotAllowCRLFInjectionWhenCallingWithAddedHeader($name, $value): void - { + #[AllowMockObjectsWithoutExpectations] + public function testDoesNotAllowCRLFInjectionWhenCallingWithAddedHeader($name, $value): void { $this->expectException(InvalidArgumentException::class); $this->message->withAddedHeader($name, $value); } - public function testWithHeaderAllowsHeaderContinuations(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithHeaderAllowsHeaderContinuations(): void { $message = $this->message->withHeader('X-Foo-Bar', "value,\r\n second value"); $this->assertSame("value, second value", $message->getHeaderLine('X-Foo-Bar')); } - public function testWithAddedHeaderAllowsHeaderContinuations(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithAddedHeaderAllowsHeaderContinuations(): void { $message = $this->message->withAddedHeader('X-Foo-Bar', "value,\r\n second value"); $this->assertSame("value, second value", $message->getHeaderLine('X-Foo-Bar')); } @@ -329,15 +328,14 @@ public static function headersWithWhitespace(): array } #[DataProvider('headersWithWhitespace')] - public function testWithHeaderTrimsWhitespace(string $value): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithHeaderTrimsWhitespace(string $value): void { $message = $this->message->withHeader('X-Foo-Bar', $value); $this->assertSame(trim($value, "\t "), $message->getHeaderLine('X-Foo-Bar')); } /** @return non-empty-array */ - public static function headersWithContinuation(): array - { + public static function headersWithContinuation(): array { return [ 'space' => ["foo\r\n bar"], 'tab' => ["foo\r\n\tbar"], @@ -345,8 +343,8 @@ public static function headersWithContinuation(): array } #[DataProvider('headersWithContinuation')] - public function testWithHeaderNormalizesContinuationToNotContainNewlines(string $value): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithHeaderNormalizesContinuationToNotContainNewlines(string $value): void { $message = $this->message->withHeader('X-Foo-Bar', $value); // Newlines must no longer appear. $this->assertStringNotContainsString("\r", $message->getHeaderLine('X-Foo-Bar')); @@ -356,8 +354,7 @@ public function testWithHeaderNormalizesContinuationToNotContainNewlines(string } /** @return non-empty-array */ - public static function numericHeaderValuesProvider(): array - { + public static function numericHeaderValuesProvider(): array { return [ 'integer' => [123], 'float' => [12.3], @@ -371,8 +368,8 @@ public static function numericHeaderValuesProvider(): array */ #[DataProvider('numericHeaderValuesProvider')] #[Group('99')] - public function testWithHeaderShouldAllowIntegersAndFloats(float $value): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithHeaderShouldAllowIntegersAndFloats(float $value): void { $message = $this->message ->withHeader('X-Test-Array', [$value]) ->withHeader('X-Test-Scalar', $value); @@ -380,12 +377,11 @@ public function testWithHeaderShouldAllowIntegersAndFloats(float $value): void $this->assertSame([ 'X-Test-Array' => [(string) $value], 'X-Test-Scalar' => [(string) $value], - ], $message->getHeaders()); + ], $message->headers); } /** @return non-empty-array */ - public static function invalidHeaderValueTypes(): array - { + public static function invalidHeaderValueTypes(): array { return [ 'null' => [null], 'true' => [true], @@ -404,8 +400,8 @@ public static function invalidArrayHeaderValues(): array #[DataProvider('invalidArrayHeaderValues')] #[Group('99')] - public function testWithHeaderShouldRaiseExceptionForInvalidHeaderValuesInArrays(mixed $value): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithHeaderShouldRaiseExceptionForInvalidHeaderValuesInArrays(mixed $value): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('header value type'); @@ -415,8 +411,8 @@ public function testWithHeaderShouldRaiseExceptionForInvalidHeaderValuesInArrays #[DataProvider('invalidHeaderValueTypes')] #[Group('99')] - public function testWithHeaderShouldRaiseExceptionForInvalidHeaderScalarValues(mixed $value): void - { + #[AllowMockObjectsWithoutExpectations] + public function testWithHeaderShouldRaiseExceptionForInvalidHeaderScalarValues(mixed $value): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('header value type'); diff --git a/tests/RelativeStreamTest.php b/tests/RelativeStreamTest.php index 1f1f408..ec10e05 100644 --- a/tests/RelativeStreamTest.php +++ b/tests/RelativeStreamTest.php @@ -7,6 +7,7 @@ use Rodas\Diactoros\RelativeStream; use Rodas\Diactoros\Stream; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\MockObject\Runtime\PropertyHook; use PHPUnit\Framework\TestCase; use RuntimeException; @@ -20,7 +21,7 @@ final class RelativeStreamTest extends TestCase public function testToString(): void { $decorated = $this->createMock(Stream::class); - $decorated->method('isSeekable')->willReturn(true); + $decorated->method(PropertyHook::get('isSeekable'))->willReturn(true); $decorated->method('tell')->willReturn(100); $decorated->expects(self::once())->method('seek')->with(100, SEEK_SET); $decorated->expects(self::once())->method('getContents')->willReturn('foobarbaz'); @@ -51,7 +52,7 @@ public function testDetach(): void public function testGetSize(): void { $decorated = $this->createMock(Stream::class); - $decorated->expects(self::once())->method('getSize')->willReturn(250); + $decorated->expects(self::once())->method(PropertyHook::get('size'))->willReturn(250); $stream = new RelativeStream($decorated, 100); $ret = $stream->size; $this->assertSame(150, $ret); @@ -69,7 +70,7 @@ public function testTell(): void public function testIsSeekable(): void { $decorated = $this->createMock(Stream::class); - $decorated->expects(self::once())->method('isSeekable')->willReturn(true); + $decorated->expects(self::once())->method(PropertyHook::get('isSeekable'))->willReturn(true); $stream = new RelativeStream($decorated, 100); $ret = $stream->isSeekable; $this->assertSame(true, $ret); @@ -77,7 +78,7 @@ public function testIsSeekable(): void public function testIsWritable(): void { $decorated = $this->createMock(Stream::class); - $decorated->expects(self::once())->method('isWritable')->willReturn(true); + $decorated->expects(self::once())->method(PropertyHook::get('isWritable'))->willReturn(true); $stream = new RelativeStream($decorated, 100); $ret = $stream->isWritable; $this->assertSame(true, $ret); @@ -86,7 +87,7 @@ public function testIsWritable(): void { public function testIsReadable(): void { $decorated = $this->createMock(Stream::class); - $decorated->expects(self::once())->method('isReadable')->willReturn(false); + $decorated->expects(self::once())->method(PropertyHook::get('isReadable'))->willReturn(false); $stream = new RelativeStream($decorated, 100); $ret = $stream->isReadable; $this->assertSame(false, $ret); @@ -189,7 +190,7 @@ public function testGetContentsRaisesExceptionWhenPointerIsBehindOffset(): void public function testCanReadContentFromNotSeekableResource(): void { $decorated = $this->createMock(Stream::class); - $decorated->method('isSeekable')->willReturn(false); + $decorated->method(PropertyHook::get('isSeekable'))->willReturn(false); $decorated->expects(self::never())->method('seek'); $decorated->method('tell')->willReturn(3); $decorated->method('getContents')->willReturn('CONTENTS'); diff --git a/tests/Request/SerializerTest.php b/tests/Request/SerializerTest.php index 8e067e2..691ae6e 100644 --- a/tests/Request/SerializerTest.php +++ b/tests/Request/SerializerTest.php @@ -11,6 +11,7 @@ use Rodas\Diactoros\Stream; use Rodas\Diactoros\Uri; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\MockObject\Runtime\PropertyHook; use PHPUnit\Framework\TestCase; use Rodas\Psr\Http\Message\RequestInterface; use Rodas\Psr\Http\Message\RequestMethod; @@ -76,12 +77,12 @@ public static function originForms(): array 'path-only' => [ 'GET /foo HTTP/1.1', '/foo', - ['getPath' => '/foo'], + ['path' => '/foo'], ], 'path-and-query' => [ 'GET /foo?bar HTTP/1.1', '/foo?bar', - ['getPath' => '/foo', 'getQuery' => 'bar'], + ['path' => '/foo', 'query' => 'bar'], ], ]; } @@ -105,8 +106,8 @@ public function testCanDeserializeRequestWithOriginForm( $this->assertSame($requestTarget, $request->requestTarget); $uri = $request->uri; - foreach ($expectations as $method => $expect) { - $this->assertSame($expect, $uri->{$method}()); + foreach ($expectations as $property => $expect) { + $this->assertSame($expect, $uri->{$property}); } } @@ -134,42 +135,42 @@ public static function absoluteForms(): array 'GET http://example.com/foo HTTP/1.1', 'http://example.com/foo', [ - 'getScheme' => 'http', - 'getHost' => 'example.com', - 'getPath' => '/foo', + 'scheme' => 'http', + 'host' => 'example.com', + 'path' => '/foo', ], ], 'path-and-query' => [ 'GET http://example.com/foo?bar HTTP/1.1', 'http://example.com/foo?bar', [ - 'getScheme' => 'http', - 'getHost' => 'example.com', - 'getPath' => '/foo', - 'getQuery' => 'bar', + 'scheme' => 'http', + 'host' => 'example.com', + 'path' => '/foo', + 'query' => 'bar', ], ], 'with-port' => [ 'GET http://example.com:8080/foo?bar HTTP/1.1', 'http://example.com:8080/foo?bar', [ - 'getScheme' => 'http', - 'getHost' => 'example.com', - 'getPort' => 8080, - 'getPath' => '/foo', - 'getQuery' => 'bar', + 'scheme' => 'http', + 'host' => 'example.com', + 'port' => 8080, + 'path' => '/foo', + 'query' => 'bar', ], ], 'with-authority' => [ 'GET https://me:too@example.com:8080/foo?bar HTTP/1.1', 'https://me:too@example.com:8080/foo?bar', [ - 'getScheme' => 'https', - 'getUserInfo' => 'me:too', - 'getHost' => 'example.com', - 'getPort' => 8080, - 'getPath' => '/foo', - 'getQuery' => 'bar', + 'scheme' => 'https', + 'userInfo' => 'me:too', + 'host' => 'example.com', + 'port' => 8080, + 'path' => '/foo', + 'query' => 'bar', ], ], ]; @@ -191,34 +192,32 @@ public function testCanDeserializeRequestWithAbsoluteForm( $message = $line . "\r\nX-Foo-Bar: Baz\r\n\r\nContent"; $request = Serializer::fromString($message); - $this->assertSame('GET', $request->getMethod()); + $this->assertSame('GET', $request->method); - $this->assertSame($requestTarget, $request->getRequestTarget()); + $this->assertSame($requestTarget, $request->requestTarget); $uri = $request->uri; - foreach ($expectations as $method => $expect) { - $this->assertSame($expect, $uri->{$method}()); + foreach ($expectations as $property => $expect) { + $this->assertSame($expect, $uri->{$property}); } } - public function testCanDeserializeRequestWithAuthorityForm(): void - { + public function testCanDeserializeRequestWithAuthorityForm(): void { $message = "CONNECT www.example.com:80 HTTP/1.1\r\nX-Foo-Bar: Baz"; $request = Serializer::fromString($message); - $this->assertSame('CONNECT', $request->getMethod()); - $this->assertSame('www.example.com:80', $request->getRequestTarget()); + $this->assertSame('CONNECT', $request->method); + $this->assertSame('www.example.com:80', $request->requestTarget); $uri = $request->uri; $this->assertNotSame('www.example.com', $uri->host); $this->assertNotSame(80, $uri->port); } - public function testCanDeserializeRequestWithAsteriskForm(): void - { + public function testCanDeserializeRequestWithAsteriskForm(): void { $message = "OPTIONS * HTTP/1.1\r\nHost: www.example.com"; $request = Serializer::fromString($message); - $this->assertSame('OPTIONS', $request->getMethod()); - $this->assertSame('*', $request->getRequestTarget()); + $this->assertSame('OPTIONS', $request->method); + $this->assertSame('*', $request->requestTarget); $uri = $request->uri; $this->assertNotSame('www.example.com', $uri->host); @@ -349,7 +348,7 @@ public function testFromStreamThrowsExceptionWhenStreamIsNotReadable(): void $stream = $this->createMock(StreamInterface::class); $stream ->expects($this->once()) - ->method('isReadable') + ->method(PropertyHook::get('isReadable')) ->willReturn(false); $this->expectException(InvalidArgumentException::class); @@ -362,11 +361,11 @@ public function testFromStreamThrowsExceptionWhenStreamIsNotSeekable(): void $stream = $this->createMock(StreamInterface::class); $stream ->expects($this->once()) - ->method('isReadable') + ->method(PropertyHook::get('isReadable')) ->willReturn(true); $stream ->expects($this->once()) - ->method('isSeekable') + ->method(PropertyHook::get('isSeekable')) ->willReturn(false); $this->expectException(InvalidArgumentException::class); @@ -382,11 +381,11 @@ public function testFromStreamStopsReadingAfterScanningHeader(): void $stream = $this->createMock(StreamInterface::class); $stream ->expects($this->once()) - ->method('isReadable') + ->method(PropertyHook::get('isReadable')) ->willReturn(true); $stream ->expects($this->once()) - ->method('isSeekable') + ->method(PropertyHook::get('isSeekable')) ->willReturn(true); // assert that full request body is not read, and returned as RelativeStream instead diff --git a/tests/RequestTest.php b/tests/RequestTest.php index c86a21e..9b6de12 100644 --- a/tests/RequestTest.php +++ b/tests/RequestTest.php @@ -98,7 +98,7 @@ public function testConstructorCanAcceptAllMessageParts(): void $this->assertSame('POST', $request->method); $this->assertSame(RequestMethod::POST, $request->requestMethod); $this->assertSame($body, $request->body); - $testHeaders = $request->getHeaders(); + $testHeaders = $request->headers; foreach ($headers as $key => $value) { $this->assertArrayHasKey($key, $testHeaders); $this->assertSame($value, $testHeaders[$key]); @@ -226,14 +226,14 @@ public function testConstructorRaisesExceptionForInvalidHeaders( public function testRequestTargetIsSlashWhenNoUriPresent(): void { $request = new Request(); - $this->assertSame('/', $request->getRequestTarget()); + $this->assertSame('/', $request->requestTarget); } public function testRequestTargetIsSlashWhenUriHasNoPathOrQuery(): void { $request = (new Request()) ->withUri(new Uri('http://example.com')); - $this->assertSame('/', $request->getRequestTarget()); + $this->assertSame('/', $request->requestTarget); } /** @return non-empty-array */ @@ -271,9 +271,8 @@ public static function requestsWithUri(): array * @param non-empty-string $expected */ #[DataProvider('requestsWithUri')] - public function testReturnsRequestTargetWhenUriIsPresent(RequestInterface $request, string $expected): void - { - $this->assertSame($expected, $request->getRequestTarget()); + public function testReturnsRequestTargetWhenUriIsPresent(RequestInterface $request, string $expected): void { + $this->assertSame($expected, $request->requestTarget); } /** @return non-empty-array */ @@ -296,7 +295,7 @@ public static function validRequestTargets(): array public function testCanProvideARequestTarget(string $requestTarget): void { $request = (new Request())->withRequestTarget($requestTarget); - $this->assertSame($requestTarget, $request->getRequestTarget()); + $this->assertSame($requestTarget, $request->requestTarget); } public function testRequestTargetCannotContainWhitespace(): void @@ -312,9 +311,9 @@ public function testRequestTargetCannotContainWhitespace(): void public function testRequestTargetDoesNotCacheBetweenInstances(): void { $request = (new Request())->withUri(new Uri('https://example.com/foo/bar')); - $original = $request->getRequestTarget(); + $original = $request->requestTarget; $newRequest = $request->withUri(new Uri('http://mwop.net/bar/baz')); - $this->assertNotSame($original, $newRequest->getRequestTarget()); + $this->assertNotSame($original, $newRequest->requestTarget); } public function testSettingNewUriResetsRequestTarget(): void @@ -322,14 +321,14 @@ public function testSettingNewUriResetsRequestTarget(): void $request = (new Request())->withUri(new Uri('https://example.com/foo/bar')); $newRequest = $request->withUri(new Uri('http://mwop.net/bar/baz')); - $this->assertNotSame($request->getRequestTarget(), $newRequest->getRequestTarget()); + $this->assertNotSame($request->requestTarget, $newRequest->requestTarget); } #[Group('39')] public function testGetHeadersContainsHostHeaderIfUriWithHostIsPresent(): void { $request = new Request('http://example.com'); - $headers = $request->getHeaders(); + $headers = $request->headers; $this->assertArrayHasKey('Host', $headers); $this->assertStringContainsString('example.com', $headers['Host'][0]); } @@ -338,7 +337,7 @@ public function testGetHeadersContainsHostHeaderIfUriWithHostIsPresent(): void public function testGetHeadersContainsHostHeaderIfUriWithHostIsDeleted(): void { $request = (new Request('http://example.com'))->withoutHeader('host'); - $headers = $request->getHeaders(); + $headers = $request->headers; $this->assertArrayHasKey('Host', $headers); $this->assertContains('example.com', $headers['Host']); } @@ -347,7 +346,7 @@ public function testGetHeadersContainsHostHeaderIfUriWithHostIsDeleted(): void public function testGetHeadersContainsNoHostHeaderIfNoUriPresent(): void { $request = new Request(); - $headers = $request->getHeaders(); + $headers = $request->headers; $this->assertArrayNotHasKey('Host', $headers); } @@ -355,7 +354,7 @@ public function testGetHeadersContainsNoHostHeaderIfNoUriPresent(): void public function testGetHeadersContainsNoHostHeaderIfUriDoesNotContainHost(): void { $request = new Request(new Uri()); - $headers = $request->getHeaders(); + $headers = $request->headers; $this->assertArrayNotHasKey('Host', $headers); } @@ -526,7 +525,7 @@ public function testWithUriAndNoPreserveHostWillOverwriteHostHeaderRegardlessOfO $new = $request->withUri($uri); $host = $new->getHeaderLine('host'); $this->assertSame('example.org', $host); - $headers = $new->getHeaders(); + $headers = $new->headers; $this->assertArrayHasKey('Host', $headers); if ($hostKey !== 'Host') { $this->assertArrayNotHasKey($hostKey, $headers); diff --git a/tests/Response/EmptyResponseTest.php b/tests/Response/EmptyResponseTest.php index bb14136..e386479 100644 --- a/tests/Response/EmptyResponseTest.php +++ b/tests/Response/EmptyResponseTest.php @@ -6,24 +6,24 @@ use Rodas\Diactoros\Response; use Rodas\Diactoros\Response\EmptyResponse; +use Rodas\Psr\Http\Message\StatusCode; use PHPUnit\Framework\TestCase; -final class EmptyResponseTest extends TestCase -{ - public function testConstructor(): void - { +final class EmptyResponseTest extends TestCase { + public function testConstructor(): void { $response = new EmptyResponse(201); $this->assertInstanceOf(Response::class, $response); $this->assertSame('', (string) $response->body); - $this->assertSame(201, $response->getStatusCode()); + $this->assertSame(201, $response->status); + $this->assertSame(StatusCode::CREATED, $response->statusCode); } - public function testHeaderConstructor(): void - { + public function testHeaderConstructor(): void { $response = EmptyResponse::withHeaders(['x-empty' => ['true']]); $this->assertInstanceOf(Response::class, $response); $this->assertSame('', (string) $response->body); - $this->assertSame(204, $response->getStatusCode()); + $this->assertSame(204, $response->status); + $this->assertSame(StatusCode::NO_CONTENT, $response->statusCode); $this->assertSame('true', $response->getHeaderLine('x-empty')); } } diff --git a/tests/Response/HtmlResponseTest.php b/tests/Response/HtmlResponseTest.php index 47ca8fd..f183cad 100644 --- a/tests/Response/HtmlResponseTest.php +++ b/tests/Response/HtmlResponseTest.php @@ -9,30 +9,29 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Rodas\Psr\Http\Message\StreamInterface; +use Rodas\Psr\Http\Message\StatusCode; -final class HtmlResponseTest extends TestCase -{ - public function testConstructorAcceptsHtmlString(): void - { +final class HtmlResponseTest extends TestCase { + public function testConstructorAcceptsHtmlString(): void { $body = 'Uh oh not found'; $response = new HtmlResponse($body); $this->assertSame($body, (string) $response->body); - $this->assertSame(200, $response->getStatusCode()); + $this->assertSame(200, $response->status); + $this->assertSame(StatusCode::OK, $response->statusCode); } - public function testConstructorAllowsPassingStatus(): void - { + public function testConstructorAllowsPassingStatus(): void { $body = 'Uh oh not found'; $status = 404; $response = new HtmlResponse($body, $status); - $this->assertSame(404, $response->getStatusCode()); + $this->assertSame(404, $response->status); + $this->assertSame(StatusCode::NOT_FOUND, $response->statusCode); $this->assertSame($body, (string) $response->body); } - public function testConstructorAllowsPassingHeaders(): void - { + public function testConstructorAllowsPassingHeaders(): void { $body = 'Uh oh not found'; $status = 404; $headers = [ @@ -42,20 +41,19 @@ public function testConstructorAllowsPassingHeaders(): void $response = new HtmlResponse($body, $status, $headers); $this->assertSame(['foo-bar'], $response->getHeader('x-custom')); $this->assertSame('text/html; charset=utf-8', $response->getHeaderLine('content-type')); - $this->assertSame(404, $response->getStatusCode()); + $this->assertSame(404, $response->status); + $this->assertSame(StatusCode::NOT_FOUND, $response->statusCode); $this->assertSame($body, (string) $response->body); } - public function testAllowsStreamsForResponseBody(): void - { + public function testAllowsStreamsForResponseBody(): void { $body = $this->createStub(StreamInterface::class); $response = new HtmlResponse($body); $this->assertSame($body, $response->body); } /** @return array */ - public static function invalidHtmlContent(): array - { + public static function invalidHtmlContent(): array { return [ 'null' => [null], 'true' => [true], @@ -70,16 +68,14 @@ public static function invalidHtmlContent(): array } #[DataProvider('invalidHtmlContent')] - public function testRaisesExceptionForNonStringNonStreamBodyContent(mixed $body): void - { + public function testRaisesExceptionForNonStringNonStreamBodyContent(mixed $body): void { $this->expectException(InvalidArgumentException::class); /** @psalm-suppress MixedArgument */ new HtmlResponse($body); } - public function testConstructorRewindsBodyStream(): void - { + public function testConstructorRewindsBodyStream(): void { $html = '

test data

'; $response = new HtmlResponse($html); diff --git a/tests/Response/JsonResponseTest.php b/tests/Response/JsonResponseTest.php index b0efaba..e67dddd 100644 --- a/tests/Response/JsonResponseTest.php +++ b/tests/Response/JsonResponseTest.php @@ -6,6 +6,7 @@ use InvalidArgumentException; use Rodas\Diactoros\Response\JsonResponse; +use Rodas\Psr\Http\Message\StatusCode; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use stdClass; @@ -35,7 +36,8 @@ public function testConstructorAcceptsDataAndCreatesJsonEncodedMessageBody(): vo $json = '{"nested":{"json":["tree"]}}'; $response = new JsonResponse($data); - $this->assertSame(200, $response->getStatusCode()); + $this->assertSame(200, $response->status); + $this->assertSame(StatusCode::OK, $response->statusCode); $this->assertSame('application/json', $response->getHeaderLine('content-type')); $this->assertSame($json, (string) $response->body); } @@ -58,7 +60,8 @@ public static function scalarValuesForJSON() { #[DataProvider('scalarValuesForJSON')] public function testScalarValuePassedToConstructorJsonEncodesDirectly(mixed $value): void { $response = new JsonResponse($value); - $this->assertSame(200, $response->getStatusCode()); + $this->assertSame(200, $response->status); + $this->assertSame(StatusCode::OK, $response->statusCode); $this->assertSame('application/json', $response->getHeaderLine('content-type')); // 15 is the default mask used by JsonResponse $this->assertSame(json_encode($value, 15), (string) $response->body); @@ -66,7 +69,8 @@ public function testScalarValuePassedToConstructorJsonEncodesDirectly(mixed $val public function testCanProvideStatusCodeToConstructor(): void { $response = new JsonResponse(null, 404); - $this->assertSame(404, $response->getStatusCode()); + $this->assertSame(404, $response->status); + $this->assertSame(StatusCode::NOT_FOUND, $response->statusCode); } public function testCanProvideAlternateContentTypeViaHeadersPassedToConstructor(): void { diff --git a/tests/Response/RedirectResponseTest.php b/tests/Response/RedirectResponseTest.php index ec82e8c..c7998d9 100644 --- a/tests/Response/RedirectResponseTest.php +++ b/tests/Response/RedirectResponseTest.php @@ -7,6 +7,7 @@ use InvalidArgumentException; use Rodas\Diactoros\Response\RedirectResponse; use Rodas\Diactoros\Uri; +use Rodas\Psr\Http\Message\StatusCode; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; @@ -15,7 +16,8 @@ final class RedirectResponseTest extends TestCase public function testConstructorAcceptsStringUriAndProduces302ResponseWithLocationHeader(): void { $response = new RedirectResponse('/foo/bar'); - $this->assertSame(302, $response->getStatusCode()); + $this->assertSame(302, $response->status); + $this->assertSame(StatusCode::FOUND, $response->statusCode); $this->assertTrue($response->hasHeader('Location')); $this->assertSame('/foo/bar', $response->getHeaderLine('Location')); } @@ -24,7 +26,8 @@ public function testConstructorAcceptsUriInstanceAndProduces302ResponseWithLocat { $uri = new Uri('https://example.com:10082/foo/bar'); $response = new RedirectResponse($uri); - $this->assertSame(302, $response->getStatusCode()); + $this->assertSame(302, $response->status); + $this->assertSame(StatusCode::FOUND, $response->statusCode); $this->assertTrue($response->hasHeader('Location')); $this->assertSame((string) $uri, $response->getHeaderLine('Location')); } @@ -32,7 +35,8 @@ public function testConstructorAcceptsUriInstanceAndProduces302ResponseWithLocat public function testConstructorAllowsSpecifyingAlternateStatusCode(): void { $response = new RedirectResponse('/foo/bar', 301); - $this->assertSame(301, $response->getStatusCode()); + $this->assertSame(301, $response->status); + $this->assertSame(StatusCode::MOVED_PERMANENTLY, $response->statusCode); $this->assertTrue($response->hasHeader('Location')); $this->assertSame('/foo/bar', $response->getHeaderLine('Location')); } @@ -40,7 +44,8 @@ public function testConstructorAllowsSpecifyingAlternateStatusCode(): void public function testConstructorAllowsSpecifyingHeaders(): void { $response = new RedirectResponse('/foo/bar', 302, ['X-Foo' => ['Bar']]); - $this->assertSame(302, $response->getStatusCode()); + $this->assertSame(302, $response->status); + $this->assertSame(StatusCode::FOUND, $response->statusCode); $this->assertTrue($response->hasHeader('Location')); $this->assertSame('/foo/bar', $response->getHeaderLine('Location')); $this->assertTrue($response->hasHeader('X-Foo')); diff --git a/tests/Response/SerializerTest.php b/tests/Response/SerializerTest.php index 2f50647..1d502ca 100644 --- a/tests/Response/SerializerTest.php +++ b/tests/Response/SerializerTest.php @@ -9,9 +9,11 @@ use Rodas\Diactoros\Response\Serializer; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\MockObject\Runtime\PropertyHook; use PHPUnit\Framework\TestCase; use Rodas\Psr\Http\Message\ResponseInterface; use Rodas\Psr\Http\Message\StreamInterface; +use Rodas\Psr\Http\Message\StatusCode; use UnexpectedValueException; final class SerializerTest extends TestCase { @@ -70,7 +72,7 @@ public function testCanDeserializeBasicResponse(): void { $this->assertInstanceOf(Response::class, $response); $this->assertSame('1.0', $response->protocolVersion); - $this->assertSame(200, $response->statusCode); + $this->assertSame(StatusCode::OK, $response->statusCode); $this->assertSame(200, $response->status); $this->assertSame('A-OK', $response->reasonPhrase); @@ -223,7 +225,7 @@ public function testFromStreamThrowsExceptionWhenStreamIsNotReadable(): void $stream = $this->createMock(StreamInterface::class); $stream ->expects($this->once()) - ->variable('isReadable') + ->method(PropertyHook::get('isReadable')) ->willReturn(false); $this->expectException(InvalidArgumentException::class); @@ -236,11 +238,11 @@ public function testFromStreamThrowsExceptionWhenStreamIsNotSeekable(): void $stream = $this->createMock(StreamInterface::class); $stream ->expects($this->once()) - ->variable('isReadable') + ->method(PropertyHook::get('isReadable')) ->willReturn(true); $stream ->expects($this->once()) - ->variable('isSeekable') + ->method(PropertyHook::get('isSeekable')) ->willReturn(false); $this->expectException(InvalidArgumentException::class); @@ -253,6 +255,7 @@ public function testDeserializeCorrectlyCastsStatusCodeToInteger(): void { $response = Response\Serializer::fromString('HTTP/1.0 204'); // according to interface the int is expected - $this->assertSame(204, $response->getStatusCode()); + $this->assertSame(204, $response->status); + $this->assertSame(StatusCode::NO_CONTENT, $response->statusCode); } } diff --git a/tests/Response/TextResponseTest.php b/tests/Response/TextResponseTest.php index 3477389..fd270d9 100644 --- a/tests/Response/TextResponseTest.php +++ b/tests/Response/TextResponseTest.php @@ -6,10 +6,12 @@ use InvalidArgumentException; use Rodas\Diactoros\Response\TextResponse; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; use Rodas\Psr\Http\Message\StreamInterface; +use Rodas\Psr\Http\Message\StatusCode; final class TextResponseTest extends TestCase { @@ -19,7 +21,8 @@ public function testConstructorAcceptsBodyAsString(): void $response = new TextResponse($body); $this->assertSame($body, (string) $response->body); - $this->assertSame(200, $response->getStatusCode()); + $this->assertSame(200, $response->status); + $this->assertSame(StatusCode::OK, $response->statusCode); } public function testConstructorAllowsPassingStatus(): void @@ -28,7 +31,8 @@ public function testConstructorAllowsPassingStatus(): void $status = 404; $response = new TextResponse($body, $status); - $this->assertSame(404, $response->getStatusCode()); + $this->assertSame(404, $response->status); + $this->assertSame(StatusCode::NOT_FOUND, $response->statusCode); $this->assertSame($body, (string) $response->body); } @@ -43,12 +47,13 @@ public function testConstructorAllowsPassingHeaders(): void $response = new TextResponse($body, $status, $headers); $this->assertSame(['foo-bar'], $response->getHeader('x-custom')); $this->assertSame('text/plain; charset=utf-8', $response->getHeaderLine('content-type')); - $this->assertSame(404, $response->getStatusCode()); + $this->assertSame(404, $response->status); + $this->assertSame(StatusCode::NOT_FOUND, $response->statusCode); $this->assertSame($body, (string) $response->body); } - public function testAllowsStreamsForResponseBody(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testAllowsStreamsForResponseBody(): void { $body = $this->createMock(StreamInterface::class); $response = new TextResponse($body); $this->assertSame($body, $response->body); diff --git a/tests/Response/XmlResponseTest.php b/tests/Response/XmlResponseTest.php index ca07ec4..d2787eb 100644 --- a/tests/Response/XmlResponseTest.php +++ b/tests/Response/XmlResponseTest.php @@ -6,10 +6,12 @@ use InvalidArgumentException; use Rodas\Diactoros\Response\XmlResponse; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; use Rodas\Psr\Http\Message\StreamInterface; +use Rodas\Psr\Http\Message\StatusCode; use const PHP_EOL; @@ -21,7 +23,8 @@ public function testConstructorAcceptsBodyAsString(): void $response = new XmlResponse($body); $this->assertSame($body, (string) $response->body); - $this->assertSame(200, $response->getStatusCode()); + $this->assertSame(200, $response->status); + $this->assertSame(StatusCode::OK, $response->statusCode); } public function testConstructorAllowsPassingStatus(): void @@ -30,7 +33,8 @@ public function testConstructorAllowsPassingStatus(): void $status = 404; $response = new XmlResponse($body, $status); - $this->assertSame(404, $response->getStatusCode()); + $this->assertSame(404, $response->status); + $this->assertSame(StatusCode::NOT_FOUND, $response->statusCode); $this->assertSame($body, (string) $response->body); } @@ -45,12 +49,13 @@ public function testConstructorAllowsPassingHeaders(): void $response = new XmlResponse($body, $status, $headers); $this->assertSame(['foo-bar'], $response->getHeader('x-custom')); $this->assertSame('application/xml; charset=utf-8', $response->getHeaderLine('content-type')); - $this->assertSame(404, $response->getStatusCode()); + $this->assertSame(404, $response->status); + $this->assertSame(StatusCode::NOT_FOUND, $response->statusCode); $this->assertSame($body, (string) $response->body); } - public function testAllowsStreamsForResponseBody(): void - { + #[AllowMockObjectsWithoutExpectations] + public function testAllowsStreamsForResponseBody(): void { $body = $this->createMock(StreamInterface::class); $response = new XmlResponse($body); $this->assertSame($body, $response->body); diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php index 0335880..f1a19f4 100644 --- a/tests/ResponseTest.php +++ b/tests/ResponseTest.php @@ -10,6 +10,7 @@ use InvalidArgumentException; use Rodas\Diactoros\Response; use Rodas\Diactoros\Stream; +use Rodas\Psr\Http\Message\StatusCode; use Override; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; @@ -50,20 +51,22 @@ protected function setUp(): void public function testStatusCodeIs200ByDefault(): void { - $this->assertSame(200, $this->response->getStatusCode()); + $this->assertSame(200, $this->response->status); + $this->assertSame(StatusCode::OK, $this->response->statusCode); } public function testStatusCodeMutatorReturnsCloneWithChanges(): void { $response = $this->response->withStatus(400); $this->assertNotSame($this->response, $response); - $this->assertSame(400, $response->getStatusCode()); + $this->assertSame(400, $response->status); + $this->assertSame(StatusCode::BAD_REQUEST, $response->statusCode); } public function testReasonPhraseDefaultsToStandards(): void { $response = $this->response->withStatus(422); - $this->assertSame('Unprocessable Content', $response->getReasonPhrase()); + $this->assertSame('Unprocessable Content', $response->reasonPhrase); } private static function fetchIanaStatusCodes(): DOMDocument @@ -177,18 +180,16 @@ public static function ianaCodesReasonPhrasesProvider(): array /** * @param non-empty-string $reasonPhrase + */ #[DataProvider('ianaCodesReasonPhrasesProvider')] - public function testReasonPhraseDefaultsAgainstIana(int $code, string $reasonPhrase): void - { + public function testReasonPhraseDefaultsAgainstIana(int $code, string $reasonPhrase): void { $response = $this->response->withStatus($code); - $this->assertSame($reasonPhrase, $response->getReasonPhrase()); + $this->assertSame($reasonPhrase, $response->reasonPhrase); } - */ - public function testCanSetCustomReasonPhrase(): void - { + public function testCanSetCustomReasonPhrase(): void { $response = $this->response->withStatus(422, 'Foo Bar!'); - $this->assertSame('Foo Bar!', $response->getReasonPhrase()); + $this->assertSame('Foo Bar!', $response->reasonPhrase); } public function testConstructorRaisesExceptionForInvalidStream(): void @@ -209,33 +210,34 @@ public function testConstructorCanAcceptAllMessageParts(): void $response = new Response($body, $status, $headers); $this->assertSame($body, $response->body); - $this->assertSame(302, $response->getStatusCode()); - $this->assertSame($headers, $response->getHeaders()); + $this->assertSame(302, $response->status); + $this->assertSame(StatusCode::FOUND, $response->statusCode); + $this->assertSame($headers, $response->headers); } #[DataProvider('validStatusCodes')] - public function testCreateWithValidStatusCodes(int $code): void + public function testCreateWithValidStatusCodes(int $status, ?StatusCode $statusCode): void { - $response = $this->response->withStatus($code); + $response = $this->response->withStatus($status); - $result = $response->getStatusCode(); + $statusResult = $response->status; + $statusCodeResult = $response->statusCode; - $this->assertSame($code, $result); + $this->assertSame($status, $statusResult); + $this->assertSame($statusCode, $statusCodeResult); } /** @return non-empty-array */ - public static function validStatusCodes(): array - { + public static function validStatusCodes(): array { return [ - 'minimum' => [100], - 'middle' => [300], - 'maximum' => [599], + 'minimum' => [100, StatusCode::CONTINUE], + 'middle' => [300, StatusCode::MULTIPLE_CHOICES], + 'maximum' => [599, null], ]; } #[DataProvider('invalidStatusCodes')] - public function testCannotSetInvalidStatusCode(mixed $code): void - { + public function testCannotSetInvalidStatusCode(mixed $code): void { $this->expectException(InvalidArgumentException::class); /** @psalm-suppress MixedArgument */ @@ -243,8 +245,7 @@ public function testCannotSetInvalidStatusCode(mixed $code): void } /** @return non-empty-array */ - public static function invalidStatusCodes(): array - { + public static function invalidStatusCodes(): array { return [ 'too-low' => [99], 'too-high' => [600], @@ -252,8 +253,7 @@ public static function invalidStatusCodes(): array } /** @return non-empty-array */ - public static function invalidResponseBody(): array - { + public static function invalidResponseBody(): array { return [ 'true' => [true], 'false' => [false], @@ -265,8 +265,7 @@ public static function invalidResponseBody(): array } #[DataProvider('invalidResponseBody')] - public function testConstructorRaisesExceptionForInvalidBody(mixed $body): void - { + public function testConstructorRaisesExceptionForInvalidBody(mixed $body): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('stream'); @@ -306,8 +305,8 @@ public function testConstructorRaisesExceptionForInvalidHeaders( public function testReasonPhraseCanBeEmpty(): void { $response = $this->response->withStatus(555); - $this->assertIsString($response->getReasonPhrase()); - $this->assertEmpty($response->getReasonPhrase()); + $this->assertIsString($response->reasonPhrase); + $this->assertEmpty($response->reasonPhrase); } /** @return non-empty-array}> */ diff --git a/tests/functions/MarshalHeadersFromSapiTest.php b/tests/ServerRequestFactory/MarshalHeadersFromSapiTest.php similarity index 100% rename from tests/functions/MarshalHeadersFromSapiTest.php rename to tests/ServerRequestFactory/MarshalHeadersFromSapiTest.php diff --git a/tests/functions/NormalizeUploadedFilesTest.php b/tests/ServerRequestFactory/NormalizeUploadedFilesTest.php similarity index 100% rename from tests/functions/NormalizeUploadedFilesTest.php rename to tests/ServerRequestFactory/NormalizeUploadedFilesTest.php diff --git a/tests/ServerRequestFactoryTest.php b/tests/ServerRequestFactoryTest.php index ea53fa5..cebfda0 100644 --- a/tests/ServerRequestFactoryTest.php +++ b/tests/ServerRequestFactoryTest.php @@ -136,7 +136,7 @@ public function testCanCreateServerRequestViaFromGlobalsMethod(): void $this->assertSame($body, $request->getParsedBody()); $this->assertEquals($expectedFiles, $request->uploadedFiles); $this->assertEmpty($request->attributes); - $this->assertSame('1.1', $request->getProtocolVersion()); + $this->assertSame('1.1', $request->protocolVersion); } public function testFromGlobalsShouldNotFallbackToSuperGlobalsWithEmptyArray(): void @@ -174,7 +174,7 @@ public function testFromGlobalsShouldNotFallbackToSuperGlobalsWithEmptyArray(): $this->assertEmpty($request->cookieParams, 'Cookies are not empty'); $this->assertEmpty($request->uploadedFiles, 'Uploaded files are not empty'); $defaults = new ServerRequest(); - $this->assertSame($defaults->getProtocolVersion(), $request->getProtocolVersion()); + $this->assertSame($defaults->protocolVersion, $request->protocolVersion); } public function testFromGlobalsUsesCookieHeaderInsteadOfCookieSuperGlobal(): void @@ -304,7 +304,7 @@ public function testNormalizeFilesReturnsOnlyActualFilesWhenOriginalFilesContain $normalizedFiles = ServerRequestFactory::normalizeUploadedFiles($files); - $this->assertCount(1, $normalizedFiles['fooFiles']); + $this->assertCount(1, $normalizedFiles); } public function testMarshalProtocolVersionRisesExceptionIfVersionIsNotRecognized(): void diff --git a/tests/ServerRequestFilter/FilterUsingXForwardedHeadersTest.php b/tests/ServerRequestFilter/FilterUsingXForwardedHeadersTest.php index d033936..1dbae4a 100644 --- a/tests/ServerRequestFilter/FilterUsingXForwardedHeadersTest.php +++ b/tests/ServerRequestFilter/FilterUsingXForwardedHeadersTest.php @@ -11,8 +11,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -final class FilterUsingXForwardedHeadersTest extends TestCase -{ +final class FilterUsingXForwardedHeadersTest extends TestCase { public function testTrustingStringProxyWithoutSpecifyingTrustedHeadersTrustsAllForwardedHeadersForThatProxy(): void { $request = new ServerRequest( @@ -258,14 +257,13 @@ public function testListOfForwardedProtosIsConsideredUntrusted(): void } /** @psalm-return iterable */ - public static function trustedReservedNetworkList(): iterable - { - yield 'ipv4-localhost' => ['127.0.0.1']; - yield 'ipv4-class-a' => ['10.10.10.10']; - yield 'ipv4-class-b' => ['172.16.16.16']; - yield 'ipv4-class-c' => ['192.168.2.1']; - yield 'ipv6-localhost' => ['::1']; - yield 'ipv6-private' => ['fdb4:d239:27bc:1d9f:0001:0001:0001:0001']; + public static function trustedReservedNetworkList(): iterable { + yield 'ipv4-localhost' => ['127.0.0.1']; + yield 'ipv4-class-a' => ['10.10.10.10']; + yield 'ipv4-class-b' => ['172.16.16.16']; + yield 'ipv4-class-c' => ['192.168.2.1']; + yield 'ipv6-localhost' => ['::1']; + yield 'ipv6-private' => ['fdb4:d239:27bc:1d9f:0001:0001:0001:0001']; yield 'ipv6-local-link' => ['fe80:0000:0000:0000:abcd:abcd:abcd:abcd']; } @@ -334,16 +332,15 @@ public function testTrustReservedSubnetsProducesFilterThatRejectsAddressesNotFro } /** @psalm-return iterable */ - public static function xForwardedProtoValues(): iterable - { - yield 'https-lowercase' => ['https', 'https']; - yield 'https-uppercase' => ['HTTPS', 'https']; - yield 'https-mixed-case' => ['hTTpS', 'https']; - yield 'http-lowercase' => ['http', 'http']; - yield 'http-uppercase' => ['HTTP', 'http']; - yield 'http-mixed-case' => ['hTTp', 'http']; - yield 'unknown-value' => ['foo', 'http']; - yield 'empty' => ['', 'http']; + public static function xForwardedProtoValues(): iterable { + yield 'https-lowercase' => ['https', 'https']; + yield 'https-uppercase' => ['HTTPS', 'https']; + yield 'https-mixed-case' => ['hTTpS', 'https']; + yield 'http-lowercase' => ['http', 'http']; + yield 'http-uppercase' => ['HTTP', 'http']; + yield 'http-mixed-case' => ['hTTp', 'http']; + yield 'unknown-value' => ['foo', 'http']; + yield 'empty' => ['', 'http']; } #[DataProvider('xForwardedProtoValues')] diff --git a/tests/ServerRequestTest.php b/tests/ServerRequestTest.php index 0a997b1..781df14 100644 --- a/tests/ServerRequestTest.php +++ b/tests/ServerRequestTest.php @@ -5,88 +5,76 @@ namespace Rodas\Test\Diactoros; use InvalidArgumentException; -use Rodas\Diactoros\ServerRequest; -use Rodas\Diactoros\UploadedFile; -use Rodas\Diactoros\Uri; use Override; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Depends; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; use ReflectionProperty; +use Rodas\Diactoros\ServerRequest; +use Rodas\Diactoros\UploadedFile; +use Rodas\Diactoros\Uri; +use Rodas\Psr\Http\Message\RequestMethod; -final class ServerRequestTest extends TestCase -{ +final class ServerRequestTest extends TestCase { private ServerRequest $request; #[Override] - protected function setUp(): void - { + protected function setUp(): void { $this->request = new ServerRequest(); } - public function testServerParamsAreEmptyByDefault(): void - { + public function testServerParamsAreEmptyByDefault(): void { $this->assertEmpty($this->request->serverParams); } - public function testQueryParamsAreEmptyByDefault(): void - { + public function testQueryParamsAreEmptyByDefault(): void { $this->assertEmpty($this->request->queryParams); } - public function testQueryParamsMutatorReturnsCloneWithChanges(): void - { + public function testQueryParamsMutatorReturnsCloneWithChanges(): void { $value = ['foo' => 'bar']; $request = $this->request->withQueryParams($value); $this->assertNotSame($this->request, $request); $this->assertSame($value, $request->queryParams); } - public function testCookiesAreEmptyByDefault(): void - { + public function testCookiesAreEmptyByDefault(): void { $this->assertEmpty($this->request->cookieParams); } - public function testCookiesMutatorReturnsCloneWithChanges(): void - { + public function testCookiesMutatorReturnsCloneWithChanges(): void { $value = ['foo' => 'bar']; $request = $this->request->withCookieParams($value); $this->assertNotSame($this->request, $request); $this->assertSame($value, $request->cookieParams); } - public function testUploadedFilesAreEmptyByDefault(): void - { + public function testUploadedFilesAreEmptyByDefault(): void { $this->assertEmpty($this->request->uploadedFiles); } - public function testParsedBodyIsEmptyByDefault(): void - { + public function testParsedBodyIsEmptyByDefault(): void { $this->assertEmpty($this->request->getParsedBody()); } - public function testParsedBodyMutatorReturnsCloneWithChanges(): void - { + public function testParsedBodyMutatorReturnsCloneWithChanges(): void { $value = ['foo' => 'bar']; $request = $this->request->withParsedBody($value); $this->assertNotSame($this->request, $request); $this->assertSame($value, $request->getParsedBody()); } - public function testAttributesAreEmptyByDefault(): void - { + public function testAttributesAreEmptyByDefault(): void { $this->assertEmpty($this->request->attributes); } - public function testSingleAttributesWhenEmptyByDefault(): void - { + public function testSingleAttributesWhenEmptyByDefault(): void { $this->assertEmpty($this->request->getAttribute('does-not-exist')); } #[Depends('testAttributesAreEmptyByDefault')] - public function testAttributeMutatorReturnsCloneWithChanges(): ServerRequest - { + public function testAttributeMutatorReturnsCloneWithChanges(): ServerRequest { $request = $this->request->withAttribute('foo', 'bar'); $this->assertNotSame($this->request, $request); $this->assertSame('bar', $request->getAttribute('foo')); @@ -94,16 +82,14 @@ public function testAttributeMutatorReturnsCloneWithChanges(): ServerRequest } #[Depends('testAttributeMutatorReturnsCloneWithChanges')] - public function testRemovingAttributeReturnsCloneWithoutAttribute(ServerRequest $request): void - { + public function testRemovingAttributeReturnsCloneWithoutAttribute(ServerRequest $request): void { $new = $request->withoutAttribute('foo'); $this->assertNotSame($request, $new); $this->assertNull($new->getAttribute('foo', null)); } /** @return non-empty-array */ - public static function provideMethods(): array - { + public static function provideMethods(): array { return [ 'post' => ['POST' , 'POST' , RequestMethod::POST], 'get' => ['GET' , 'GET' , RequestMethod::GET], @@ -116,8 +102,7 @@ public static function provideMethods(): array * @param non-empty-string $methodReturned */ #[DataProvider('provideMethods')] - public function testUsesProvidedConstructorArguments(?string $parameterMethod, string $methodReturned, RequestMethod $requestMethodReturned): void - { + public function testUsesProvidedConstructorArguments(?string $parameterMethod, string $methodReturned, RequestMethod $requestMethodReturned): void { $server = [ 'foo' => 'bar', 'baz' => 'bat', @@ -174,45 +159,39 @@ public function testUsesProvidedConstructorArguments(?string $parameterMethod, s } #[Group('46')] - public function testCookieParamsAreAnEmptyArrayAtInitialization(): void - { + public function testCookieParamsAreAnEmptyArrayAtInitialization(): void { $request = new ServerRequest(); $this->assertIsArray($request->cookieParams); $this->assertCount(0, $request->cookieParams); } #[Group('46')] - public function testQueryParamsAreAnEmptyArrayAtInitialization(): void - { + public function testQueryParamsAreAnEmptyArrayAtInitialization(): void { $request = new ServerRequest(); $this->assertIsArray($request->queryParams); $this->assertCount(0, $request->queryParams); } #[Group('46')] - public function testParsedBodyIsNullAtInitialization(): void - { + public function testParsedBodyIsNullAtInitialization(): void { $request = new ServerRequest(); $this->assertNull($request->getParsedBody()); } - public function testAllowsRemovingAttributeWithNullValue(): void - { + public function testAllowsRemovingAttributeWithNullValue(): void { $request = new ServerRequest(); $request = $request->withAttribute('boo', null); $request = $request->withoutAttribute('boo'); $this->assertSame([], $request->attributes); } - public function testAllowsRemovingNonExistentAttribute(): void - { + public function testAllowsRemovingNonExistentAttribute(): void { $request = new ServerRequest(); $request = $request->withoutAttribute('boo'); $this->assertSame([], $request->attributes); } - public function testTryToAddInvalidUploadedFiles(): void - { + public function testTryToAddInvalidUploadedFiles(): void { $request = new ServerRequest(); $this->expectException(InvalidArgumentException::class); @@ -220,8 +199,7 @@ public function testTryToAddInvalidUploadedFiles(): void $request->withUploadedFiles([null]); } - public function testNestedUploadedFiles(): void - { + public function testNestedUploadedFiles(): void { $request = new ServerRequest(); $uploadedFiles = [ diff --git a/tests/StreamTest.php b/tests/StreamTest.php index 8bf2eec..935d416 100644 --- a/tests/StreamTest.php +++ b/tests/StreamTest.php @@ -11,6 +11,7 @@ use Override; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\MockObject\Runtime\PropertyHook; use PHPUnit\Framework\TestCase; use ReflectionProperty; use RuntimeException; @@ -137,8 +138,7 @@ public function testPassingInvalidStreamResourceToConstructorRaisesException(): new Stream([' THIS WILL NOT WORK ']); } - public function testStringSerializationReturnsEmptyStringWhenStreamIsNotReadable(): void - { + public function testStringSerializationReturnsEmptyStringWhenStreamIsNotReadable(): void { $this->tmpnam = tempnam(sys_get_temp_dir(), 'diac'); assert($this->tmpnam !== false, 'Always true condition for psalm type safety'); file_put_contents($this->tmpnam, 'FOO BAR'); @@ -147,8 +147,7 @@ public function testStringSerializationReturnsEmptyStringWhenStreamIsNotReadable $this->assertSame('', $stream->__toString()); } - public function testCloseClosesResource(): void - { + public function testCloseClosesResource(): void { $this->tmpnam = tempnam(sys_get_temp_dir(), 'diac'); assert($this->tmpnam !== false, 'Always true condition for psalm type safety'); $resource = fopen($this->tmpnam, 'wb+'); @@ -158,8 +157,7 @@ public function testCloseClosesResource(): void $this->assertFalse(is_resource($resource)); } - public function testCloseUnsetsResource(): void - { + public function testCloseUnsetsResource(): void { $this->tmpnam = tempnam(sys_get_temp_dir(), 'diac'); assert($this->tmpnam !== false, 'Always true condition for psalm type safety'); $resource = fopen($this->tmpnam, 'wb+'); @@ -565,6 +563,7 @@ public function testAttachWithNonStringNonResourceRaisesException(mixed $resourc /** * @return array + */ public static function invalidStringResources(): array { return [ @@ -575,14 +574,12 @@ public static function invalidStringResources(): array } #[DataProvider('invalidStringResources')] - public function testAttachWithInvalidStringResourceRaisesException(string $stream): void - { + public function testAttachWithInvalidStringResourceRaisesException(string $stream): void { $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Empty or non-existent stream identifier or file path provided'); $this->stream->attach($stream); } - */ public function testAttachWithResourceAttachesResource(): void { @@ -764,12 +761,14 @@ public function testCanReadContentFromNotSeekableResource(): void $stream = $this ->getMockBuilder(Stream::class) ->setConstructorArgs([$resource]) - ->onlyMethods(['isSeekable']) ->getMock(); - $stream->expects($this->any())->method('isSeekable') + $stream->expects($this->any()) + ->method(PropertyHook::get('isSeekable')) ->willReturn(false); + $stream = new Stream($resource); + $this->assertSame('FOO BAR', $stream->__toString()); } From e65dd50fec8ff2b348cfd6ec8874e5e9b1ad1550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcos=20Porto=20Mari=C3=B1o?= Date: Tue, 13 Jan 2026 00:48:26 +0100 Subject: [PATCH 2/2] Fix `ServerRequestFilter/FilterUsingXForwardedHeaders.php` --- src/ServerRequestFilter/FilterUsingXForwardedHeaders.php | 3 +-- src/version.txt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ServerRequestFilter/FilterUsingXForwardedHeaders.php b/src/ServerRequestFilter/FilterUsingXForwardedHeaders.php index 60bbb2b..97a8979 100644 --- a/src/ServerRequestFilter/FilterUsingXForwardedHeaders.php +++ b/src/ServerRequestFilter/FilterUsingXForwardedHeaders.php @@ -77,14 +77,13 @@ public function __invoke(ServerRequestInterface $request): ServerRequestInterfac ! is_string($remoteAddress) || // Should we trigger a warning here? ! $this->isFromTrustedProxy($remoteAddress)) { // Do nothing - //return $request; + return $request; } // Update the URI based on the trusted headers $uri = $originalUri = $request->uri; foreach ($this->trustedHeaders as $headerName) { $header = $request->getHeaderLine($headerName); - fwrite(STDERR, $headerName . ': ' . $header . PHP_EOL); if ('' === $header || str_contains($header, ',')) { // Reject empty headers and/or headers with multiple values diff --git a/src/version.txt b/src/version.txt index fc6b9fb..4d06113 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1,2 +1,2 @@ -Commit: 89ff700 +Commit: dcfcfd5 Version: 0.1.0