Files
KantSites/bin/dev-router.php
T
kantarciandClaude Fable 5.2 7d0369bada Serve images without PHP, add the 720 and 1280 steps, trim two hot paths
- Image widths are 480, 720, 960, 1280, 1600 and 2400. A phone needed
  721 pixels and got 960. Existing images receive the new steps from
  a task; stored pages pick them up at render time, so no document is
  rewritten. Mobile backgrounds use 720.
- A variant served once is mirrored to public/media, and a rewrite rule
  lets the web server deliver it from there. var/media stays the source.
- The hero preload follows the image each device really shows.
- The post navigation reads the order and then two posts instead of
  up to 500 documents.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 12:24:30 +01:00

60 lines
3.0 KiB
PHP

<?php
/*
* Router for the PHP built-in server only.
*
* The built-in server serves existing files itself and answers 404 for any missing path that looks
* like a file, so /sitemap.xml, /robots.txt, /llms.txt and /feed.xml never reach the front
* controller. Real hosting rewrites everything that is not a file to public/index.php, which is
* what public/.htaccess does. This script reproduces that behaviour for local development.
*/
$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
// Wie die Regel in public/.htaccess: Ein Bild der Mediathek, das im Abbild public/media/ liegt,
// geht ohne die Anwendung raus (ADR 129). Ohne das maesse man hier weiter den Weg ueber PHP.
if (1 === preg_match('#^/media/[a-f0-9]{32}/[0-9]{1,4}$#D', $path) && is_file(__DIR__.'/../public'.$path.'.webp')) {
header('Content-Type: image/webp');
header('Cache-Control: public, max-age=31536000, immutable');
header('X-Content-Type-Options: nosniff');
header('Content-Length: '.filesize(__DIR__.'/../public'.$path.'.webp'));
readfile(__DIR__.'/../public'.$path.'.webp');
exit;
}
$file = __DIR__.'/../public'.$path;
if ('/' !== $path && !str_contains($path, '..') && is_file($file)) {
// Dieselben Antwortkoepfe wie public/.htaccess sie auf einem Webserver setzt: Stylesheets,
// Skripte, Schriften und Bilder tragen ihre Fassung oder ihre Kennung in der Adresse und
// duerfen deshalb lange im Browser bleiben. Ohne das misst jede Messung hier etwas anderes
// als das, was spaeter ausgeliefert wird - und man optimiert an der falschen Stelle.
if (1 === preg_match('/\.(css|js|woff2|webp|png|jpe?g|gif|svg)$/', $path, $treffer) && !str_starts_with($path, '/assets/seite/')) {
header('Content-Type: '.match ($treffer[1]) {
'css' => 'text/css; charset=UTF-8',
'js' => 'text/javascript; charset=UTF-8',
'woff2' => 'font/woff2',
'svg' => 'image/svg+xml; charset=UTF-8',
'jpg', 'jpeg' => 'image/jpeg',
default => 'image/'.$treffer[1],
});
header('Cache-Control: public, max-age=31536000, immutable');
// Und ebenso die Komprimierung, die mod_deflate auf einem Webserver uebernimmt: ohne sie
// misst man hier eine Stylesheetgroesse, die es im Betrieb nie gibt.
$inhalt = (string) file_get_contents($file);
if (in_array($treffer[1], ['css', 'js', 'svg'], true) && str_contains($_SERVER['HTTP_ACCEPT_ENCODING'] ?? '', 'gzip')) {
$inhalt = (string) gzencode($inhalt, 6);
header('Content-Encoding: gzip');
header('Vary: Accept-Encoding');
}
header('Content-Length: '.strlen($inhalt));
echo $inhalt;
exit;
}
return false; // Let the built-in server deliver the static file.
}
// Auch die Seiten selbst gehen komprimiert raus, wie auf einem Webserver mit mod_deflate.
ini_set('zlib.output_compression', '1');
ini_set('zlib.output_compression_level', '6');
require __DIR__.'/../public/index.php';