willy68/pg-router

A fast, flexible, and PSR-7 compatible router for PHP.

v1.0.9 2025-07-03 10:48 UTC

This package is auto-updated.

Last update: 2025-07-08 20:03:21 UTC


README

Latest Stable Version Total Downloads Latest Unstable Version License PHP Version Require Coverage Status

A fast, flexible, and PSR-7 compatible HTTP router for PHP applications. Built for performance with support for advanced routing patterns, middleware stacking, and route caching.

Table of Contents

Features

  • PSR-7 Compatible: Full support for PSR-7 request interfaces
  • 🚀 High Performance: Optimized route matching with caching support
  • 🎯 Flexible Routing: Support for complex route patterns and constraints
  • 🔧 Middleware Support: Route-level and group-level middleware stacking
  • 📦 Route Grouping: Organize routes with shared prefixes and middlewares
  • 🎨 Named Routes: Easy URL generation with named route support
  • CRUD Helpers: Quick REST resource route generation
  • 🔀 Optional Segments: Advanced optional route segment support
  • 🏗️ Extensible: Custom matchers and collectors for advanced use cases

Requirements

  • PHP 8.1 or higher
  • Composer

Installation

Install via Composer:

composer require willy68/pg-router

Quick Start

use Pg\Router\Router;
use guzzlehttp\Psr7\Response;
use guzzlehttp\Psr7\ServerRequest;

$request = ServerRequest::fromGlobals(); // Create a PSR-7 request from global variables
$router = new Router();

$router->route('/hello/{name: \w+}', function ($request): ResponseInterface {
    $name = $request->getAttribute('name');
    (return new Response())->getBody()->write("Hello, $name!");
}, 'hello', ['GET']);

$res = $router->match($request);

if ($res->isSuccess()) {
// add route attributes to the request
    foreach ($res->getMatchedAttributes() as $key => $val) {
        $request = $request->withAttribute($key, $val);
    }

$callback = $res->getMatchedRoute()->getCallback();
$response = $callback($request);

Advanced Usage

Route Parameters

// Simple parameter, id matches any alphanumeric string
$router->route('/user/{id}','handler', 'user.show', ['GET']);

//Define route parameters with custom patterns:
// Parameter with regex constraint 
$router->route('/user/{id:\d+}','handler', 'user.show', ['GET']);

// Multiple parameters 
$router->route('/blog/{year:\d{4}}/{month:\d{2}}/{slug}','handler', 'blog.post', ['GET']);

Optional Segments

Breaking Change: Optional segments now use a new syntax with [!...;...] pattern:

// Example route with optional segments
$router->route('/article[!/{id: \d+};/{slug: [\w-]+}]', function ($request) {
    $id = $request->getAttribute('id', null);
    $slug = $request->getAttribute('slug', null);
    // ...
}, 'article.show', ['GET']);

This route matches:

  • /article (no parameters)
  • /article/123 (id parameter only)
  • /article/123/my-article-title (both id and slug parameters)

Parameters will be available in the request based on the provided segments.

Route Groups

Organize related routes with shared prefixes and middlewares:

$router->group('/api/v1', function ($group) { 
    $group->route('/users', 'UserController::index', 'api.users.index', ['GET']);
    $group->route('/users/{id:\d+}', 'UserController::show', 'api.users.show', ['GET']); 
    $group->route('/users', 'UserController::store', 'api.users.store', ['POST']); 
})->middlewares([AuthMiddleware::class, ApiMiddleware::class]);

Middleware

Apply middleware to individual routes or groups:

// Route-level middleware
$router->route('/admin/dashboard','handler', 'admin.dashboard', ['GET'])
    ->middlewares([AuthMiddleware::class, AdminMiddleware::class]);

// Group-level middleware
$router->group('/admin', function ($group) { 
    $group->route('/users', 'AdminController::users', 'admin.users', ['GET']);
    $group->route('/settings', 'AdminController::settings', 'admin.settings', ['GET']);
})->middlewares([AuthMiddleware::class, AdminMiddleware::class]);

Named Routes

Generate URLs using named routes:

// Define a named route
$router->route('/user/{id:\d+}','handler', 'user.profile', ['GET']);

// Generate URL
$url = $router->generateUri('user.profile', ['id' => 123]); 
// Result: /user/123

// Generate URL with query parameters
$url = $router->generateUri('user.profile', ['id' => 123], ['tab' => 'settings']); 
// Result: /user/123?tab=settings

CRUD Helper

$router->crud('/posts', PostController::class, 'posts');

This creates the following routes:

  • GET /postsPostController::index (posts.index)
  • GET /posts/newPostController::create (posts.create)
  • POST /posts/newPostController::create (posts.create.post)
  • GET /posts/{id:\d+}PostController::edit (posts.edit)
  • POST /posts/{id:\d+}PostController::edit (posts.edit.post)
  • DELETE /posts/{id:\d+}PostController::delete (posts.delete)

URL Generation

// Basic URL generation
$url = $router->generateUri('hello', ['name' => 'Alice']);
// With query parameters
$url = $router->generateUri('user.profile', ['id' => 123], ['tab' => 'settings']);

Route Caching

Enable route caching for production environments:

// Enable caching with a cache file
      $router = new Router (
           null,
           null,
           [
               Router::CONFIG_CACHE_ENABLED => ($env === 'prod'),
               Router::CONFIG_CACHE_DIR => '/tmp/cache',
               Router::CONFIG_CACHE_POOL_FACTORY => function (): CacheItemPoolInterface {...},
           ]
      )

// In production, routes are cached and loaded from the cache file
// In development, disable caching or clear cache when routes change
$router->clearCache();

Router::CONFIG_CACHE_POOL_FACTORY allows you to use a custom PSR-6 compatible cache pool implementation,
but this parameter is optional.

Performance

pg-router is designed for high performance:

  • Optimized Route Matching: Uses efficient algorithms for route compilation and matching
  • Route Caching: Cache compiled routes for production use
  • Minimal Memory Footprint: Efficient memory usage for large route tables
  • Fast Parameter Extraction: Optimized parameter extraction from matched routes

Testing

This project uses PHPUnit for testing. To run the tests, ensure you have PHPUnit installed via Composer:

# Run all tests
./vendor/bin/phpunit

# Run tests with coverage
php -dxdebug.mode=coverage ./vendor/bin/phpunit --coverage-text --coverage-html=build/coverage
# or
composer run coverage

# Run specific test file
./vendor/bin/phpunit tests/RouterTest.php

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Please ensure your code follows PSR-12 coding standards and includes appropriate tests.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Author: William Lety Maintainer: willy68 Repository: pg-router