Modern, fast and flexible PHP router with support for static and instance modes, middleware chains, route groups, and advanced routing features.
- ✅ Dual Mode: Static (auto-dispatch) and Instance (manual control)
- ✅ Route Parameters: Simple
{id}and regex{id:\d+}with optional parameters - ✅ Route Groups: Nested groups with prefix and middleware inheritance
- ✅ Middleware Chain: Elegant middleware system with dependency injection
- ✅ Multiple HTTP Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, ANY, and custom
- ✅ Controller Support: String syntax, array syntax, and closures
- ✅ Named Routes: Reference routes by name (coming soon)
- ✅ PSR-4 Autoloading: Modern namespace structure
- ✅ Type Safety: Full PHP 8.1+ type hints
- ✅ Zero Dependencies: Standalone library
- ✅ Debug Mode: Built-in debugging capabilities
composer require israel-nogueira/fast-router- PHP >= 8.1
- Composer
<?php
require 'vendor/autoload.php';
use IsraelNogueira\FastRouter\Router;
// Simple route
Router::get('/', function() {
echo "Hello World!";
});
// Route with parameter
Router::get('user/{id}', function($id) {
echo "User ID: {$id}";
});
// POST route
Router::post('contact', function() {
echo "Form submitted!";
});- Static vs Instance Mode
- Route Parameters
- Route Groups
- Middleware
- Controllers
- HTTP Methods
- Advanced Features
Routes are automatically dispatched after each definition:
use IsraelNogueira\FastRouter\Router;
Router::get('/', function() {
echo "Home";
});
Router::get('about', function() {
echo "About";
});
// No need to call dispatch() - routes auto-executeFor more control, create a Router instance:
$router = new Router();
$router->get('/', function() {
echo "Home";
});
$router->get('about', function() {
echo "About";
});
// Manually dispatch routes
$router->dispatch();When to use each:
- Static Mode: Simple applications, rapid prototyping
- Instance Mode: Testing, multiple routers, advanced control
Router::get('user/{id}', function($id) {
echo "User: {$id}";
});
Router::get('post/{year}/{month}/{slug}', function($year, $month, $slug) {
echo "Post: {$slug} from {$month}/{$year}";
});// Only numbers
Router::get('product/{id:\d+}', function($id) {
echo "Product: {$id}";
});
// Only lowercase letters and hyphens
Router::get('category/{slug:[a-z-]+}', function($slug) {
echo "Category: {$slug}";
});
// Date pattern
Router::get('archive/{date:\d{4}-\d{2}-\d{2}}', function($date) {
echo "Archive: {$date}";
});// Single optional parameter
Router::get('blog/{id:\d+}[/{title}/]', function($id, $title = null) {
echo "Blog: {$id}";
if ($title) echo " - {$title}";
});
// Multiple optional parameters
Router::get('archive/{year:\d{4}}[/{month:\d{2}}[/{day:\d{2}}/]/]',
function($year, $month = null, $day = null) {
echo "Archive: {$year}/{$month}/{$day}";
}
);Syntax Rules:
- Required:
{param}or{param:regex} - Optional:
[/{param}/]with slashes inside brackets - Nested:
[/{a}[/{b}/]/]for multiple optional params
Router::group('admin', function($router) {
$router->get('dashboard', function() {
echo "Admin Dashboard";
// URL: /admin/dashboard
});
$router->get('users', function() {
echo "Admin Users";
// URL: /admin/users
});
});Router::group('admin', function($router) {
$router->group('products', function($router) {
$router->get('list', function() {
echo "Product List";
// URL: /admin/products/list
});
$router->group('category', function($router) {
$router->get('list', function() {
echo "Category List";
// URL: /admin/products/category/list
});
});
});
});Router::group([
'prefix' => 'admin',
'middleware' => ['auth', 'admin']
], function($router) {
$router->get('dashboard', function() {
echo "Secure Dashboard";
});
$router->get('settings', function() {
echo "Secure Settings";
});
});Middleware allows you to filter HTTP requests before they reach your route handler.
class AuthMiddleware
{
public function handle(array $returns, callable $next): mixed
{
// Check authentication
if (!isset($_SESSION['user'])) {
http_response_code(401);
die('Unauthorized');
}
// Add data to returns array
$returns[] = [
'middleware' => 'auth',
'user' => $_SESSION['user']
];
// Continue to next middleware
return $next($returns);
}
}Router::get([
'prefix' => 'dashboard',
'middleware' => [AuthMiddleware::class]
], function() {
echo "Protected Dashboard";
});Router::group([
'prefix' => 'admin',
'middleware' => [AuthMiddleware::class, LogMiddleware::class]
], function($router) {
$router->get('users', function() {
echo "Admin Users";
});
$router->get('settings', function() {
echo "Admin Settings";
});
});Router::get([
'prefix' => 'profile',
'middleware' => ['App\Middleware\Auth@handle']
], function() {
echo "User Profile";
});Middlewares execute in order. Each middleware must call $next($returns) to continue:
class FirstMiddleware
{
public function handle(array $returns, callable $next): mixed
{
echo "Before First\n";
$result = $next($returns);
echo "After First\n";
return $result;
}
}
class SecondMiddleware
{
public function handle(array $returns, callable $next): mixed
{
echo "Before Second\n";
$result = $next($returns);
echo "After Second\n";
return $result;
}
}
// Output:
// Before First
// Before Second
// [Route Handler]
// After Second
// After FirstTo stop execution, don't call $next():
class AdminCheckMiddleware
{
public function handle(array $returns, callable $next): mixed
{
if (!$this->isAdmin()) {
http_response_code(403);
die('Forbidden');
// Chain stops here
}
return $next($returns);
}
}class UserController
{
public function index()
{
echo "User List";
}
public function show($id)
{
echo "User: {$id}";
}
public function store()
{
echo "Create User";
}
}Router::get('users', 'UserController@index');
Router::get('users/{id}', 'UserController@show');
Router::post('users', 'UserController@store');Router::get('users', [UserController::class, 'index']);
Router::get('users/{id}', [UserController::class, 'show']);
Router::post('users', [UserController::class, 'store']);Execute multiple callbacks in sequence:
Router::get('dashboard', [
'logRequest', // Function
'UserController@auth', // Controller method
[DashboardController::class, 'index'], // Array syntax
function() { // Closure
echo " - Rendered";
}
]);$resource = 'posts';
$controller = PostController::class;
Router::get($resource, [$controller, 'index']); // GET /posts
Router::get("{$resource}/create", [$controller, 'create']); // GET /posts/create
Router::post($resource, [$controller, 'store']); // POST /posts
Router::get("{$resource}/{id}", [$controller, 'show']); // GET /posts/123
Router::get("{$resource}/{id}/edit", [$controller, 'edit']); // GET /posts/123/edit
Router::put("{$resource}/{id}", [$controller, 'update']); // PUT /posts/123
Router::delete("{$resource}/{id}", [$controller, 'destroy']); // DELETE /posts/123Router::get('/', fn() => 'GET');
Router::post('/', fn() => 'POST');
Router::put('/', fn() => 'PUT');
Router::delete('/', fn() => 'DELETE');
Router::patch('/', fn() => 'PATCH');
Router::options('/', fn() => 'OPTIONS');
Router::head('/', fn() => 'HEAD');Accepts all HTTP methods:
Router::any('search', function() {
echo "Works with any HTTP method";
});// Using math() method
Router::math(['GET', 'POST'], 'form', function() {
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
echo "Show form";
} else {
echo "Process form";
}
});Router::addRoute(['CUSTOM', 'SPECIAL'], 'endpoint', function() {
echo "Custom HTTP method";
});By default, router stops after first match. To continue:
Router::get([
'prefix' => 'log/{id}',
'continue' => true
], function($id) {
error_log("Logging access to: {$id}");
});
Router::get('log/{id}', function($id) {
echo "Viewing log: {$id}";
});Enable debugging to see route matching process:
$router = new Router();
$router->debug(true);
$router->get('test', function() {
echo "Test";
});
$router->dispatch();
// Output includes HTML comments:
// <!-- Fast Router Debug -->
// <!-- Method: GET -->
// <!-- URL: /test -->
// <!-- Routes: 1 -->
// <!-- Matched: test -->Test routes with custom URL and method:
$router = new Router();
$router->get('api/users/{id}', function($id) {
return ['user_id' => $id];
});
// Dispatch with custom parameters
$result = $router->dispatch('GET', '/api/users/123');Access registered routes:
$router = new Router();
$router->get('home', fn() => 'Home');
$router->get('about', fn() => 'About');
$collection = $router->getRoutes();
echo "Total routes: " . $collection->count();
foreach ($collection->all() as $route) {
echo $route->pattern . "\n";
}$router = new Router();
$router->get('test1', fn() => 'Test 1');
$router->get('test2', fn() => 'Test 2');
// Clear all routes
$router->clear();use PHPUnit\Framework\TestCase;
use IsraelNogueira\FastRouter\Router;
class RouterTest extends TestCase
{
public function testBasicRoute()
{
$router = new Router();
$router->get('test', function() {
return 'success';
});
$result = $router->dispatch('GET', '/test');
$this->assertEquals('success', $result);
}
public function testRouteParameters()
{
$router = new Router();
$router->get('user/{id:\d+}', function($id) {
return $id;
});
$result = $router->dispatch('GET', '/user/123');
$this->assertEquals('123', $result);
}
}- Namespace change:
IsraelNogueira\fastRouter→IsraelNogueira\FastRouter - PHP requirement: Now requires PHP 8.1+
- Type hints: All methods now use strict types
// OLD (v1.x)
use IsraelNogueira\fastRouter\router;
router::get('/', function() {});
// NEW (v2.x)
use IsraelNogueira\FastRouter\Router;
Router::get('/', function() {});Good news: All public APIs remain compatible! Just update the namespace.
Contributions are welcome! Please feel free to submit a Pull Request.
git clone https://github.com/israel-nogueira/fast-router.git
cd fast-router
composer installcomposer testcomposer analyseThis project is licensed under the GPL-3.0-or-later License - see the LICENSE file for details.
Israel Nogueira
- Email: contato@israelnogueira.com
- GitHub: @israel-nogueira
Give a ⭐️ if this project helped you!
- ✨ Complete refactoring with modern PHP 8.1+ features
- ✨ Added strict type hints throughout
- ✨ Improved middleware system
- ✨ Better error handling with custom exceptions
- ✨ PSR-4 autoloading structure
- ✨ Debug mode
- 🐛 Fixed various edge cases in route matching
- 📚 Complete documentation rewrite
- Initial releases with basic routing functionality
Made with ❤️ by Israel Nogueira