TrueAsync\StaticHandler
(PHP 8.6+, true_async_server 0.6+)
Handler integrado de archivos estáticos (issue #13). Una instancia = un prefix-mount. Se acopla al servidor mediante HttpServer::addStaticHandler().
Totalmente en C: las solicitudes no spawnean corrutinas ni entran a la VM de PHP. Los archivos se entregan mediante operaciones async de filesystem de libuv directamente al response-stream.
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 precomprimidos
public function enablePrecompressed(string ...$encodings): static;
public function disablePrecompressed(): static;
// seguridad
public function setDotfilePolicy(StaticDotfiles $policy): static;
public function setSymlinkPolicy(StaticSymlinks $policy): static;
public function hide(string ...$globs): static;
// caché / cabeceras
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;
// introspección
public function getUrlPrefix(): string;
public function getRootDirectory(): string;
public function isLocked(): bool;
}Constructor
__construct
public StaticHandler::__construct(string $urlPrefix, string $rootDirectory)| Parámetro | Requisitos |
|---|---|
$urlPrefix | Prefijo URL. Debe empezar y terminar por /. Ejemplo: "/static/". |
$rootDirectory | Ruta absoluta del directorio en disco; se canonicaliza al acoplar. |
Index / fallthrough
setIndexFiles
public StaticHandler::setIndexFiles(string ...$files): staticNombres de archivos a entregar ante una solicitud sobre una URL de directorio. Default ["index.html"]. Una lista vacía desactiva el lookup de index.
disableIndex
public StaticHandler::disableIndex(): staticEquivalente a setIndexFiles().
setOnMissing
public StaticHandler::setOnMissing(StaticOnMissing $mode): staticQué hacer si la ruta solicitada no resuelve a un archivo normal dentro de root:
| Valor | Comportamiento |
|---|---|
StaticOnMissing::NOT_FOUND (default) | 404 en C, la solicitud no entra a la VM de PHP |
StaticOnMissing::NEXT | El control vuelve al dispatcher, se spawnea el manejador-corrutina habitual: la solicitud entra a addHttpHandler() |
Sidecars precomprimidos
enablePrecompressed
public StaticHandler::enablePrecompressed(string ...$encodings): staticActiva la entrega de sidecars precomprimidos (main.css.br, main.css.gz, main.css.zst) cuando el cliente lo permite mediante Accept-Encoding. Argumentos: nombres de content-coding "br", "gzip", "zstd". Desconocidos: InvalidArgumentException en el setter.
disablePrecompressed
public StaticHandler::disablePrecompressed(): staticSeguridad
setDotfilePolicy
public StaticHandler::setDotfilePolicy(StaticDotfiles $policy): static"Dotfile" es cualquier path-segment que empieza por ., incluido .. (este último siempre lo rechaza el guard contra traversal, sea cual sea la política).
| Comportamiento | |
|---|---|
StaticDotfiles::DENY (default) | 404 ante cualquier ruta con un dotfile-componente |
StaticDotfiles::ALLOW | los dotfiles se sirven como archivos normales |
StaticDotfiles::IGNORE | como si el archivo no existiera (passthrough según StaticOnMissing) |
setSymlinkPolicy
public StaticHandler::setSymlinkPolicy(StaticSymlinks $policy): static| Comportamiento | |
|---|---|
StaticSymlinks::REJECT (default) | 404 ante cualquier symlink en la ruta. O_NOFOLLOW + lstat por segmento, el symlink nunca se atraviesa |
StaticSymlinks::FOLLOW | los symlinks se siguen; el target post realpath() debe permanecer dentro de root |
StaticSymlinks::OWNER_MATCH | se sigue solo si symlink y target pertenecen al mismo uid |
hide
public StaticHandler::hide(string ...$globs): staticPatrones glob: las rutas que coincidan devuelven 404 con independencia de su existencia. Comparación relativa a root, separador /.
Caché / cabeceras
setEtagEnabled
public StaticHandler::setEtagEnabled(bool $enabled): staticToggle weak ETag (default true). Si está activo, cada 200 lleva ETag: W/"…" derivado de (mtime_ns, size, ino); If-None-Match / If-Modified-Since dan 304.
setCacheControl
public StaticHandler::setCacheControl(string $value): staticCache-Control literal. Cadena vacía: no se emite.
setOpenFileCache
public StaticHandler::setOpenFileCache(int $maxEntries, int $ttlSeconds = 60): staticOpen-file cache estilo nginx: guarda la ruta resuelta, los metadatos fstat, MIME, ETag y Last-Modified de las últimas N solicitudes. Dentro de ttlSeconds las solicitudes repetidas golpean la caché y se saltan realpath/stat/MIME-walk.
Desactivada por defecto. Aporta valor con dentry frío / docroot grande / sistemas de archivos en red. Con dentry caliente en disco local las syscalls ya están por debajo de µs y el overhead del lookup HashTable se come la ganancia.
$maxEntries == 0 la desactiva.
disableOpenFileCache
public StaticHandler::disableOpenFileCache(): staticAzúcar para setOpenFileCache(0).
setHeader
public StaticHandler::setHeader(string $name, string $value): staticCabecera fija, evaluada una vez al acoplar. Se emite en cada 200 y en cada 304 (salvo las cabeceras Content-* según RFC 9110 §15.4.5).
Directory listing
setBrowseEnabled
public StaticHandler::setBrowseEnabled(bool $enabled): staticToggle del listing HTML ante una solicitud sobre un directorio sin index. Default false.
Reservado para el PR #6: por ahora no-op, se acepta en el setter sin efecto.
MIME
setMimeType
public StaticHandler::setMimeType(string $extension, string $contentType): staticOverride del Content-Type para archivos con la extensión indicada. Extensión en minúsculas, sin punto inicial.
Introspección
getUrlPrefix / getRootDirectory
public StaticHandler::getUrlPrefix(): string
public StaticHandler::getRootDirectory(): stringisLocked
public StaticHandler::isLocked(): booltrue tras acoplar al servidor mediante addStaticHandler(). Un handler bloqueado rechaza todos los setters con una runtime-exception.
Enums
Véanse las páginas dedicadas:
(Los tres son enum: int bajo el namespace TrueAsync.)
Ejemplo
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);