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