diff --git a/src/HttpContentType.php b/src/HttpContentType.php new file mode 100644 index 0000000..685a224 --- /dev/null +++ b/src/HttpContentType.php @@ -0,0 +1,29 @@ +value; + } +} diff --git a/src/HttpHeaders.php b/src/HttpHeaders.php new file mode 100644 index 0000000..edf8b2f --- /dev/null +++ b/src/HttpHeaders.php @@ -0,0 +1,43 @@ +key(); + $this->values['header'][$key] = sprintf('%s: %s', $key, $header->value()); + + return $this; + } + + public function getHeader(): array + { + return $this->values['header'] ?? []; + } + + public function hasHeaders(): bool + { + return !empty($this->values); + } + + public function hasHeader(string $key): bool + { + return !empty($this->getHeader()[$key]); + } + + public function toArray(): array + { + return $this->values; + } +} diff --git a/src/HttpResponse.php b/src/HttpResponse.php index c8c79e0..7c079f2 100644 --- a/src/HttpResponse.php +++ b/src/HttpResponse.php @@ -12,22 +12,22 @@ */ final class HttpResponse { - public static function ok(mixed $data, array $headers = []): ResponseInterface + public static function ok(mixed $data, ?HttpHeaders $headers = null): ResponseInterface { return Response::from(code: HttpCode::OK, data: $data, headers: $headers); } - public static function created(mixed $data, array $headers = []): ResponseInterface + public static function created(mixed $data, HttpHeaders $headers = null): ResponseInterface { return Response::from(code: HttpCode::CREATED, data: $data, headers: $headers); } - public static function accepted(mixed $data, array $headers = []): ResponseInterface + public static function accepted(mixed $data, HttpHeaders $headers = null): ResponseInterface { return Response::from(code: HttpCode::ACCEPTED, data: $data, headers: $headers); } - public static function noContent(array $headers = []): ResponseInterface + public static function noContent(HttpHeaders $headers = null): ResponseInterface { return Response::from(code: HttpCode::NO_CONTENT, data: null, headers: $headers); } diff --git a/src/Internal/Header.php b/src/Internal/Header.php new file mode 100644 index 0000000..6eb3807 --- /dev/null +++ b/src/Internal/Header.php @@ -0,0 +1,10 @@ + 'application/json']; + if (is_null($headers) || !$headers->hasHeaders()) { + $headers = (new HttpHeaders())->add(header: HttpContentType::APPLICATION_JSON); } return new Response(code: $code, body: StreamFactory::from(data: $data), headers: $headers); @@ -64,17 +66,17 @@ public function getProtocolVersion(): string public function getHeaders(): array { - return $this->headers; + return $this->headers->toArray(); } public function hasHeader(string $name): bool { - return isset($this->headers[$name]); + return $this->headers->hasHeader(key: $name); } public function getHeader(string $name): array { - return $this->headers[$name] ?? []; + return $this->headers->getHeader(); } public function getHeaderLine(string $name): string diff --git a/tests/HttpResponseTest.php b/tests/HttpResponseTest.php index dd01ab9..28c0399 100644 --- a/tests/HttpResponseTest.php +++ b/tests/HttpResponseTest.php @@ -8,7 +8,7 @@ class HttpResponseTest extends TestCase { - private array $defaultHeader = [['Content-Type' => 'application/json']]; + private array $defaultHeader = ['header' => ['Content-Type' => 'Content-Type: application/json']]; /** * @dataProvider providerData diff --git a/tests/Internal/HeadersTest.php b/tests/Internal/HeadersTest.php new file mode 100644 index 0000000..8dcfb82 --- /dev/null +++ b/tests/Internal/HeadersTest.php @@ -0,0 +1,29 @@ +add(header: HttpContentType::APPLICATION_JSON); + $expected = ['header' => ['Content-Type' => 'Content-Type: application/json']]; + + self::assertEquals($expected, $headers->toArray()); + } + + public function testAddAndGetUniqueValues(): void + { + $headers = (new HttpHeaders()) + ->add(header: HttpContentType::TEXT_HTML) + ->add(header: HttpContentType::APPLICATION_PDF); + + $expected = ['header' => ['Content-Type' => 'Content-Type: application/pdf']]; + + self::assertEquals($expected, $headers->toArray()); + } +} diff --git a/tests/Internal/ResponseTest.php b/tests/Internal/ResponseTest.php index 3f4d094..1ae43cc 100644 --- a/tests/Internal/ResponseTest.php +++ b/tests/Internal/ResponseTest.php @@ -4,125 +4,116 @@ use PHPUnit\Framework\TestCase; use TinyBlocks\Http\HttpCode; +use TinyBlocks\Http\HttpContentType; +use TinyBlocks\Http\HttpHeaders; use TinyBlocks\Http\Internal\Exceptions\BadMethodCall; use TinyBlocks\Http\Internal\Stream\StreamFactory; class ResponseTest extends TestCase { - private Response $response; + private const TEXT_PLAIN = 'Content-Type: text/plain'; + private const APPLICATION_JSON = 'Content-Type: application/json'; - protected function setUp(): void + public function testDefaultHeaders(): void { - $this->response = Response::from(code: HttpCode::OK, data: [], headers: []); + $response = Response::from(code: HttpCode::OK, data: [], headers: null); + $expected = ['header' => ['Content-Type' => self::APPLICATION_JSON]]; + + self::assertEquals($expected, $response->getHeaders()); } public function testGetProtocolVersion(): void { - self::assertEquals('1.1', $this->response->getProtocolVersion()); + $response = Response::from(code: HttpCode::OK, data: [], headers: null); + + self::assertEquals('1.1', $response->getProtocolVersion()); } public function testGetHeaders(): void { - $headers = [ - 'Content-Type' => 'application/json', - 'X-Auth-Token' => 'abc123' - ]; - + $headers = (new HttpHeaders())->add(header: HttpContentType::APPLICATION_JSON); $response = Response::from(code: HttpCode::OK, data: [], headers: $headers); + $expected = ['Content-Type' => self::APPLICATION_JSON]; - self::assertEquals($headers, $response->getHeaders()); + self::assertEquals($headers->toArray(), $response->getHeaders()); + self::assertEquals($expected, $response->getHeader(name: 'Content-Type')); } public function testHasHeader(): void { - $headers = [ - 'Content-Type' => 'application/json', - 'X-Auth-Token' => 'abc123' - ]; - + $headers = (new HttpHeaders())->add(header: HttpContentType::TEXT_PLAIN); $response = Response::from(code: HttpCode::OK, data: [], headers: $headers); + $expected = ['Content-Type' => self::TEXT_PLAIN]; self::assertTrue($response->hasHeader(name: 'Content-Type')); - self::assertFalse($response->hasHeader(name: 'Authorization')); - } - - public function testGetHeader(): void - { - $headers = [ - 'Content-Type' => ['application/json'], - 'X-Auth-Token' => ['abc123'], - 'X-Custom-Header' => ['value1', 'value2'] - ]; - - $response = Response::from(code: HttpCode::OK, data: [], headers: $headers); - - self::assertEquals(['application/json'], $response->getHeader(name: 'Content-Type')); - self::assertEquals(['abc123'], $response->getHeader(name: 'X-Auth-Token')); - self::assertEquals(['value1', 'value2'], $response->getHeader(name: 'X-Custom-Header')); - self::assertEquals([], $response->getHeader(name: 'Authorization')); + self::assertEquals($expected, $response->getHeader(name: 'Content-Type')); } public function testGetHeaderLine(): void { - $headers = [ - 'Content-Type' => ['application/json'], - 'X-Auth-Token' => ['abc123'], - 'X-Custom-Header' => ['value1', 'value2'] - ]; - + $headers = (new HttpHeaders())->add(header: HttpContentType::APPLICATION_JSON); $response = Response::from(code: HttpCode::OK, data: [], headers: $headers); - self::assertEquals('application/json', $response->getHeaderLine(name: 'Content-Type')); - self::assertEquals('abc123', $response->getHeaderLine(name: 'X-Auth-Token')); - self::assertEquals('value1, value2', $response->getHeaderLine(name: 'X-Custom-Header')); - self::assertEquals('', $response->getHeaderLine(name: 'Authorization')); + self::assertEquals(self::APPLICATION_JSON, $response->getHeaderLine(name: 'Content-Type')); } public function testExceptionWhenBadMethodCallOnWithBody(): void { + $response = Response::from(code: HttpCode::OK, data: [], headers: null); + self::expectException(BadMethodCall::class); self::expectExceptionMessage('Method cannot be used.'); - $this->response->withBody(body: StreamFactory::from(data: [])); + $response->withBody(body: StreamFactory::from(data: [])); } public function testExceptionWhenBadMethodCallOnWithStatus(): void { + $response = Response::from(code: HttpCode::OK, data: [], headers: null); + self::expectException(BadMethodCall::class); self::expectExceptionMessage('Method cannot be used.'); - $this->response->withStatus(code: HttpCode::OK->value); + $response->withStatus(code: HttpCode::OK->value); } public function testExceptionWhenBadMethodCallOnWithHeader(): void { + $response = Response::from(code: HttpCode::OK, data: [], headers: null); + self::expectException(BadMethodCall::class); self::expectExceptionMessage('Method cannot be used.'); - $this->response->withHeader(name: '', value: ''); + $response->withHeader(name: '', value: ''); } public function testExceptionWhenBadMethodCallOnWithoutHeader(): void { + $response = Response::from(code: HttpCode::OK, data: [], headers: null); + self::expectException(BadMethodCall::class); self::expectExceptionMessage('Method cannot be used.'); - $this->response->withoutHeader(name: ''); + $response->withoutHeader(name: ''); } public function testExceptionWhenBadMethodCallOnWithAddedHeader(): void { + $response = Response::from(code: HttpCode::OK, data: [], headers: null); + self::expectException(BadMethodCall::class); self::expectExceptionMessage('Method cannot be used.'); - $this->response->withAddedHeader(name: '', value: ''); + $response->withAddedHeader(name: '', value: ''); } public function testExceptionWhenBadMethodCallOnWithProtocolVersion(): void { + $response = Response::from(code: HttpCode::OK, data: [], headers: null); + self::expectException(BadMethodCall::class); self::expectExceptionMessage('Method cannot be used.'); - $this->response->withProtocolVersion(version: ''); + $response->withProtocolVersion(version: ''); } }