Setzt Phase 1 aus Visualization.md um: Vanilla-PHP-REST-API mit SQLite-Persistenz, statischen API-Keys pro Agent, Codeberg-Sync fuer Backlog/Done, PHPUnit-Tests und vollstaendiger API.md-Dokumentation - Vorgaben aus Detailantworten.md (F1-F16) umgesetzt. Entwicklung/Build/Test laufen komplett in Docker-Compose-Containern (PHP 8.2 + SQLite + Composer + PHPUnit/pcov), analog zum NC-Gantt-Projekt ist auf dem Host kein PHP/Composer noetig. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
68 lines
2 KiB
PHP
68 lines
2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Monitor\Api\Http;
|
|
|
|
final class Request
|
|
{
|
|
/** @param array<string, string> $headers */
|
|
private function __construct(
|
|
public readonly string $method,
|
|
public readonly string $path,
|
|
public readonly array $query,
|
|
public readonly array $body,
|
|
private readonly array $headers,
|
|
) {
|
|
}
|
|
|
|
public static function fromGlobals(): self
|
|
{
|
|
$method = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET');
|
|
$uri = $_SERVER['REQUEST_URI'] ?? '/';
|
|
$path = rtrim((string) parse_url($uri, PHP_URL_PATH), '/');
|
|
if ($path === '') {
|
|
$path = '/';
|
|
}
|
|
|
|
parse_str((string) (parse_url($uri, PHP_URL_QUERY) ?? ''), $query);
|
|
|
|
$rawBody = file_get_contents('php://input') ?: '';
|
|
$body = [];
|
|
if ($rawBody !== '') {
|
|
$decoded = json_decode($rawBody, true);
|
|
if (is_array($decoded)) {
|
|
$body = $decoded;
|
|
}
|
|
}
|
|
|
|
$headers = [];
|
|
foreach ($_SERVER as $key => $value) {
|
|
if (str_starts_with($key, 'HTTP_')) {
|
|
$name = str_replace('_', '-', strtolower(substr($key, 5)));
|
|
$headers[$name] = (string) $value;
|
|
}
|
|
}
|
|
if (isset($_SERVER['CONTENT_TYPE'])) {
|
|
$headers['content-type'] = (string) $_SERVER['CONTENT_TYPE'];
|
|
}
|
|
|
|
return new self($method, $path, $query, $body, $headers);
|
|
}
|
|
|
|
/** @param array<string, mixed> $body */
|
|
public static function create(string $method, string $path, array $query = [], array $body = [], array $headers = []): self
|
|
{
|
|
$normalized = [];
|
|
foreach ($headers as $name => $value) {
|
|
$normalized[strtolower($name)] = $value;
|
|
}
|
|
|
|
return new self($method, rtrim($path, '/') ?: '/', $query, $body, $normalized);
|
|
}
|
|
|
|
public function header(string $name): ?string
|
|
{
|
|
return $this->headers[strtolower($name)] ?? null;
|
|
}
|
|
}
|