infocyph / webrick
A fast, modern PHP router with production-grade middleware, signed & temporary URLs, smart responses and first-class route caching.
2.0
2026-06-02 09:55 UTC
Requires
- php: >=8.4
- infocyph/arraykit: ^4.2
- infocyph/cachelayer: ^1.2
- infocyph/intermix: ^7.2
- psr/log: ^3.0.2
Requires (Dev)
- infocyph/phpforge: dev-main
Suggests
- open-telemetry/exporter-otlp: Export traces to OpenTelemetry collectors (Jaeger, Zipkin, etc.)
- open-telemetry/sdk: Full OpenTelemetry tracing with automatic span export (auto-detected by TelemetryMiddleware)
- psr/http-factory: PSR-17 HTTP factories for framework interoperability
- psr/http-server-middleware: PSR-15 middleware support
This package is auto-updated.
Last update: 2026-06-03 11:27:58 UTC
README
A modern PHP router with route caching, signed URLs, production middleware, and response helpers.
Highlights
- Fast routing: named routes, groups, domains, resources, attribute discovery
- Signed URLs: permanent, TTL-based, or explicit-expiry links
- Rich signing controls: relative or absolute payloads, ignored query params, key rotation, custom signature params and algorithms
- Response helpers: JSON, plaintext, redirects, streaming, ranged file/download responses, views
- Route caching: sharded, fused, or generated matchers
- Middleware pipeline: negotiation, compression, throttling, validators, telemetry, cookie encryption, and more
Requirements
- PHP 8.4+
- Composer 2.x
Installation
composer require infocyph/webrick
Minimal Boot Example
<?php declare(strict_types=1); use Infocyph\Webrick\Request\Request; use Infocyph\Webrick\Response\Emitter\AutoEmitter; use Infocyph\Webrick\Response\Response; use Infocyph\Webrick\Router\Definition\Registrar; use Infocyph\Webrick\Router\Facade\Router as Route; use Infocyph\Webrick\Router\Kernel\RouterKernel; use Infocyph\Webrick\Router\Matching\ShardedMatcher; use Psr\Log\NullLogger; require __DIR__ . '/vendor/autoload.php'; $kernel = RouterKernel::bootWithRegistrar( log: new NullLogger(), matcher: ShardedMatcher::make(), register: static function (Registrar $registrar): void { unset($registrar); Route::get('/', fn() => Response::plaintext('Hello Webrick', 200), 'home'); Route::get('/api/users/{id:int}', fn(Request $request, string $id) => Response::json([ 'id' => (int) $id, 'method' => $request->getMethod(), ]), 'users.show'); }, routeCache: __DIR__ . '/.route-cache', ); (new AutoEmitter())->emit($kernel->handle(Request::fromGlobals()));
Production-Oriented Boot Example
<?php declare(strict_types=1); use Infocyph\Webrick\Middleware\CompressionMiddleware; use Infocyph\Webrick\Middleware\GatewayHardeningMiddleware; use Infocyph\Webrick\Middleware\NegotiationMiddleware; use Infocyph\Webrick\Middleware\ThrottleMiddleware; use Infocyph\Webrick\Middleware\VerifySignedUrlMiddleware; use Infocyph\Webrick\Request\Request; use Infocyph\Webrick\Response\Emitter\AutoEmitter; use Infocyph\Webrick\Response\Response; use Infocyph\Webrick\Router\Definition\Registrar; use Infocyph\Webrick\Router\Dispatch\MiddlewareAliases; use Infocyph\Webrick\Router\Facade\Router as Route; use Infocyph\Webrick\Router\Kernel\RouterKernel; use Infocyph\Webrick\Router\Matching\ShardedMatcher; use Infocyph\Webrick\Router\Route\Collection; use Infocyph\Webrick\Router\Url\SignedUrlConfig; use Psr\Log\NullLogger; require __DIR__ . '/vendor/autoload.php'; $signKey = $_ENV['WEBRICK_SIGN_KEY'] ?? 'change-me'; $baseUri = $_ENV['WEBRICK_URL_BASE_URI'] ?? 'http://localhost'; $signedUrls = new SignedUrlConfig( generationKey: $signKey, verificationKeys: [$signKey], defaultTtl: 900, ); MiddlewareAliases::register( 'throttle', static fn(...$params) => new ThrottleMiddleware( max: (int) ($params[0] ?? 60), window: (int) ($params[1] ?? 60), ), ); MiddlewareAliases::register( 'verifySignedUrl', static fn() => new VerifySignedUrlMiddleware($signKey, 5), ); $kernel = RouterKernel::bootWithRegistrar( log: new NullLogger(), matcher: ShardedMatcher::make(), register: static function (Registrar $registrar): void { unset($registrar); Route::get('/users/{id:int}', fn(string $id) => Response::json(['id' => (int) $id]), 'users.show'); Route::get('/files/{file}', fn(string $file) => Response::attachment(__DIR__ . '/files/' . $file, $file), [ 'as' => 'files.show', 'middleware' => ['verifySignedUrl'], ]); }, routeCache: __DIR__ . '/.route-cache', registrarOptions: [ 'exposeUrlServices' => true, 'signKey' => $signKey, 'signedDefaultTtl' => 900, 'signedUrlConfig' => $signedUrls, 'urlBaseUri' => $baseUri, ], preGlobal: [ GatewayHardeningMiddleware::class, NegotiationMiddleware::class, ], postGlobal: [ CompressionMiddleware::class, ], bindUrlServices: static function (Collection $routes) use ($signKey, $signedUrls, $baseUri): void { Route::bindUrlServices($routes, $signKey, 900, $signedUrls, $baseUri); }, fallbackAliasesFromRegistrar: true, ); (new AutoEmitter())->emit($kernel->handle(Request::fromGlobals()));
URL Generation
use DateTimeImmutable; use Infocyph\Webrick\Router\Facade\Router as Route; use Infocyph\Webrick\Router\Url\SignedUrlConfig; $url = Route::urlFor('users.show', ['id' => 42]); $absolute = Route::urlFor('users.show', ['id' => 42], absolute: true); $signed = Route::signedUrlFor('files.show', ['file' => 'report.pdf']); $temp = Route::temporaryUrlFor('files.show', ['file' => 'report.pdf'], ttl: 900); $until = Route::temporaryUrlUntil('files.show', new DateTimeImmutable('+15 minutes'), ['file' => 'report.pdf']); $absolutePayload = Route::signedUrlFor( 'files.show', ['file' => 'report.pdf'], absolute: true, payloadMode: SignedUrlConfig::MODE_ABSOLUTE, );
Route Cache
Build cache artifacts during CI or deploy:
php ./webrick route:cache --matcher=sharded --cache=.route-cache --routes=routes.php
Clear them when needed:
php ./webrick route:clear --matcher=sharded --cache=.route-cache
Security
Protected by PHPForge — an automated quality and security gate for PHP projects.
Made with ❤️ for the PHP communityMIT Licensed
Documentation • Security • Code of Conduct • Contributing • Report | Request | Suggest