lachie4145/yaml-router

A lightweight YAML-configured HTTP router for PHP projects.

Maintainers

Package info

github.com/lachie4145/yaml-router

Type:composer-plugin

pkg:composer/lachie4145/yaml-router

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.1 2026-04-30 05:03 UTC

This package is auto-updated.

Last update: 2026-04-30 05:04:23 UTC


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