Skip to content

Commit

Permalink
Merge pull request #8 from tiny-blocks/release/2.1.0
Browse files Browse the repository at this point in the history
Release/2.1.0
  • Loading branch information
gustavofreze committed Jun 13, 2023
2 parents cfd2e1f + 7862271 commit 55850de
Show file tree
Hide file tree
Showing 19 changed files with 906 additions and 7 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ echo $method->name; # GET
echo $method->value; # GET
```

### Using the HttpResponse

The library exposes a concrete implementation for HTTP responses via the `HttpResponse` class. Responses are of the
[ResponseInterface](https://github.com/php-fig/http-message/blob/master/src/ResponseInterface.php) type, according to
the specifications defined in [PSR-7](https://www.php-fig.org/psr/psr-7).

```php
$data = new Xyz(value: 10);
$response = HttpResponse::ok(data: $data);

$response->getStatusCode(); # 200
$response->getReasonPhrase(); # 200 Ok
$response->getBody()->getContents(); # {"value":10}
```

## License

Math is licensed under [MIT](/LICENSE).
Expand Down
17 changes: 11 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
"keywords": [
"psr",
"psr-4",
"psr-7",
"psr-12",
"http",
"http-code",
"tiny-blocks",
"http-status",
"tiny-blocks"
"http-methods",
"http-response"
],
"authors": [
{
Expand All @@ -39,20 +42,22 @@
},
"require": {
"php": "^8.1||^8.2",
"tiny-blocks/serializer": "1.*",
"psr/http-message": "2.*",
"ext-mbstring": "*"
},
"require-dev": {
"infection/infection": "^0.26",
"phpmd/phpmd": "^2.12",
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.7"
"infection/infection": "0.*",
"phpmd/phpmd": "2.*",
"phpunit/phpunit": "9.*",
"squizlabs/php_codesniffer": "3.*"
},
"suggest": {
"ext-mbstring": "Provides multibyte-specific string functions that help us deal with multibyte encodings in PHP."
},
"scripts": {
"phpcs": "phpcs --standard=PSR12 --extensions=php ./src",
"phpmd": "phpmd ./src text phpmd.xml --suffixes php --exclude /src/HttpCode.php --ignore-violations-on-exit",
"phpmd": "phpmd ./src text phpmd.xml --suffixes php --exclude /src/HttpCode.php --exclude /src/Internal/Response --ignore-violations-on-exit",
"test": "phpunit --log-junit=report/coverage/junit.xml --coverage-xml=report/coverage/coverage-xml --coverage-html=report/coverage/coverage-html tests",
"test-mutation": "infection --only-covered --logger-html=report/coverage/mutation-report.html --coverage=report/coverage --min-msi=100 --min-covered-msi=100 --threads=4",
"test-no-coverage": "phpunit --no-coverage",
Expand Down
10 changes: 9 additions & 1 deletion infection.json.dist
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@
"summary": "report/logs/infection-summary.log"
},
"mutators": {
"@default": true
"@default": true,
"ArrayItem": false,
"LogicalOr": false,
"IfNegation": false,
"InstanceOf_": false,
"ArrayItemRemoval": false,
"MethodCallRemoval": false,
"LogicalOrAllSubExprNegation": false,
"LogicalOrSingleSubExprNegation": false
},
"phpUnit": {
"configDir": "",
Expand Down
4 changes: 4 additions & 0 deletions src/HttpCode.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
* Responses are grouped in five classes:
*
* Informational (100 – 199)
*
* Successful (200 – 299)
*
* Redirection (300 – 399)
*
* Client error (400 – 499)
*
* Server error (500 – 599)
*
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#information_responses
Expand Down
34 changes: 34 additions & 0 deletions src/HttpResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace TinyBlocks\Http;

use Psr\Http\Message\ResponseInterface;
use TinyBlocks\Http\Internal\Response;

/**
* Define HTTP response following the PSR7 specification.
*
* @see https://www.php-fig.org/psr/psr-7
*/
final class HttpResponse
{
public static function ok(mixed $data, array $headers = []): ResponseInterface
{
return Response::from(code: HttpCode::OK, data: $data, headers: $headers);
}

public static function created(mixed $data, array $headers = []): ResponseInterface
{
return Response::from(code: HttpCode::CREATED, data: $data, headers: $headers);
}

public static function accepted(mixed $data, array $headers = []): ResponseInterface
{
return Response::from(code: HttpCode::ACCEPTED, data: $data, headers: $headers);
}

public static function noContent(array $headers = []): ResponseInterface
{
return Response::from(code: HttpCode::NO_CONTENT, data: null, headers: $headers);
}
}
14 changes: 14 additions & 0 deletions src/Internal/Exceptions/BadMethodCall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace TinyBlocks\Http\Internal\Exceptions;

use BadMethodCallException;

final class BadMethodCall extends BadMethodCallException
{
public function __construct(private readonly string $method)
{
$template = 'Method <%s> cannot be used.';
parent::__construct(message: sprintf($template, $this->method));
}
}
13 changes: 13 additions & 0 deletions src/Internal/Exceptions/MissingResourceStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace TinyBlocks\Http\Internal\Exceptions;

use RuntimeException;

final class MissingResourceStream extends RuntimeException
{
public function __construct()
{
parent::__construct(message: 'No resource available.');
}
}
13 changes: 13 additions & 0 deletions src/Internal/Exceptions/NonReadableStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace TinyBlocks\Http\Internal\Exceptions;

use RuntimeException;

final class NonReadableStream extends RuntimeException
{
public function __construct()
{
parent::__construct(message: 'Stream is not readable.');
}
}
13 changes: 13 additions & 0 deletions src/Internal/Exceptions/NonSeekableStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace TinyBlocks\Http\Internal\Exceptions;

use RuntimeException;

final class NonSeekableStream extends RuntimeException
{
public function __construct()
{
parent::__construct(message: 'Stream is not seekable.');
}
}
13 changes: 13 additions & 0 deletions src/Internal/Exceptions/NonWritableStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace TinyBlocks\Http\Internal\Exceptions;

use RuntimeException;

final class NonWritableStream extends RuntimeException
{
public function __construct()
{
parent::__construct(message: 'Stream is not writable.');
}
}
97 changes: 97 additions & 0 deletions src/Internal/Response.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

namespace TinyBlocks\Http\Internal;

use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use TinyBlocks\Http\HttpCode;
use TinyBlocks\Http\Internal\Exceptions\BadMethodCall;
use TinyBlocks\Http\Internal\Stream\StreamFactory;

final class Response implements ResponseInterface
{
private function __construct(
private readonly HttpCode $code,
private readonly StreamInterface $body,
private readonly array $headers
) {
}

public static function from(HttpCode $code, mixed $data, array $headers): ResponseInterface
{
$headers = empty($headers) ? ['Content-Type' => 'application/json'] : $headers;

return new Response(code: $code, body: StreamFactory::from(data: $data), headers: $headers);
}

public function withBody(StreamInterface $body): MessageInterface
{
throw new BadMethodCall(method: __METHOD__);
}

public function withStatus(int $code, string $reasonPhrase = ''): ResponseInterface
{
throw new BadMethodCall(method: __METHOD__);
}

public function withHeader(string $name, mixed $value): MessageInterface
{
throw new BadMethodCall(method: __METHOD__);
}

public function withoutHeader(string $name): MessageInterface
{
throw new BadMethodCall(method: __METHOD__);
}

public function withAddedHeader(string $name, mixed $value): MessageInterface
{
throw new BadMethodCall(method: __METHOD__);
}

public function withProtocolVersion(string $version): MessageInterface
{
throw new BadMethodCall(method: __METHOD__);
}

public function getProtocolVersion(): string
{
return '1.1';
}

public function getHeaders(): array
{
return $this->headers;
}

public function hasHeader(string $name): bool
{
return isset($this->headers[$name]);
}

public function getHeader(string $name): array
{
return $this->headers[$name] ?? [];
}

public function getHeaderLine(string $name): string
{
return implode(', ', $this->getHeader(name: $name));
}

public function getBody(): StreamInterface
{
return $this->body;
}

public function getStatusCode(): int
{
return $this->code->value;
}

public function getReasonPhrase(): string
{
return $this->code->message();
}
}
Loading

0 comments on commit 55850de

Please sign in to comment.