PHPackages                             timetoogo/rapid-route - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. timetoogo/rapid-route

AbandonedArchivedLibrary[HTTP &amp; Networking](/categories/http)

timetoogo/rapid-route
=====================

Another fast router library for PHP

2.0.0(10y ago)593.0k4MITPHPPHP &gt;=5.4.0

Since May 30Pushed 10y ago5 watchersCompare

[ Source](https://github.com/TimeToogo/RapidRoute)[ Packagist](https://packagist.org/packages/timetoogo/rapid-route)[ Docs](http://www.github.com/TimeToogo/RapidRoute)[ RSS](/packages/timetoogo-rapid-route/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (2)Versions (3)Used By (0)

RapidRoute - Another fast router for PHP
========================================

[](#rapidroute---another-fast-router-for-php)

[![Build status](https://camo.githubusercontent.com/002c9763db55bbb2a20b7ec99974545cde6593af7020bc6b62e33cb3d31bb00a/68747470733a2f2f696d672e736869656c64732e696f2f7472617669732f54696d65546f6f676f2f5261706964526f7574652f6d61737465722e7376673f7374796c653d666c61742d737175617265)](https://travis-ci.org/TimeToogo/RapidRoute)[![Code quality](https://camo.githubusercontent.com/7a4de7059bcc9d10ea53ae74dac27b41a0e5182dea4b23cf8ceb1a5622ad7504/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f672f54696d65546f6f676f2f5261706964526f7574652e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/TimeToogo/RapidRoute)[![Coverage Status](https://camo.githubusercontent.com/258c28ae217e69d78c431be5c6fdacfce033c2b77a60aca6325c5af9f1551a0f/68747470733a2f2f696d672e736869656c64732e696f2f636f766572616c6c732f54696d65546f6f676f2f5261706964526f7574652f6d61737465722e7376673f7374796c653d666c61742d737175617265)](https://coveralls.io/r/TimeToogo/RapidRoute?branch=master)[![Stable Release](https://camo.githubusercontent.com/071257b29754744227655418cd9c8781814518aa1dadbc2c2836c88baf2e0ce7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f767072652f74696d65746f6f676f2f72617069642d726f7574652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/timetoogo/rapid-route)[![License](https://camo.githubusercontent.com/49855d6aff0f2c786c4c67db936f930e524ec1f97ab31cacfb2058f6337dc7a8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f74696d65746f6f676f2f72617069642d726f7574652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/timetoogo/rapid-route)

RapidRoute aims to be another fast router for PHP. This library takes a different approach to uri routing by compiling the router to optimized PHP code, minimizing the need for traditional regular expressions.

As this project focuses on performance, the scope of this library is limited. All in all, this library provides the ability to match a supplied HTTP request (method and uri) against a set of route definitions. See below for usage examples.

Benchmarks
==========

[](#benchmarks)

Test NameRapidRoute (req/sec)FastRoute (req/sec)ChangeFirst static route3385.282906.6416.47% fasterLast static route3419.562901.0917.87% fasterFirst dynamic route3428.942829.1821.20% fasterLast dynamic route3379.562890.1816.93% fasterNon-existent route3412.312823.2720.86% fasterLongest route3371.362853.4018.15% fasterInvalid method, static route3125.812864.199.13% fasterInvalid method, dynamic route3402.572847.5519.49% fasterThese results are generated using [this benchmark suite](https://github.com/TimeToogo/RouterBenchmark) running on PHP 5.5 with opcache enabled. These results indicate a consistent 10-20% performance gain over FastRoute depending on the input uri and http method.

Installation
============

[](#installation)

This project is compatible with PHP 5.4+. It can be loaded via composer:

```
composer require timetoogo/rapid-route ~2.0

```

Router Usage
============

[](#router-usage)

This library is designed to be used by another library/framework or as a standalone package. It provides specific APIs for each use case.

Usage in a framework
====================

[](#usage-in-a-framework)

A framework often provides its own wrapper API so this library offers a lower-level API in this case. A basic example is shown:

```
use RapidRoute\CompiledRouter;
use RapidRoute\RouteCollection;
use RapidRoute\MatchResult;

$compiledRouterPath = __DIR__ . '/path/to/compiled/router.php';

$router = CompiledRouter::generate(
    $compiledRouterPath,
    function (RouteCollection $routes) {
        // Route definitions...
    }
);

$result = $router($httpMethod, $uri);

switch ($result[0]) {
    case MatchResult::NOT_FOUND:
        // 404 Not Found...
        break;

    case MatchResult::HTTP_METHOD_NOT_ALLOWED:
        // 405 Method Not Allowed...
        $allowedMethods = $result[1];
        break;

    case MatchResult::FOUND:
        // Matched route, dispatch to associated handler...
        $routeData = $result[1];
        $parameters = $result[2];
        break;
}
```

The result from the router is an array that contains the result status as the first element. The following elements of the array are dependent on the status and will be one of three formats:

```
// Could not match route
[MatchResult::NOT_FOUND]

// Matched route but disallowed HTTP method
[MatchResult::HTTP_METHOD_NOT_ALLOWED, []]

// Found matching route
[MatchResult::FOUND, , []]
```

Usage as a standalone package
=============================

[](#usage-as-a-standalone-package)

If this library is intended to be used as a standalone package, a cleaner and more extensive wrapper API is provided. A similar example showing off this the API is shown:

```
use RapidRoute\Router;
use RapidRoute\RouteCollection;
use RapidRoute\MatchResult;

$compiledRouterPath = __DIR__ . '/path/to/compiled/router.php';

$router = new Router(
    $compiledRouterPath,
    function (RouteCollection $routes) {
        // Route definitions...
    }
);

// If true the router will be recompiled every request
$router->setDevelopmentMode($developmentMode);
// Or you can manually call when appropriate
// $router->clearCompiled();

$result = $router->match($httpMethod, $uri);

if($result->isNotFound()) {
    // 404 Not Found...
} elseif ($result->isDisallowedHttpMethod()) {
    // 405 Method Not Allowed...
    $allowedMethods = $result->getAllowedHttpMethods();
} elseif ($result->isFound()) {
    // Matched route, dispatch to associated handler...
    $routeData = $result->getRouteData();
    $parameters = $result->getParameters();
}

// Or if preferred
switch ($result->getStatus()) {
    // case MatchResult::* as above
}
```

The result from the call to `$router->match(...)` will be an instance of `RapidRoute\MatchResult`.

Route definitions
=================

[](#route-definitions)

**Route patterns**

To define the routes, a familiar url structure is used:

```
// This is a static route, it will extactly match '/shop/product'
'/shop/product'

// A dynamic route can be defined using the {...} parameter syntax
// This will match urls such as '/shop/product/123' or '/shop/product/abcd'
'/shop/product/{id}'

// If a route parameter must match a specific format you can define it
// by passing an array with a regex in the following format
['/shop/product/{id}', 'id' => '\d+']

// Or, if you prefer, you can use the predefined patterns using RapidRoute\Pattern
['/shop/product/{id}', 'id' => Pattern::DIGITS]

// More complex routes patterns are supported
[
  '/shop/category/{category_id}/product/search/{filter_by}:{filter_value}',
  'category_id' => Pattern::DIGITS,
  'filter_by'   => Pattern::ALPHA_LOWER
]

// You can also inline the parameter regexps using the following syntax
// The following is equivalent to the previous route definition
'/shop/category/{category_id:\d+}/product/search/{filter_by:[a-z]+}:{filter_value}'
```

**Adding Routes**

To define the routes, the router API takes a `callable` parameter which will be called with an instance of `RapidRoute\RouteCollection` when the router is being compiled. This can be used like so:

```
function (RouteCollection $routes) {
    $routes->add('GET', '/', ['name' => 'home']);

    // There are also shortcuts for the standard HTTP methods
    // the following is equivalent to the previous call
    $routes->get('/', ['name' => 'home']);

    // Or if any HTTP method should be allowed:
    $routes->any('/contact', ['name' => 'contact']);
}
```

Using the `RouteCollection` you can also define a route parameter regex globally to avoid repetitions:

```
function (RouteCollection $routes) {
    $routes->param('product_id', Pattern::DIGITS);
    $routes->param('page_slug', Pattern::ALPHA_NUM_DASH);

    $routes->get('/shop/product/{product_id}', ['name' => 'shop.product.show']);
    $routes->get('/page/{page_slug}', ['name' => 'page.show']);
}
```

Basic usage example
===================

[](#basic-usage-example)

The associated route data will be available when the route is matched. This is a very basic example of how this library may be implemented as a standalone router package. The route data contains the associated handler so it can be easily dispatched when the route is matched.

```
use RapidRoute\Router;
use RapidRoute\RouteCollection;
use RapidRoute\Pattern;
use RapidRoute\MatchResult;

require __DIR__ . './vendor/autoload.php';

$compiledRouterPath = __DIR__ . '/path/to/compiled/router.php';

$router = new Router(
    $compiledRouterPath,
    function (RouteCollection $routes) {
        $routes->param('user_id', Pattern::DIGITS);

        $routes->get('/', ['handler' => ['HomeController', 'index']]);
        $routes->get('/user', ['handler' => ['UserController', 'index']]);
        $routes->get('/user/create', ['handler' => ['UserController', 'create']]);
        $routes->post('/user', ['handler' => ['UserController', 'store']]);
        $routes->get('/user/{user_id}', ['handler' => ['UserController', 'show']]);
        $routes->get('/user/{user_id}/edit', ['handler' => ['UserController', 'edit']]);
        $routes->add(['PUT', 'PATCH'], '/user/{user_id}', ['handler' => ['UserController', 'update']]);
        $routes->delete('/user/{user_id}', ['handler' => ['UserController', 'delete']]);
    }
);

$router->setDevelopmentMode($developmentMode);

$result = $router->match($_SERVER['REQUEST_METHOD'], parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));

switch($result->getStatus()) {
    case MatchResult::NOT_FOUND:
        render((new ErrorController())->notFound());
        break;

    case MatchResult::HTTP_METHOD_NOT_ALLOWED:
        render((new ErrorController())->methodNotAllowed($result->getAllowedHttpMethods()));
        break;

    case MatchResult::FOUND:
        // Dispatcher matched route to associated handler
        list($controller, $method) = $result->getRouteData()['handler'];
        $parameters = $result->getParameters();

        render((new $controller())->{$method}($parameters));
        break;
}
```

Here are some examples of how this set up should handle the incoming request:

RequestDispatched HandlerGET /`HomeController::index([])`GET /user`UserController::index([])`POST /user`UserController::store([])`POST /`ErrorController::methodNotAllowed(['GET'])`GET /abc`ErrorController::notFound()`GET /user/123`UserController::show(['user_id' => '123'])`PUT /user/123`UserController::update(['user_id' => '123'])`PUT /user/abc`ErrorController::notFound()`Notes
=====

[](#notes)

- When matching a uri, the uri string **must** contain a preceding `/` if it is not empty.
- Route defined with a trailing slash will **not** match a uri without the slash
    - `'/shop/product/'` will not match `'/shop/product'` and vice-versa
- A route that allows the `GET` method will also accept the `HEAD` method as per HTTP spec.

Compilation
===========

[](#compilation)

Given that this library compiles route definitions to plain PHP, there is much room for optimization. The current approach is using a tree structure matching each segment in a uri (`'/shop/product'` is composed of the `'shop'` and `'product'` segments). Currently the structure is compiled to nested `switch` and `if` blocks using optimized comparisons where applicable.

One consideration of the compiled router is that it must be able to be called directly and as such must handle the any expected error cases within the compiled router.

**Example compiled router**

Route definitions:

```
$router = CompiledRouter::generate(
    __DIR__ . '/compiled/rr.php',
    function (\RapidRoute\RouteCollection $routes) {
        $routes->param('post_slug', Pattern::APLHA_NUM_DASH);

        $routes->get('/', ['name' => 'home']);
        $routes->get('/blog', ['name' => 'blog.index']);
        $routes->get('/blog/post/{post_slug}', ['name' => 'blog.post.show']);
        $routes->post('/blog/post/{post_slug}/comment', ['name' => 'blog.post.comment']);
    }
)
```

Currently the compiled router for the above will be similar to the following:

```
use RapidRoute\RapidRouteException;

return function ($method, $uri) {
    if($uri === '') {
        return [0];
    } elseif ($uri[0] !== '/') {
        throw new RapidRouteException("Cannot match route: non-empty uri must be prefixed with '/', '{$uri}' given");
    }

    $segments = explode('/', substr($uri, 1));

    switch (count($segments)) {
        case 1:
            list($s0) = $segments;
            if ($s0 === '') {
                switch ($method) {
                    case 'GET':
                    case 'HEAD':
                        return [2, ['name' => 'home'], []];
                    default:
                        $allowedHttpMethods[] = 'GET';
                        $allowedHttpMethods[] = 'HEAD';
                        break;
                }
            }
            if ($s0 === 'blog') {
                switch ($method) {
                    case 'GET':
                    case 'HEAD':
                        return [2, ['name' => 'blog.index'], []];
                    default:
                        $allowedHttpMethods[] = 'GET';
                        $allowedHttpMethods[] = 'HEAD';
                        break;
                }
            }
            return isset($allowedHttpMethods) ? [1, $allowedHttpMethods] : [0];
            break;

        case 3:
            list($s0, $s1, $s2) = $segments;
            if ($s0 === 'blog' && $s1 === 'post' && ctype_alnum(str_replace('-', '', $s2))) {
                switch ($method) {
                    case 'GET':
                    case 'HEAD':
                        return [2, ['name' => 'blog.post.show'], ['post_slug' => $s2]];
                    default:
                        $allowedHttpMethods[] = 'GET';
                        $allowedHttpMethods[] = 'HEAD';
                        break;
                }
            }
            return isset($allowedHttpMethods) ? [1, $allowedHttpMethods] : [0];
            break;

        case 4:
            list($s0, $s1, $s2, $s3) = $segments;
            if ($s0 === 'blog' && $s1 === 'post' && $s3 === 'comment' && ctype_alnum(str_replace('-', '', $s2))) {
                switch ($method) {
                    case 'POST':
                        return [2, ['name' => 'blog.post.comment'], ['post_slug' => $s2]];
                    default:
                        $allowedHttpMethods[] = 'POST';
                        break;
                }
            }
            return isset($allowedHttpMethods) ? [1, $allowedHttpMethods] : [0];
            break;

        default:
            return [0];
    }
};
```

The complexity of the router will grow in proportion to the number and complexity of the route definitions.

###  Health Score

35

—

LowBetter than 80% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity32

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 93.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~70 days

Total

2

Last Release

3936d ago

Major Versions

1.0.0 → 2.0.02015-08-09

### Community

Maintainers

![](https://www.gravatar.com/avatar/2c14fef2f0c0d5ad1da6656db39abf9f2659d649c6548d80d42130a6587ef8df?d=identicon)[TimeToogo](/maintainers/TimeToogo)

---

Top Contributors

[![TimeToogo](https://avatars.githubusercontent.com/u/689898?v=4)](https://github.com/TimeToogo "TimeToogo (29 commits)")[![klaussilveira](https://avatars.githubusercontent.com/u/467729?v=4)](https://github.com/klaussilveira "klaussilveira (1 commits)")[![Natorator](https://avatars.githubusercontent.com/u/1920659?v=4)](https://github.com/Natorator "Natorator (1 commits)")

---

Tags

routerfastroute

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/timetoogo-rapid-route/health.svg)

```
[![Health](https://phpackages.com/badges/timetoogo-rapid-route/health.svg)](https://phpackages.com/packages/timetoogo-rapid-route)
```

###  Alternatives

[league/route

Fast routing and dispatch component including PSR-15 middleware, built on top of FastRoute.

6633.1M115](/packages/league-route)[aura/router

Powerful, flexible web routing for PSR-7 requests.

5231.5M67](/packages/aura-router)[coffeecode/router

A classic CoffeeCode Router is easy, fast and extremely uncomplicated. Create and manage your routes in minutes!

181111.1k5](/packages/coffeecode-router)[pmjones/auto-route

Automatically routes HTTP request to action classes.

20158.6k6](/packages/pmjones-auto-route)[miladrahimi/phprouter

A powerful, lightweight, and very fast HTTP URL router for PHP projects.

20832.6k2](/packages/miladrahimi-phprouter)[contributte/api-router

RESTful Router for your Apis in Nette Framework - created either directly or via attributes

20802.8k3](/packages/contributte-api-router)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
