Quelltext des aktuellen Standes
<?php
declare(strict_types=1);
use OpenNIT\Core\Response;
/**
* Haelt fest, dass der HTTP-Status die Ausgabe ueberlebt.
*
* Fehler- und Ablehnungsseiten setzen ihren Code vor dem Rendern; wuerde die
* Ausgabe ihn auf 200 zuruecksetzen, koennte ein Aufrufer eine abgelehnte oder
* nicht gefundene Antwort nur noch am Seiteninhalt erkennen.
*/
return static function (): void {
/** Liest den gesetzten Status aus der Response. */
$status = static function (Response $r): int {
$p = (new ReflectionProperty(Response::class, 'statusCode'));
$p->setAccessible(true);
return (int)$p->getValue($r);
};
/**
* Fuehrt die Ausgabe aus, ohne Rumpf und header()-Warnungen in die
* Testausgabe zu spuelen (in der CLI ist der Header-Versand nicht moeglich).
*/
$stillAusgeben = static function (callable $fn): void {
ob_start();
try {
@$fn();
} finally {
ob_end_clean();
}
};
test('Eine HTML-Ausgabe behaelt den zuvor gesetzten Status', function () use ($status, $stillAusgeben) {
$r = new Response();
@$r->setStatusCode(404);
$stillAusgeben(static fn () => $r->html('<p>nicht gefunden</p>'));
assertSame(404, $status($r));
});
test('Eine JSON-Ausgabe behaelt den zuvor gesetzten Status', function () use ($status, $stillAusgeben) {
// Betrifft die Abweisungen im Router: Ratenbegrenzung, fehlende
// Anmeldung und erzwungene Zwei-Faktor-Einrichtung.
foreach ([429, 401, 403] as $code) {
$r = new Response();
@$r->setStatusCode($code);
$stillAusgeben(static fn () => $r->json(['error' => 'abgelehnt']));
assertSame($code, $status($r));
}
});
test('Ohne Angabe gilt weiterhin 200', function () use ($status, $stillAusgeben) {
$r = new Response();
$stillAusgeben(static fn () => $r->html('<p>ok</p>'));
assertSame(200, $status($r));
$r2 = new Response();
$stillAusgeben(static fn () => $r2->json(['ok' => true]));
assertSame(200, $status($r2));
});
test('Ein ausdruecklich uebergebener Status gewinnt', function () use ($status, $stillAusgeben) {
$r = new Response();
@$r->setStatusCode(404);
$stillAusgeben(static fn () => $r->html('<p>doch ok</p>', 200));
assertSame(200, $status($r));
$r2 = new Response();
$stillAusgeben(static fn () => $r2->json(['error' => 'weg'], 410));
assertSame(410, $status($r2));
});
test('Der Rumpf bleibt unveraendert', function () {
// Die Statusbehandlung darf an der Ausgabe selbst nichts aendern.
$r = new Response();
@$r->setStatusCode(403);
ob_start();
try {
@$r->html('<p>verboten</p>');
$rumpf = (string)ob_get_contents();
} finally {
ob_end_clean();
}
assertSame('<p>verboten</p>', $rumpf);
});
test('Fehlerseiten setzen ihren Status vor dem Rendern', function () {
// Der Status muss gesetzt sein, bevor der Rumpf die Kopfzeilen abschickt.
$code = (string)file_get_contents(dirname(__DIR__) . '/src/Controllers/DnsController.php');
assertTrue(
(bool)preg_match('/setStatusCode\(404\);\s*\n\s*\$this->render\(\$response, \'errors\/404\'/', $code),
'Reihenfolge von Status und Rendern vertauscht'
);
});
};