<?php
declare(strict_types=1);
namespace Machweis\Backend;
final readonly class Request
{
/** @param array<string, string> $headers */
public function __construct(public string $method, public string $path, public array $headers, public string $body = '') {}
public static function fromGlobals(): self
{
$normalized = [];
foreach (self::nativeHeaders() as $name => $value) {
if (is_string($name) && is_string($value)) $normalized[strtolower($name)] = $value;
}
if (!isset($normalized['authorization'])) {
$authorization = self::authorizationFromEnvironment();
if ($authorization !== null) $normalized['authorization'] = $authorization;
}
$uri = $_SERVER['REQUEST_URI'] ?? '/';
return new self($_SERVER['REQUEST_METHOD'] ?? 'GET', parse_url($uri, PHP_URL_PATH) ?: '/', $normalized, file_get_contents('php://input') ?: '');
}
/** @return array<array-key, mixed> */
private static function nativeHeaders(): array
{
if (function_exists('getallheaders')) {
$headers = getallheaders();
if (is_array($headers)) return $headers;
}
if (function_exists('apache_request_headers')) {
$headers = apache_request_headers();
if (is_array($headers)) return $headers;
}
return [];
}
private static function authorizationFromEnvironment(): ?string
{
$candidates = [
$_SERVER['HTTP_AUTHORIZATION'] ?? null,
$_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? null,
$_SERVER['Authorization'] ?? null,
];
foreach ($_SERVER as $name => $value) {
if (is_string($name) && preg_match('/^(?:REDIRECT_)+HTTP_AUTHORIZATION$/', $name) === 1) $candidates[] = $value;
}
foreach (['HTTP_AUTHORIZATION', 'REDIRECT_HTTP_AUTHORIZATION', 'REDIRECT_REDIRECT_HTTP_AUTHORIZATION'] as $name) {
$candidates[] = getenv($name);
}
foreach ($candidates as $candidate) {
if (!is_string($candidate)) continue;
$candidate = trim($candidate);
if ($candidate !== '') return $candidate;
}
return null;
}
public function header(string $name): ?string { return $this->headers[strtolower($name)] ?? null; }
public function cookie(string $name): ?string { foreach (explode(';', $this->header('cookie') ?? '') as $part) { $pair = explode('=', trim($part), 2); if (count($pair) === 2 && hash_equals($name, trim($pair[0]))) return rawurldecode(trim($pair[1])); } return null; }
public function query(string $key, string $default = ''): string { parse_str(parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_QUERY) ?? '', $query); return isset($query[$key]) && is_scalar($query[$key]) ? (string) $query[$key] : $default; }
/** @return array<string, mixed> */
public function json(): array { $value = json_decode($this->body, true); return is_array($value) ? $value : []; }
}
