zero-to-prod/http-router

High-performance HTTP router for PHP with three-level indexing, PSR-15 middleware support, and route caching

Fund package maintenance!
Github

Installs: 401

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/zero-to-prod/http-router

v0.1.1 2025-12-09 00:00 UTC

This package is auto-updated.

Last update: 2025-12-09 13:53:09 UTC


README

Repo GitHub Actions Workflow Status Packagist Downloads php Packagist Version License

High-performance HTTP router for PHP 7.2+ with three-level indexing, PSR-15 middleware support, and route caching.

Features

  • High Performance: Three-level route indexing for O(1) static route lookup and optimized dynamic route matching
  • PSR-15 Middleware: Full support for PSR-15 middleware alongside legacy variadic middleware
  • Route Caching: Production-optimized serialization with automatic cache management
  • RESTful Resources: Automatic generation of resourceful routes
  • Route Groups: Organize routes with shared prefixes and middleware
  • Named Routes: Generate URLs from route names with parameter substitution
  • Parameter Constraints: Inline ({id:\d+}) and fluent (where()) constraint syntax
  • PHP 7.1+ Compatible: Broad multi-version support

Installation

composer require zero-to-prod/http-router

Quick Start

use Zerotoprod\HttpRouter\HttpRouter;

$router = HttpRouter::for('GET', '/users/123')
    ->get('/users/{id}', [UserController::class, 'show'])
    ->where('id', '\d+')
    ->name('user.show')
    ->middleware(AuthMiddleware::class);

$router->dispatch();

Basic Usage

Defining Routes

use Zerotoprod\HttpRouter\HttpRouter;

$router = HttpRouter::create();

// Static routes
$router->get('/', function() {
    echo 'Home';
});

// Dynamic routes with parameters
$router->get('/users/{id}', function(array $params) {
    echo "User ID: " . $params['id'];
});

// Routes with inline constraints
$router->get('/posts/{id:\d+}', [PostController::class, 'show']);

// Routes with fluent constraints
$router->get('/users/{id}', [UserController::class, 'show'])
    ->where('id', '\d+');

HTTP Methods

$router->get('/users', [UserController::class, 'index']);
$router->post('/users', [UserController::class, 'store']);
$router->put('/users/{id}', [UserController::class, 'update']);
$router->patch('/users/{id}', [UserController::class, 'patch']);
$router->delete('/users/{id}', [UserController::class, 'destroy']);
$router->options('/users', [UserController::class, 'options']);
$router->head('/users', [UserController::class, 'head']);

// Multiple methods
$router->any('/users/{id}', [UserController::class, 'handler'], ['GET', 'POST']);

RESTful Resources

// Generates all 7 RESTful routes
$router->resource('users', UserController::class);

// With filters
$router->resource('posts', PostController::class, ['only' => ['index', 'show']]);
$router->resource('comments', CommentController::class, ['except' => ['destroy']]);

Generated routes:

  • GET /usersindex()
  • GET /users/createcreate()
  • POST /usersstore()
  • GET /users/{id}show()
  • GET /users/{id}/editedit()
  • PUT /users/{id}update()
  • DELETE /users/{id}destroy()

Route Groups

// Prefix groups
$router->prefix('api/v1')->group(function($router) {
    $router->get('/users', [UserController::class, 'index']); // /api/v1/users
});

// Middleware groups
$router->middleware([AuthMiddleware::class])->group(function($router) {
    $router->get('/dashboard', [DashboardController::class, 'index']);
});

// Combined
$router->prefix('admin')
    ->middleware([AuthMiddleware::class, AdminMiddleware::class])
    ->group(function($router) {
        $router->get('/users', [AdminController::class, 'users']);
    });

Named Routes

// Define named route
$router->get('/users/{id}', [UserController::class, 'show'])
    ->name('user.show');

// Generate URL
$url = $router->route('user.show', ['id' => 123]); // /users/123

Middleware

PSR-15 Middleware

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

class AuthMiddleware implements MiddlewareInterface
{
    public function process(
        ServerRequestInterface $request, 
        RequestHandlerInterface $handler
    ): ResponseInterface {
        // Check authentication
        return $handler->handle($request);
    }
}

$router->get('/dashboard', [DashboardController::class, 'index'])
    ->middleware(AuthMiddleware::class);

Variadic Middleware

class LogMiddleware
{
    public function __invoke(callable $next, ...$context)
    {
        // Before request
        error_log('Request started');
        
        $next();
        
        // After request
        error_log('Request completed');
    }
}

$router->get('/users', [UserController::class, 'index'])
    ->middleware(LogMiddleware::class);

Global Middleware

$router->globalMiddleware([
    CorsMiddleware::class,
    SecurityHeadersMiddleware::class
]);

Route Caching

// Enable automatic caching in production
$router->autoCache(
    cache_path: __DIR__ . '/cache/routes.php',
    env_var: 'APP_ENV',
    cache_envs: ['production']
);

// Manual caching
if ($router->isCacheable()) {
    $compiled = $router->compile();
    file_put_contents('routes.php', $compiled);
}

// Load from cache
$data = file_get_contents('routes.php');
$router->loadCompiled($data);

Fallback Handler

$router->fallback(function() {
    http_response_code(404);
    echo 'Not Found';
});

Advanced Usage

Dispatching with Context

// Pass additional arguments to actions and middleware
$router = Router::for('GET', '/users', $container, $request);
$router->get('/users', function(array $params, $container, $request) {
    // Access container and request
});

Optional Parameters

$router->get('/users/{id?}', function(array $params) {
    $id = $params['id'] ?? 'all';
    echo "User: $id";
});

Multiple Constraints

$router->get('/posts/{year}/{month}/{slug}', [PostController::class, 'show'])
    ->where([
        'year' => '\d{4}',
        'month' => '\d{2}',
        'slug' => '[a-z0-9-]+'
    ]);

Action Types

The router supports three types of actions:

Controller Array

$router->get('/users', [UserController::class, 'index']);

Invokable Class

$router->get('/users', UserAction::class);

Closure

$router->get('/users', function(array $params) {
    echo 'Users';
});

Note: Closures cannot be cached due to PHP serialization limitations.

Performance

The router uses a three-level indexing strategy for optimal performance:

  1. Static Index (O(1)): Hash map method:path → Route for exact matches
  2. Prefix Index (O(1) + O(n)): Hash map method:prefix → [Routes] for common prefixes
  3. Method Index (O(n)): Hash map method → [Routes] as fallback

This approach minimizes regex matching for most common routing patterns.

Testing

composer test

License

MIT License. See LICENSE for details.

Contributing

Contributions are welcome! Please submit pull requests to the GitHub repository.

Support

Credits

Created and maintained by David Smith.