TrueAsync\StaticHandler
(PHP 8.6+, true_async_server 0.6+)
Handler intégré de fichiers statiques (issue #13). Une instance = un prefix-mount. S'attache au serveur via HttpServer::addStaticHandler().
Entièrement en C : les requêtes ne spawn pas de coroutine et n'entrent pas dans la VM PHP — les fichiers sont envoyés via les async fs ops libuv directement dans le stream de la réponse.
namespace TrueAsync;
final class StaticHandler
{
public function __construct(string $urlPrefix, string $rootDirectory);
// index / fallthrough
public function setIndexFiles(string ...$files): static;
public function disableIndex(): static;
public function setOnMissing(StaticOnMissing $mode): static;
// sidecars précompressés
public function enablePrecompressed(string ...$encodings): static;
public function disablePrecompressed(): static;
// sécurité
public function setDotfilePolicy(StaticDotfiles $policy): static;
public function setSymlinkPolicy(StaticSymlinks $policy): static;
public function hide(string ...$globs): static;
// cache / en-têtes
public function setEtagEnabled(bool $enabled): static;
public function setCacheControl(string $value): static;
public function setOpenFileCache(int $maxEntries, int $ttlSeconds = 60): static;
public function disableOpenFileCache(): static;
public function setHeader(string $name, string $value): static;
// directory listing
public function setBrowseEnabled(bool $enabled): static;
// MIME
public function setMimeType(string $extension, string $contentType): static;
// introspection
public function getUrlPrefix(): string;
public function getRootDirectory(): string;
public function isLocked(): bool;
}Constructeur
__construct
public StaticHandler::__construct(string $urlPrefix, string $rootDirectory)| Paramètre | Exigences |
|---|---|
$urlPrefix | Préfixe URL. Doit commencer et se terminer par /. Exemple : "/static/". |
$rootDirectory | Chemin absolu vers un répertoire disque ; canonicalisé à attach-time. |
Index / fallthrough
setIndexFiles
public StaticHandler::setIndexFiles(string ...$files): staticNoms de fichiers servis pour une requête sur une URL répertoire. Défaut ["index.html"]. Liste vide : désactive le lookup d'index.
disableIndex
public StaticHandler::disableIndex(): staticÉquivalent de setIndexFiles() sans arguments.
setOnMissing
public StaticHandler::setOnMissing(StaticOnMissing $mode): staticQuoi faire si le chemin demandé ne résout pas vers un fichier régulier sous root :
| Valeur | Comportement |
|---|---|
StaticOnMissing::NOT_FOUND (défaut) | 404 en C, la requête n'entre pas dans la VM PHP |
StaticOnMissing::NEXT | Le contrôle est rendu au dispatcher, un handler-coroutine normal est spawné — la requête passe à addHttpHandler() |
Sidecars précompressés
enablePrecompressed
public StaticHandler::enablePrecompressed(string ...$encodings): staticActive la livraison de sidecars précompressés (main.css.br, main.css.gz, main.css.zst), quand le client le permet via Accept-Encoding. Arguments : noms de content-coding "br", "gzip", "zstd". Inconnus : InvalidArgumentException au setter.
disablePrecompressed
public StaticHandler::disablePrecompressed(): staticSécurité
setDotfilePolicy
public StaticHandler::setDotfilePolicy(StaticDotfiles $policy): static"Dotfile" : tout path-segment commençant par ., y compris .. (ce dernier est toujours rejeté par le traversal guard, indépendamment de la politique).
| Comportement | |
|---|---|
StaticDotfiles::DENY (défaut) | 404 sur tout chemin avec composant dotfile |
StaticDotfiles::ALLOW | les dotfiles sont servis comme des fichiers normaux |
StaticDotfiles::IGNORE | comme si le fichier n'existait pas (passthrough selon StaticOnMissing) |
setSymlinkPolicy
public StaticHandler::setSymlinkPolicy(StaticSymlinks $policy): static| Comportement | |
|---|---|
StaticSymlinks::REJECT (défaut) | 404 sur tout symlink dans le chemin. O_NOFOLLOW + lstat par segment — le symlink n'est jamais traversé |
StaticSymlinks::FOLLOW | symlinks suivis ; après realpath(), la cible doit rester sous root |
StaticSymlinks::OWNER_MATCH | follow uniquement si symlink et cible appartiennent au même uid |
hide
public StaticHandler::hide(string ...$globs): staticPatterns glob : les paths matchés renvoient 404 indépendamment de l'existence. Comparaison relative à root, séparateur /.
Cache / en-têtes
setEtagEnabled
public StaticHandler::setEtagEnabled(bool $enabled): staticToggle weak ETag (défaut true). À l'activation, chaque 200 porte ETag: W/"…" calculé depuis (mtime_ns, size, ino) ; If-None-Match / If-Modified-Since donnent 304.
setCacheControl
public StaticHandler::setCacheControl(string $value): staticCache-Control littéral. Chaîne vide : supprime l'émission.
setOpenFileCache
public StaticHandler::setOpenFileCache(int $maxEntries, int $ttlSeconds = 60): staticOpen-file cache style nginx : stocke le chemin résolu, les métadonnées fstat, le MIME, l'ETag et Last-Modified pour les N dernières requêtes. Dans la fenêtre ttlSeconds, les requêtes répétées hitent le cache et sautent les passes realpath/stat/MIME.
Désactivé par défaut. Gagne sur cold-dentry / docroot volumineux / FS réseau. Sur warm-dentry de disque local, les syscalls sont déjà sous µs — le surcoût du HashTable-lookup mange le gain.
$maxEntries == 0 : désactiver.
disableOpenFileCache
public StaticHandler::disableOpenFileCache(): staticSucre pour setOpenFileCache(0).
setHeader
public StaticHandler::setHeader(string $name, string $value): staticEn-tête fixe, évalué une seule fois à attach-time. Émis sur chaque 200 et sur 304 (sauf les en-têtes Content-* selon RFC 9110 §15.4.5).
Directory listing
setBrowseEnabled
public StaticHandler::setBrowseEnabled(bool $enabled): staticToggle du listing HTML pour une requête sur un répertoire sans index. Défaut false.
Réservé sous PR #6 — actuellement no-op, accepté au setter sans effet.
MIME
setMimeType
public StaticHandler::setMimeType(string $extension, string $contentType): staticOverride du Content-Type pour les fichiers ayant l'extension donnée. Extension en minuscules, sans dot initial.
Introspection
getUrlPrefix / getRootDirectory
public StaticHandler::getUrlPrefix(): string
public StaticHandler::getRootDirectory(): stringisLocked
public StaticHandler::isLocked(): booltrue après attach au serveur via addStaticHandler(). Un handler locked refuse tous les setters avec une runtime-exception.
Enums
Voir pages séparées :
(Les trois sont enum: int sous namespace TrueAsync.)
Exemple
use TrueAsync\StaticHandler;
use TrueAsync\StaticOnMissing;
use TrueAsync\StaticDotfiles;
$static = (new StaticHandler('/static/', '/var/www/public'))
->setIndexFiles('index.html', 'index.htm')
->enablePrecompressed('br', 'gzip')
->setOnMissing(StaticOnMissing::NEXT)
->setDotfilePolicy(StaticDotfiles::DENY)
->setCacheControl('public, max-age=31536000, immutable')
->setEtagEnabled(true)
->setOpenFileCache(maxEntries: 1024, ttlSeconds: 60)
->setHeader('Strict-Transport-Security', 'max-age=63072000')
->hide('*.bak', '*.tmp', 'private/**');
$server->addStaticHandler($static);