lachie4145 / yaml-router
A lightweight YAML-configured HTTP router for PHP projects.
Package info
github.com/lachie4145/yaml-router
Type:composer-plugin
pkg:composer/lachie4145/yaml-router
Requires
- php: >=8.1
- composer-plugin-api: ^2.0
- symfony/yaml: ^7.0
Requires (Dev)
- composer/composer: ^2.0
Suggests
- psr/http-message: Required to use YamlRouter\PsrAdapter for PSR-7 request/response handling.
README
A lightweight YAML-configured HTTP router for PHP projects.
Define your routes in a routes.yaml file and let YamlRouter handle matching, dispatching, middleware, and more — no framework required.
Requirements
- PHP 8.1+
symfony/yaml^7.0 (installed automatically)
Installation
composer require lachie4145/yaml-router
After install, a routes.example.yaml file is copied to your project root as a reference.
Quick Start
<?php require __DIR__ . '/vendor/autoload.php'; $router = YamlRouter\Router::create(); $router->run();
By default Router::create() reads routes.yaml from the current working directory. You can pass an explicit path:
$router = YamlRouter\Router::create(__DIR__ . '/config/routes.yaml');
Route File Format
routes: - method: GET path: / response: Hello from YamlRouter - method: GET path: /users/{id} handler: App\Controllers\UserController@show
Each route supports:
| Key | Required | Description |
|---|---|---|
method |
Yes | HTTP method (GET, POST, PUT, PATCH, DELETE) |
path |
Yes | URL path starting with / |
response |
One of these | Static response body |
handler |
One of these | PHP handler (see Handlers) |
redirect |
One of these | Redirect target URL |
name |
No | Named route identifier |
middleware |
No | String or list of middleware handlers |
constraints |
No | Per-parameter regex constraints |
status |
No | Override HTTP status code (default 200) |
headers |
No | Extra response headers |
Handlers
Static response
- method: GET path: /hello response: Hello, world!
Controller method
- method: GET path: /users/{id} handler: App\Controllers\UserController@show
The method receives array $params where keys are the dynamic path segments:
class UserController { public function show(array $params): string { return 'User: ' . $params['id']; } }
PHP file
- method: GET path: /legacy handler: handlers/legacy.php
The file is included with $params in scope. If the file echoes output it is captured; if it returns a value that value is used as the response.
Callable function
- method: GET path: /ping handler: ping_handler
Route Groups
Group routes under a shared prefix, namespace, and/or middleware:
groups: - prefix: /api/v1 namespace: App\Controllers\Api middleware: - App\Middleware\ApiAuthMiddleware@handle routes: - method: GET path: /users handler: UserController@index - method: GET path: /users/{id} constraints: id: int handler: UserController@show
The prefix is prepended to every route path in the group. The namespace is prepended to controller-style handlers that don't already contain a namespace.
Constraints
Constrain dynamic parameters to a regex pattern:
- method: GET path: /posts/{slug} constraints: slug: slug # built-in alias handler: PostController@show - method: GET path: /orders/{id} constraints: id: '\d{4,10}' # custom regex handler: OrderController@show
Built-in aliases:
| Alias | Pattern |
|---|---|
int |
[0-9]+ |
slug |
[a-z0-9]+(?:-[a-z0-9]+)* |
uuid |
[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} |
Named Routes
Assign a name to a route and generate URLs from it:
- method: GET path: /users/{id} name: user.show handler: App\Controllers\UserController@show
$router = YamlRouter\Router::create(); echo $router->url('user.show', ['id' => '42']); // /users/42
Middleware
Middleware handlers use the same ClassName@method or callable string format as route handlers. A middleware that returns a non-null value short-circuits dispatch and sends that value as the response instead. Returning null allows the request to continue.
Global middleware
Runs before every request:
middleware: - App\Middleware\RequestLogger@handle
Group middleware
Runs for every route in the group:
groups: - prefix: /admin middleware: - App\Middleware\AdminAuth@handle routes: - ...
Per-route middleware
- method: POST path: /users middleware: - App\Middleware\AuthMiddleware@handle handler: App\Controllers\UserController@store
Middleware runs in order: global → group → per-route.
Redirects
- method: GET path: /old-path redirect: /new-path
The default redirect status is 302. Override with status:
- method: GET path: /old-path redirect: /new-path status: 301
Custom Status & Headers
- method: GET path: /api/data status: 202 headers: Content-Type: application/json X-Custom-Header: value handler: App\Controllers\DataController@index
Route Caching
Pass a cache file path to avoid re-parsing the YAML on every request:
$router = YamlRouter\Router::create( routeFile: __DIR__ . '/routes.yaml', cachePath: __DIR__ . '/storage/routes.cache.php' ); $router->run();
The cache is automatically invalidated when routes.yaml is modified.
Built-in Route Docs
Enable a GET /docs endpoint that renders an HTML table of all registered routes:
enableDocs: true
Disable or guard this endpoint in production.
WAF
Enable the built-in Web Application Firewall to block common attack patterns:
enableWAF: true waf: blockSqlInjection: true blockXss: true blockPathTraversal: true rateLimit: enabled: true maxRequests: 100 windowSeconds: 60 logPath: storage/waf.log # optional
Custom WAF rules can be added via customRules:
waf: customRules: - file: app/Security/MyRules.php class: MyRules method: checkRequest
PSR-7 Support
Install psr/http-message in your project and use PsrAdapter:
$router = YamlRouter\Router::create(); $adapter = new YamlRouter\PsrAdapter($router); $psrResponse = $adapter->handle($psrRequest, $psrResponse);
License
MIT