feat: add Request and Response value objects
This commit is contained in:
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace BattleForge\Http;
|
||||||
|
|
||||||
|
final readonly class Request
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $get
|
||||||
|
* @param array<string, mixed> $post
|
||||||
|
* @param array<string, array<string, mixed>> $files
|
||||||
|
* @param array<string, string> $cookies
|
||||||
|
* @param array<string, string> $server
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
public array $get,
|
||||||
|
public array $post,
|
||||||
|
public array $files,
|
||||||
|
public array $cookies,
|
||||||
|
public array $server,
|
||||||
|
public string $queryString,
|
||||||
|
public string $method,
|
||||||
|
public string $path,
|
||||||
|
public ?string $contentType,
|
||||||
|
public string $rawBody,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace BattleForge\Http;
|
||||||
|
|
||||||
|
final class Response
|
||||||
|
{
|
||||||
|
private const SECURITY_HEADERS = [
|
||||||
|
'X-Content-Type-Options' => 'nosniff',
|
||||||
|
'Referrer-Policy' => 'same-origin',
|
||||||
|
'Content-Security-Policy' => "default-src 'self'; img-src 'self' data:; style-src 'self'",
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @param array<string, string> $headers */
|
||||||
|
public function __construct(
|
||||||
|
public int $status,
|
||||||
|
public array $headers,
|
||||||
|
public string $body,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function html(int $status, string $body): self
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
$status,
|
||||||
|
['Content-Type' => 'text/html; charset=utf-8'] + self::SECURITY_HEADERS,
|
||||||
|
$body,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function json(int $status, mixed $body): self
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
$status,
|
||||||
|
['Content-Type' => 'application/json; charset=utf-8'] + self::SECURITY_HEADERS,
|
||||||
|
json_encode($body, JSON_THROW_ON_ERROR),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function redirect(string $location, int $status = 303): self
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
$status,
|
||||||
|
['Location' => $location] + self::SECURITY_HEADERS,
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace BattleForge\Tests\Unit\Http;
|
||||||
|
|
||||||
|
use BattleForge\Http\Request;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
final class RequestTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testItExposesAllSuperglobalsAsConstructorArgs(): void
|
||||||
|
{
|
||||||
|
$request = new Request(
|
||||||
|
get: ['q' => '1'],
|
||||||
|
post: ['name' => 'alpha'],
|
||||||
|
files: ['image' => ['name' => 'a.png', 'tmp_name' => '/tmp/x', 'error' => 0, 'size' => 12, 'type' => 'image/png']],
|
||||||
|
cookies: ['__csrf' => 'cookie-value'],
|
||||||
|
server: ['HTTP_HOST' => 'localhost'],
|
||||||
|
queryString: 'q=1',
|
||||||
|
method: 'POST',
|
||||||
|
path: '/scenarios/demo/edit/team',
|
||||||
|
contentType: 'application/x-www-form-urlencoded',
|
||||||
|
rawBody: '',
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertSame(['q' => '1'], $request->get);
|
||||||
|
self::assertSame(['name' => 'alpha'], $request->post);
|
||||||
|
self::assertSame('a.png', $request->files['image']['name']);
|
||||||
|
self::assertSame('cookie-value', $request->cookies['__csrf']);
|
||||||
|
self::assertSame('localhost', $request->server['HTTP_HOST']);
|
||||||
|
self::assertSame('q=1', $request->queryString);
|
||||||
|
self::assertSame('POST', $request->method);
|
||||||
|
self::assertSame('/scenarios/demo/edit/team', $request->path);
|
||||||
|
self::assertSame('application/x-www-form-urlencoded', $request->contentType);
|
||||||
|
self::assertSame('', $request->rawBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testItAllowsNullContentType(): void
|
||||||
|
{
|
||||||
|
$request = new Request(
|
||||||
|
get: [],
|
||||||
|
post: [],
|
||||||
|
files: [],
|
||||||
|
cookies: [],
|
||||||
|
server: [],
|
||||||
|
queryString: '',
|
||||||
|
method: 'GET',
|
||||||
|
path: '/',
|
||||||
|
contentType: null,
|
||||||
|
rawBody: '',
|
||||||
|
);
|
||||||
|
|
||||||
|
self::assertNull($request->contentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user