infocyph/webrick

A fast, modern PHP router with production-grade middleware, signed & temporary URLs, smart responses and first-class route caching.

Maintainers

Package info

github.com/infocyph/Webrick

pkg:composer/infocyph/webrick

Statistics

Installs: 1 550

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

2.0 2026-06-02 09:55 UTC

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.

Security & Standards Packagist Downloads License: MIT Packagist Version Packagist PHP Version GitHub Code Size Documentation

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 community
MIT Licensed
DocumentationSecurityCode of ConductContributingReport | Request | Suggest