---
title: "Routing Project"
description: "Derafu Routing"
type: "docs"
category: "doc"
tags: [php]
authors: [Anonymous]
date: "2026-08-21"
last_update: "2026-08-21"
time_minutes: 1
draft: false
unlisted: false
url: "https://www.derafu.dev/docs/core/routing"
---

# Derafu Routing



---

## Introduction

Elegant PHP Router with Plugin Architecture

# Elegant PHP Router with Plugin Architecture

![GitHub last commit](https://img.shields.io/github/last-commit/derafu/routing/main)
![CI Workflow](https://github.com/derafu/routing/actions/workflows/ci.yml/badge.svg?branch=main&amp;event=push)
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/derafu/routing)
![GitHub Issues](https://img.shields.io/github/issues-raw/derafu/routing)
![Total Downloads](https://poser.pugx.org/derafu/routing/downloads)
![Monthly Downloads](https://poser.pugx.org/derafu/routing/d/monthly)

A lightweight, extensible PHP routing library that combines simplicity with power through its parser-based architecture.

## Features

- 🔌 Plugin architecture with swappable parsers.
- 🎯 Multiple routing strategies (static, dynamic, filesystem).
- 🧩 Easy to extend with custom parsers.
- 📁 Built-in filesystem routing for static sites.
- 🔄 Support for different content types (.md, .twig, etc.).
- 🛠️ Clean separation of concerns.
- 🪶 Lightweight with zero dependencies.
- ⚡ Fast pattern matching.
- 🧪 Comprehensive test coverage.
- 🔗 URL generation for named routes.

## Why Derafu\Routing?

Unlike traditional monolithic routers, Derafu\Routing uses a unique parser-based architecture that offers several advantages:

- **Modularity**: Each routing strategy is encapsulated in its own parser.
- **Flexibility**: Easy to add new routing patterns without modifying existing code.
- **Clarity**: Clear separation between route matching and request handling.
- **Extensibility**: Add custom parsers for specific routing needs.
- **Predictability**: Each parser has a single responsibility.
- **Performance**: Only load the parsers you need.

## Installation

Install via Composer:

```bash
composer require derafu/routing
```

## Basic Usage

```php
use Derafu\Routing\Router;
use Derafu\Routing\Dispatcher;
use Derafu\Routing\Parser\StaticParser;
use Derafu\Routing\Parser\FileSystemParser;

// Create and configure router.
$router = new Router();
$router-&gt;addParser(new StaticParser());
$router-&gt;addParser(new FileSystemParser([__DIR__ . &#039;/pages&#039;]));

// Add routes.
$router-&gt;addRoute(&#039;/&#039;, &#039;HomeController::index&#039;, name: &#039;home&#039;);
$router-&gt;addDirectory(__DIR__ . &#039;/pages&#039;);

// Create and configure dispatcher.
$dispatcher = new Dispatcher([
    &#039;md&#039; =&gt; fn ($file, $params) =&gt; renderMarkdown($file),
    &#039;twig&#039; =&gt; fn($file, $params) =&gt; renderTwig($file, $params),
]);

// Handle request.
try {
    $route = $router-&gt;match();
    echo $dispatcher-&gt;dispatch($route);
} catch (RouterException $e) {
    // Handle error.
}
```

## Available Parsers

### StaticParser

Handles exact route matches:

```php
$router-&gt;addRoute(&#039;/about&#039;, &#039;PagesController::about&#039;, name: &#039;about&#039;);
```

### DynamicParser

Supports parameters and patterns:

```php
$router-&gt;addRoute(&#039;/users/{id:\d+}&#039;, &#039;UserController::show&#039;, name: &#039;user.show&#039;);
$router-&gt;addRoute(&#039;/blog/{year}/{slug}&#039;, &#039;BlogController::post&#039;, name: &#039;blog.post&#039;);
```

### FileSystemParser

Maps URLs to files in directories:

```php
$router-&gt;addDirectory(__DIR__ . &#039;/pages&#039;);
// Examples:
// /about maps to /pages/about.md
// /contact maps to /pages/contact.html.twig
```

## URL Generation

Generate URLs for named routes:

```php
// Set request context (needed for absolute URLs).
$router-&gt;setContext(new RequestContext(
    baseUrl: &#039;/myapp&#039;,
    scheme: &#039;https&#039;,
    host: &#039;example.com&#039;
));

// Generate URLs.
$url = $router-&gt;generate(&#039;user.show&#039;, [&#039;id&#039; =&gt; 123]); // /myapp/users/123
$url = $router-&gt;generate(&#039;blog.post&#039;, [
    &#039;year&#039; =&gt; &#039;2024&#039;,
    &#039;slug&#039; =&gt; &#039;hello-world&#039;
]); // /myapp/blog/2024/hello-world

// Generate absolute URL.
$url = $router-&gt;generate(&#039;about&#039;, [], UrlReferenceType::ABSOLUTE_URL);
// https://example.com/myapp/about
```

## Creating Custom Parsers

Implement your own routing strategy by creating a parser:

```php
class CustomParser implements ParserInterface
{
    public function parse(string $uri, array $routes): ?RouteMatch
    {
        // Your custom routing logic.
    }

    public function supports(Route $route): bool
    {
        // Define what routes this parser can handle.
    }
}

$router-&gt;addParser(new CustomParser());
```

## File-based Routing Example

Perfect for static sites:

```
pages/
├── about.md
├── contact.html.twig
└── blog/
    ├── post-1.md
    └── post-2.md
```

URLs are automatically mapped to files:

- `/about` → `pages/about.md`
- `/contact` → `pages/contact.html.twig`
- `/blog/post-1` → `pages/blog/post-1.md`




---

## Guide

Complete Usage Guide

# Complete Usage Guide

Derafu Routing is a flexible PHP routing library that uses a parser-based architecture. Instead of having a monolithic router, it separates routing logic into specialized parsers, each handling different types of routes.

## Installation

```bash
composer require derafu/routing
```

## Basic Concepts

### Parser Architecture

The routing system is built around specialized parsers:

1. **StaticParser**: Handles exact route matches.
2. **DynamicParser**: Processes routes with parameters.
3. **FileSystemParser**: Maps URLs to physical files.

Each parser implements the `ParserInterface`:

```php
interface ParserInterface {
    public function parse(string $uri, array $routes): ?RouteMatchInterface;
    public function supports(RouteInterface $route): bool;
}
```

## Route Types

### Static Routes

The simplest form of routing, handled by `StaticParser`:

```php
$router = new Router([new StaticParser()]);
$router-&gt;addRoute(&#039;/about&#039;, &#039;PagesController::action&#039;, name: &#039;about&#039;);
$router-&gt;addRoute(&#039;/contact&#039;, &#039;ContactController::show&#039;, name: &#039;contact&#039;);
```

### Dynamic Routes

Handled by `DynamicParser`, supporting various parameter types:

```php
$router-&gt;addParser(new DynamicParser());

// Basic parameter.
$router-&gt;addRoute(&#039;/users/{id}&#039;, &#039;UserController::show&#039;, name: &#039;user.show&#039;);

// Validation with regular expressions.
$router-&gt;addRoute(&#039;/users/{id:\d+}&#039;, &#039;UserController::show&#039;, name: &#039;user.show&#039;);

// Optional parameters.
$router-&gt;addRoute(&#039;/blog/{year?}&#039;, &#039;BlogController::index&#039;, name: &#039;blog.index&#039;);

// Multiple parameters.
$router-&gt;addRoute(&#039;/blog/{year}/{month?}&#039;, &#039;BlogController::archive&#039;, name: &#039;blog.archive&#039;);

// Complex patterns.
$router-&gt;addRoute(&#039;/users/{username:[a-z0-9_-]+}&#039;, &#039;UserController::profile&#039;, name: &#039;user.profile&#039;);
```

### Filesystem Routes

The `FileSystemParser` maps URLs to actual files:

```php
$parser = new FileSystemParser(
    directories: [__DIR__ . &#039;/pages&#039;],
    extensions: [&#039;.html.twig&#039;, &#039;.md&#039;]
);
$router-&gt;addParser($parser);
```

Directory structure:
```
pages/
├── about.md           # Matches /about
├── contact.twig       # Matches /contact
└── blog/
    ├── post-1.md     # Matches /blog/post-1
    └── post-2.md     # Matches /blog/post-2
```

## Using the Router

### Basic Configuration

```php
use Derafu\Routing\Router;
use Derafu\Routing\Parser\StaticParser;
use Derafu\Routing\Parser\DynamicParser;

$router = new Router([
    new StaticParser(),
    new DynamicParser(),
]);
```

### Adding Routes

```php
// String handler (Controller@action).
$router-&gt;addRoute(&#039;/users&#039;, &#039;UserController::index&#039;, name: &#039;users.index&#039;);

// Closure handler.
$router-&gt;addRoute(&#039;/api/data&#039;, function($params) {
    return [&#039;data&#039; =&gt; &#039;value&#039;];
}, name: &#039;api.data&#039;);

// Array handler.
$router-&gt;addRoute(&#039;/blog&#039;, [
    &#039;controller&#039; =&gt; &#039;BlogController&#039;,
    &#039;action&#039; =&gt; &#039;list&#039;
], name: &#039;blog.index&#039;);

// Routes with name and parameters.
$router-&gt;addRoute(
    route: &#039;/users/{id}&#039;,
    handler: &#039;UserController::show&#039;,
    name: &#039;user.show&#039;,
    parameters: [&#039;active&#039; =&gt; true]
);
```

### Route Matching

```php
try {
    $match = $router-&gt;match(&#039;/users/123&#039;);
    // $match-&gt;getHandler(): Returns the route handler.
    // $match-&gt;getParameters(): Returns the route parameters.
    // $match-&gt;getName(): Returns the route name if defined.
} catch (RouteNotFoundException $e) {
    // Handle 404.
}
```

### URL Generation

The router allows generating URLs from named routes:

```php
// Set request context (needed for absolute URLs).
$router-&gt;setContext(new RequestContext(
    baseUrl: &#039;/myapp&#039;,
    scheme: &#039;https&#039;,
    host: &#039;example.com&#039;
));

// Generate relative URLs.
$url = $router-&gt;generate(&#039;user.show&#039;, [&#039;id&#039; =&gt; 123]); // /myapp/users/123
$url = $router-&gt;generate(&#039;blog.archive&#039;, [
    &#039;year&#039; =&gt; &#039;2024&#039;,
    &#039;month&#039; =&gt; &#039;03&#039;
]); // /myapp/blog/2024/03

// Generate URL without optional parameter.
$url = $router-&gt;generate(&#039;blog.archive&#039;, [
    &#039;year&#039; =&gt; &#039;2024&#039;
]); // /myapp/blog/2024

// Generate absolute URL.
$url = $router-&gt;generate(&#039;about&#039;, [], UrlReferenceType::ABSOLUTE_URL);
// https://example.com/myapp/about

// Generate network path URL.
$url = $router-&gt;generate(&#039;about&#039;, [], UrlReferenceType::NETWORK_PATH);
// //example.com/myapp/about
```

Available reference types are:

- `ABSOLUTE_PATH`: Absolute path from root (default).
- `ABSOLUTE_URL`: Complete URL with scheme and host.
- `NETWORK_PATH`: URL without scheme (useful for resources that work on both HTTP and HTTPS).

## The Dispatcher

The dispatcher handles the execution of matching routes:

```php
$dispatcher = new Dispatcher([
    &#039;md&#039; =&gt; function($file, $params) {
        // Render markdown file.
        return parseMarkdown(file_get_contents($file));
    },
    &#039;twig&#039; =&gt; function($file, $params) {
        // Render Twig template.
        return $twig-&gt;render($file, $params);
    }
]);

$result = $dispatcher-&gt;dispatch($match);
```

**Note**: This is a very basic *dispatcher*, you should implement your own.

## Advanced Usage

### Custom Parser Example

```php
class RegexParser implements ParserInterface
{
    public function parse(string $uri, array $routes): ?RouteMatchInterface
    {
        foreach ($routes as $route) {
            if (!$this-&gt;supports($route)) {
                continue;
            }

            // Custom regex matching logic
            if (preg_match($route-&gt;getPath(), $uri, $matches)) {
                return new RouteMatch(
                    $route-&gt;getHandler(),
                    $matches
                );
            }
        }
        return null;
    }

    public function supports(RouteInterface $route): bool
    {
        // Define what routes this parser can handle
        return str_starts_with($route-&gt;getPath(), &#039;#&#039;);
    }
}
```

## Best Practices

1. **Parser Order**: Add parsers in order of specificity.
   - StaticParser first (faster, more specific).
   - DynamicParser next.
   - FileSystemParser last (more flexible but slower).

2. **Route Organization**: Group related routes.
   ```php
   // User management
   $router-&gt;addRoute(&#039;/users&#039;, &#039;UserController::index&#039;, name: &#039;users.index&#039;);
   $router-&gt;addRoute(&#039;/users/{id}&#039;, &#039;UserController::show&#039;, name: &#039;users.show&#039;);

   // Blog system
   $router-&gt;addRoute(&#039;/blog&#039;, &#039;BlogController::index&#039;, name: &#039;blog.index&#039;);
   $router-&gt;addRoute(&#039;/blog/{slug}&#039;, &#039;BlogController::show&#039;, name: &#039;blog.show&#039;);
   ```

3. **Parameter Validation**: Use regex constraints for better security.
   ```php
   // Ensure ID is numeric.
   $router-&gt;addRoute(&#039;/users/{id:\d+}&#039;, &#039;UserController::show&#039;, name: &#039;users.show&#039;);

   // Validate username format.
   $router-&gt;addRoute(&#039;/users/{username:[a-z0-9_-]+}&#039;, &#039;UserController::profile&#039;, name: &#039;users.profile&#039;);
   ```

4. **Error Handling**: Always wrap matches in try-catch.
   ```php
   try {
       $match = $router-&gt;match($uri);
       $result = $dispatcher-&gt;dispatch($match);
   } catch (RouteNotFoundException $e) {
       // Handle 404.
   } catch (DispatcherException $e) {
       // Handle dispatcher errors.
   }
   ```

5. **URL Generation**: Always use route names instead of hardcoded URLs.
   ```php
   // Bad
   $url = &#039;/users/&#039; . $id;

   // Good
   $url = $router-&gt;generate(&#039;users.show&#039;, [&#039;id&#039; =&gt; $id]);
   ```

6. **Request Context**: Configure context if absolute URLs are needed.
   ```php
   $router-&gt;setContext(new RequestContext(
       baseUrl: &#039;/myapp&#039;,
       scheme: &#039;https&#039;,
       host: &#039;example.com&#039;,
       httpPort: 80,
       httpsPort: 443
   ));
   ```





---
Last updated on 21/08/2026
#php
