zekiunal / router
Lightweight, high-performance PHP router implementation based on fast-route with simple array configuration
Installs: 2
Dependents: 1
Suggesters: 0
Security: 0
Stars: 0
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/zekiunal/router
Requires
- php: ^8.4
- nikic/fast-route: ^v1.3.0
README
This document provides a comprehensive overview of the Nexus Application Router system, a PHP routing library built on top of FastRoute.
Table of Contents
Overview
The Nexus Application Router is a flexible, feature-rich routing system designed for PHP applications. It provides:
- URL to controller mapping
- HTTP method routing
- Event system for hooks and extensions
- Middleware support
- Authentication checks
- Request validation
- Response handling
- Template integration
The router leverages FastRoute for efficient route matching and provides additional application-level features like authentication, validation, and template management.
Installation
composer require nexus/application
Usage
Basic Configuration
<?php use Nexus\Application\Router; // Define routes $routes = [ '/' => [ [ 'method' => 'GET', 'uri' => '', 'controller' => 'App\Controllers\HomeController', 'action' => 'index', 'is_public' => true ], [ 'method' => 'GET', 'uri' => 'about', 'controller' => 'App\Controllers\PageController', 'action' => 'about', 'is_public' => true, 'template' => 'about' ] ], '/api' => [ [ 'method' => 'POST', 'uri' => '/users', 'controller' => 'App\Controllers\Api\UserController', 'action' => 'create', 'is_public' => false, 'accept' => ['name', 'email', 'password'], 'validations' => [ 'name' => [ 'App\Validators\RequiredValidator' => [ 'message' => 'Name is required' ] ], 'email' => [ 'App\Validators\EmailValidator' => [ 'message' => 'Invalid email format' ] ] ], 'middlewares' => [ 'App\Middlewares\RateLimitMiddleware' ] ] ] ]; // Initialize router $router = new Router($routes); // Add event listeners $router->on('route.dispatch', function($httpMethod, $uri, $data) { // Log the request error_log("Request: $httpMethod $uri"); }); // Dispatch request $response = $router->dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_POST);
Route Definition
Routes are organized in groups by prefix. Each route defines:
method: HTTP method (GET, POST, PUT, DELETE, etc.)uri: The route pathcontroller: Controller class nameaction: Controller method to executeis_public: Whether authentication is required (false = authentication required)template(optional): Template name for view renderingaccept(optional): Fields to accept from the request datavalidations(optional): Validation rules for accepted fieldsmiddlewares(optional): Array of middleware classes to execute
Handling Requests
The router processes requests by:
- Triggering the
route.dispatchevent - Matching the request URI against defined routes
- Triggering the
route.matchedevent (for matched routes) - Checking authentication requirements
- Running middleware stack
- Triggering the
route.beforeevent - Validating request data if needed
- Instantiating the appropriate controller
- Executing the controller action
- Triggering the
route.afterevent - Returning the response
- If errors occur, triggering the
route.errorevent
Features
Event System
The router includes a powerful event system that allows you to hook into various points in the request lifecycle:
// Register event listener $router->on('route.dispatch', function($httpMethod, $uri, $data) { // Log the request error_log("Request: $httpMethod $uri"); return null; // Continue processing }); // Event listeners can modify the flow by returning values: $router->on('route.before', function($handler, $vars, $data) { if ($handler['action'] === 'sensitiveOperation') { // Block the operation conditionally return [ 'code' => 403, 'message' => 'This operation is currently disabled' ]; } return null; // Continue processing });
Available events:
route.dispatch: Before dispatching, receives ($httpMethod, $uri, $data)route.notFound: When no route matches, receives ($uri)route.matched: When a route is matched, receives ($handler, $vars, $uri)route.middleware: Before each middleware executes, receives ($middleware, $handler, $vars, $data)route.before: Before the controller action executes, receives ($handler, $vars, $data)route.after: After the controller action executes, receives ($response, $handler, $vars)route.error: When an exception occurs, receives ($exception, $handler, $vars)
Authentication Control
The router provides authentication control with the is_public flag:
is_public = true: Route accessible without authenticationis_public = false: Route requires authentication (checks $_SESSION['user_id'])
Request Validation
The validation system allows defining field-specific validation rules:
'validations' => [ 'email' => [ 'App\Validators\EmailValidator' => [ 'params' => [...], 'message' => 'Invalid email format' ] ] ]
Validation errors are stored in $_SESSION['validation_errors'] and the user is redirected back to the form.
Middleware Support
The router supports middleware for pre-processing requests:
'middlewares' => [ 'App\Middlewares\AuthMiddleware', 'App\Middlewares\RateLimitMiddleware' ]
Middleware classes must implement a handle method that receives the route handler, route variables, and request data. They can:
- Return
falseto halt request processing - Return an array to modify the request data
- Return
nullortrueto continue processing
Template Support
Controllers can receive template information from routes:
if (method_exists($controller, 'setTemplate') && isset($handler['template'])) { $controller->setTemplate($handler['template']); }
Architecture
The Nexus Application Router follows a modular architecture:
- Initialization: Router receives route definitions and builds a FastRoute dispatcher
- Event System: Allows hooking into various points in the request lifecycle
- Dispatching: Processes HTTP requests to match defined routes
- Middleware: Executes middleware stack for pre-processing
- Handling: Executes controllers with authentication and validation
- Response: Controllers return structured responses
API Reference
Router Class
__construct(array $routes, $container = null)
- Initializes the router with routes and an optional DI container
on(string $event, callable $callback): self
- Registers an event listener for the specified event
trigger(string $event, array $params = []): mixed
- Triggers an event and executes all registered listeners
dispatch(string $httpMethod, string $uri, array $data = []): array
- Processes a request and returns the response
- Parameters:
$httpMethod: HTTP method (GET, POST, etc.)$uri: Request URI$data: Request data (typically $_POST)
getDispatcher(): Dispatcher
- Returns the FastRoute dispatcher instance
handleNotFound(string $uri = ''): array
- Returns a 404 Not Found response
handleMethodNotAllowed($allowedMethods, string $uri = ''): array
- Returns a 405 Method Not Allowed response with allowed methods
handleFound($handler, $vars, array $data = [], string $uri = ''): array
- Processes a matched route and executes the controller action
Examples
Basic GET route
$routes = [ '/' => [ [ 'method' => 'GET', 'uri' => 'products', 'controller' => 'App\Controllers\ProductController', 'action' => 'list', 'is_public' => true ] ] ];
Protected POST route with validation
$routes = [ '/admin' => [ [ 'method' => 'POST', 'uri' => '/products/create', 'controller' => 'App\Controllers\Admin\ProductController', 'action' => 'create', 'is_public' => false, 'accept' => ['name', 'price', 'description'], 'validations' => [ 'name' => [ 'App\Validators\RequiredValidator' => [ 'message' => 'Product name is required' ] ], 'price' => [ 'App\Validators\NumericValidator' => [ 'message' => 'Price must be a number' ], 'App\Validators\RangeValidator' => [ 'params' => ['min' => 0], 'message' => 'Price must be at least {{min}}' ] ] ] ] ] ];
Using middleware and event listeners
$router = new Router($routes); // Add global authentication check $router->on('route.matched', function($handler, $vars, $uri) { // Check if user has role-based permissions for this route if (!UserService::hasPermission($handler['controller'], $handler['action'])) { return [ 'code' => 403, 'message' => 'Permission denied' ]; } return null; }); // Log all errors $router->on('route.error', function($exception, $handler, $vars) { error_log("Error in {$handler['controller']}::{$handler['action']}: " . $exception->getMessage()); return null; });
Flow Diagrams
Request Processing Flow
flowchart TD
A[HTTP Request] --> B[Router::dispatch]
B --> B1[Trigger route.dispatch event]
B1 --> C{Route Matched?}
C -->|No| D[Trigger route.notFound]
D --> D1[404 Not Found]
C -->|Yes, but wrong method| E[405 Method Not Allowed]
C -->|Yes| F[Trigger route.matched event]
F --> G{Authentication Check}
G -->|Not Public & Not Authenticated| H[Exit: Not Authenticated]
G -->|Public or Authenticated| I{Has Middlewares?}
I -->|Yes| J[Process Middlewares]
J --> J1[Trigger route.middleware event]
J1 --> K{Middleware Result?}
K -->|False| L[403 Forbidden]
K -->|Array| M[Modify request data]
K -->|Null/True| N[Continue]
I -->|No| O[Trigger route.before event]
M --> O
N --> O
O --> P{Before Result?}
P -->|False/Array| Q[Return early]
P -->|Null/True| R{Has Validations?}
R -->|Yes| S[Validate Request]
S -->|Invalid| T[Store Errors & Redirect]
S -->|Valid| U[Instantiate Controller]
R -->|No| U
U --> V[Set Template if Provided]
V --> W[Execute Controller Action]
W --> X[Trigger route.after event]
X --> Y[Return Response]
W -->|Exception| Z[Trigger route.error event]
Z --> AA[Return Error Response]
Loading
Class Diagram
classDiagram
class Router {
-Dispatcher $dispatcher
-$container
-array $listeners
+__construct(array routes, container = null)
+on(string event, callable callback): self
+trigger(string event, array params): mixed
-buildDispatcher(array routes): void
+dispatch(string httpMethod, string uri, array data): array
+getDispatcher(): Dispatcher
+handleNotFound(string uri): array
+handleMethodNotAllowed(allowedMethods, string uri): array
+handleFound(handler, vars, array data, string uri): array
-validateRequest(array acceptFields, array validations, array data): void
-parseMessage(message, params)
-handleValidationErrors(errors): void
-isAuthenticated(): bool
}
class FastRouteDispatcher {
+dispatch(httpMethod, uri)
}
class RouteCollector {
+addRoute(method, uri, handler)
}
class Controller {
+setData(data)
+setTemplate(template)
+action(...)
}
class Middleware {
+handle(handler, vars, data)
}
class Validator {
+validate(value, params): bool
}
Router --> FastRouteDispatcher : uses
Router --> RouteCollector : configures
Router --> Controller : instantiates
Router --> Middleware : executes
Router --> Validator : creates instances
Loading
Route Structure
flowchart TD
A[Routes Array] --> B{Route Groups by Prefix}
B --> C["/"]
B --> D["/api"]
B --> E["/admin"]
C --> F[Route Definition]
D --> G[Route Definition]
E --> H[Route Definition]
F --> I[Route Properties]
I --> J[method]
I --> K[uri]
I --> L[controller]
I --> M[action]
I --> N[is_public]
I --> O[template]
I --> P[accept]
I --> Q[validations]
I --> R[middlewares]
Q --> S[Field Name]
S --> T[Validator Class]
T --> U[params]
T --> V[message]
R --> W[Middleware Class]
Loading
Event System Flow
sequenceDiagram
participant C as Client
participant R as Router
participant E as Event System
participant M as Middleware
participant CT as Controller
C->>R: HTTP Request
R->>E: Trigger route.dispatch
E-->>R: Continue
R->>R: Match Route
R->>E: Trigger route.matched
E-->>R: Continue
R->>R: Auth Check
loop For each middleware
R->>E: Trigger route.middleware
E-->>R: Continue
R->>M: Execute middleware.handle()
M-->>R: Result
end
R->>E: Trigger route.before
E-->>R: Continue
R->>R: Validate Request (if needed)
R->>CT: Execute controller action
alt No Exception
CT-->>R: Response
R->>E: Trigger route.after
E-->>R: Modified Response
else Exception occurs
CT--xR: Throw Exception
R->>E: Trigger route.error
E-->>R: Error Response
end
R-->>C: Return Response
Loading