PHPackages                             rareloop/router - 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. [Framework](/categories/framework)
4. /
5. rareloop/router

ActiveLibrary[Framework](/categories/framework)

rareloop/router
===============

A powerful PHP Router for PSR7 messages inspired by the Laravel API

v6.2.0(1mo ago)92186.3k↓28.1%9[1 PRs](https://github.com/Rareloop/router/pulls)3MITPHPPHP &gt;=8.3CI passing

Since Jul 21Pushed 1mo ago12 watchersCompare

[ Source](https://github.com/Rareloop/router)[ Packagist](https://packagist.org/packages/rareloop/router)[ Docs](https://github.com/rareloop/router)[ RSS](/packages/rareloop-router/feed)WikiDiscussions master Synced 3d ago

READMEChangelog (6)Dependencies (26)Versions (38)Used By (3)

Rare Router
===========

[](#rare-router)

[![Latest Stable Version](https://camo.githubusercontent.com/0049405fd65c3725ec014b083e5a3bb890d7498a15ac511d85f9ba88fadfa717/68747470733a2f2f706f7365722e707567782e6f72672f726172656c6f6f702f726f757465722f762f737461626c65)](https://packagist.org/packages/rareloop/router)[![CI](https://github.com/rareloop/router/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/rareloop/router/actions/workflows/ci.yml/badge.svg?branch=master)[![Coverage Status](https://camo.githubusercontent.com/84161e282723f2d39f9e9da82c47dbec0a9d5949cf7c3187b9bf3690cfe52994/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f526172656c6f6f702f726f757465722f62616467652e737667)](https://coveralls.io/github/Rareloop/router)

A simple PHP router built on [AltoRouter](https://github.com/dannyvankooten/AltoRouter) but inspired by the [Laravel](https://laravel.com/docs/5.4/routing) API.

Installation
------------

[](#installation)

```
composer require rareloop/router

```

Usage
-----

[](#usage)

### Creating Routes

[](#creating-routes)

#### Map

[](#map)

Creating a route is done using the `map` function:

```
use Rareloop\Router\Router;

$router = new Router;

// Creates a route that matches the uri `/posts/list` both GET
// and POST requests.
$router->map(['GET', 'POST'], 'posts/list', function () {
    return 'Hello World';
});
```

`map()` takes 3 parameters:

- `methods` (array): list of matching request methods, valid values:
    - `GET`
    - `POST`
    - `PUT`
    - `PATCH`
    - `DELETE`
    - `OPTIONS`
- `uri` (string): The URI to match against
- `action` (function|string): Either a closure or a Controller string

#### Route Parameters

[](#route-parameters)

Parameters can be defined on routes using the `{keyName}` syntax. When a route matches that contains parameters, an instance of the `RouteParams` object is passed to the action.

```
$router->map(['GET'], 'posts/{id}', function(RouteParams $params) {
    return $params->id;
});
```

If you need to add constraints to a parameter you can pass a regular expression pattern to the `where()` function of the defined `Route`:

```
$router->map(['GET'], 'posts/{id}/comments/{commentKey}', function(RouteParams $params) {
    return $params->id;
})->where('id', '[0-9]+')->where('commentKey', '[a-zA-Z]+');

// or

$router->map(['GET'], 'posts/{id}/comments/{commentKey}', function(RouteParams $params) {
    return $params->id;
})->where([
    'id', '[0-9]+',
    'commentKey', '[a-zA-Z]+',
]);
```

#### Optional route Parameters

[](#optional-route-parameters)

Sometimes your route parameters needs to be optional, in this case you can add a `?` after the parameter name:

```
$router->map(['GET'], 'posts/{id?}', function(RouteParams $params) {
    if (isset($params->id)) {
        // Param provided
    } else {
        // Param not provided
    }
});
```

#### Named Routes

[](#named-routes)

Routes can be named so that their URL can be generated programatically:

```
$router->map(['GET'], 'posts/all', function () {})->name('posts.index');

$url = $router->url('posts.index');
```

If the route requires parameters you can be pass an associative array as a second parameter:

```
$router->map(['GET'], 'posts/{id}', function () {})->name('posts.show');

$url = $router->url('posts.show', ['id' => 123]);
```

If a passed in parameter fails the regex constraint applied, a `RouteParamFailedConstraintException` will be thrown.

#### HTTP Verb Shortcuts

[](#http-verb-shortcuts)

Typically you only need to allow one HTTP verb for a route, for these cases the following shortcuts can be used:

```
$router->get('test/route', function () {});
$router->post('test/route', function () {});
$router->put('test/route', function () {});
$router->patch('test/route', function () {});
$router->delete('test/route', function () {});
$router->options('test/route', function () {});
```

#### Setting the basepath

[](#setting-the-basepath)

The router assumes you're working from the route of a domain. If this is not the case you can set the base path:

```
$router->setBasePath('base/path');
$router->map(['GET'], 'route/uri', function () {}); // `/base/path/route/uri`
```

#### Controllers

[](#controllers)

If you'd rather use a class to group related route actions together you can pass a Controller String to `map()` instead of a closure. The string takes the format `{name of class}@{name of method}`. It is important that you use the complete namespace with the class name.

Example:

```
// TestController.php
namespace \MyNamespace;

class TestController
{
    public function testMethod()
    {
        return 'Hello World';
    }
}

// routes.php
$router->map(['GET'], 'route/uri', '\MyNamespace\TestController@testMethod');
```

### Creating Groups

[](#creating-groups)

It is common to group similar routes behind a common prefix. This can be achieved using Route Groups:

```
$router->group('prefix', function ($group) {
    $group->map(['GET'], 'route1', function () {}); // `/prefix/route1`
    $group->map(['GET'], 'route2', function () {}); // `/prefix/route2§`
});
```

### Middleware

[](#middleware)

PSR-15/7 Middleware can be added to both routes and groups.

#### Adding Middleware to a route

[](#adding-middleware-to-a-route)

At it's simplest, adding Middleware to a route can be done by passing an object to the `middleware()` function:

```
$middleware = new AddHeaderMiddleware('X-Key1', 'abc');

$router->get('route/uri', '\MyNamespace\TestController@testMethod')->middleware($middleware);
```

Multiple middleware can be added by passing more params to the `middleware()` function:

```
$header = new AddHeaderMiddleware('X-Key1', 'abc');
$auth = new AuthMiddleware();

$router->get('route/uri', '\MyNamespace\TestController@testMethod')->middleware($header, $auth);
```

Or alternatively, you can also pass an array of middleware:

```
$header = new AddHeaderMiddleware('X-Key1', 'abc');
$auth = new AuthMiddleware();

$router->get('route/uri', '\MyNamespace\TestController@testMethod')->middleware([$header, $auth]);
```

#### Adding Middleware to a group

[](#adding-middleware-to-a-group)

Middleware can also be added to a group. To do so you need to pass an array as the first parameter of the `group()` function instead of a string.

```
$header = new AddHeaderMiddleware('X-Key1', 'abc');

$router->group(['prefix' => 'my-prefix', 'middleware' => $header]), function ($group) {
    $group->map(['GET'], 'route1', function () {}); // `/my-prefix/route1`
    $group->map(['GET'], 'route2', function () {}); // `/my-prefix/route2§`
});
```

You can also pass an array of middleware if you need more than one:

```
$header = new AddHeaderMiddleware('X-Key1', 'abc');
$auth = new AuthMiddleware();

$router->group(['prefix' => 'my-prefix', 'middleware' => [$header, $auth]]), function ($group) {
    $group->map(['GET'], 'route1', function () {}); // `/my-prefix/route1`
    $group->map(['GET'], 'route2', function () {}); // `/my-prefix/route2§`
});
```

#### Defining Middleware on Controllers

[](#defining-middleware-on-controllers)

You can also apply Middleware on a Controller class too. In order to do this your Controller must extend the `Rareloop\Router\Controller` base class.

Middleware is added by calling the `middleware()` function in your Controller's `__constructor()`.

```
use Rareloop\Router\Controller;

class MyController extends Controller
{
    public function __construct()
    {
        // Add one at a time
        $this->middleware(new AddHeaderMiddleware('X-Key1', 'abc'));
        $this->middleware(new AuthMiddleware());

        // Add multiple with one method call
        $this->middleware([
            new AddHeaderMiddleware('X-Key1', 'abc',
            new AuthMiddleware(),
        ]);
    }
}
```

By default all Middleware added via a Controller will affect all methods on that class. To limit what methods Middleware applies to you can use `only()` and `except()`:

```
use Rareloop\Router\Controller;

class MyController extends Controller
{
    public function __construct()
    {
        // Only apply to `send()` method
        $this->middleware(new AddHeaderMiddleware('X-Key1', 'abc'))->only('send');

        // Apply to all methods except `show()` method
        $this->middleware(new AuthMiddleware())->except('show');

        // Multiple methods can be provided in an array to both methods
        $this->middleware(new AuthMiddleware())->except(['send', 'show']);
    }
}
```

### Matching Routes to Requests

[](#matching-routes-to-requests)

Once you have routes defined, you can attempt to match your current request against them using the `match()` function. `match()` accepts an instance of Symfony's `Request` and returns an instance of Symfony's `Response`:

```
$request = Request::createFromGlobals();
$response = $router->match($request);
$response->send();
```

#### Return values

[](#return-values)

If you return an instance of `Response` from your closure it will be sent back un-touched. If however you return something else, it will be wrapped in an instance of `Response` with your return value as the content.

#### Responsable objects

[](#responsable-objects)

If you return an object from your closure that implements the `Responsable` interface, it's `toResponse()` object will be automatically called for you.

```
class MyObject implements Responsable
{
    public function toResponse(RequestInterface $request) : ResponseInterface
    {
        return new TextResponse('Hello World!');
    }
}

$router->get('test/route', function () {
    return new MyObject();
});
```

#### 404

[](#404)

If no route matches the request, a `Response` object will be returned with it's status code set to `404`;

#### Accessing current route

[](#accessing-current-route)

The currently matched `Route` can be retrieved by calling:

```
$route = $router->currentRoute();
```

If no route matches or `match()` has not been called, `null` will be returned.

You can also access the name of the currently matched `Route` by calling:

```
$name = $router->currentRouteName();
```

If no route matches or `match()` has not been called or the matched route has no name, `null` will be returned.

### Using with a Dependency Injection Container

[](#using-with-a-dependency-injection-container)

The router can also be used with a PSR-11 compatible Container of your choosing. This allows you to type hint dependencies in your route closures or Controller methods.

To make use of a container, simply pass it as a parameter to the Router's constructor:

```
use MyNamespace\Container;
use Rareloop\Router\Router;

$container = new Container();
$router = new Router($container);
```

After which, your route closures and Controller methods will be automatically type hinted:

```
$container = new Container();

$testServiceInstance = new TestService();
$container->set(TestService::class, $testServiceInstance);

$router = new Router($container);

$router->get('/my/route', function (TestService $service) {
    // $service is now the same object as $testServiceInstance
});
```

###  Health Score

68

—

FairBetter than 99% of packages

Maintenance90

Actively maintained with recent releases

Popularity47

Moderate usage in the ecosystem

Community26

Small or concentrated contributor base

Maturity90

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 91.7% 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 ~146 days

Recently: every ~421 days

Total

23

Last Release

52d ago

Major Versions

v1.0.0 → v2.0.02017-09-30

v2.1.0 → v3.0.02018-06-05

v3.2.1 → v4.0.02018-08-12

v4.4.0 → v5.0.02021-07-20

v4.x-dev → v6.0.02021-09-15

PHP version history (5 changes)v1.0.0PHP ^7.0

v4.4.0PHP ^7.1

v5.0.0PHP ^7.3||^8.0

v6.0.3PHP &gt;=8.1

v6.1.0PHP &gt;=8.3

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1554376?v=4)[Rareloop](/maintainers/rareloop)[@Rareloop](https://github.com/Rareloop)

---

Top Contributors

[![joelambert](https://avatars.githubusercontent.com/u/644362?v=4)](https://github.com/joelambert "joelambert (77 commits)")[![adamtomat](https://avatars.githubusercontent.com/u/2631499?v=4)](https://github.com/adamtomat "adamtomat (2 commits)")[![tommitchelmore](https://avatars.githubusercontent.com/u/19690833?v=4)](https://github.com/tommitchelmore "tommitchelmore (2 commits)")[![AliceKLWilliams](https://avatars.githubusercontent.com/u/25745335?v=4)](https://github.com/AliceKLWilliams "AliceKLWilliams (1 commits)")[![nlemoine](https://avatars.githubusercontent.com/u/2526939?v=4)](https://github.com/nlemoine "nlemoine (1 commits)")[![trovster](https://avatars.githubusercontent.com/u/48729?v=4)](https://github.com/trovster "trovster (1 commits)")

---

Tags

middlewarephp-routerpsr7psr7-compliantpsr7-httprouterrouterrareloop

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/rareloop-router/health.svg)

```
[![Health](https://phpackages.com/badges/rareloop-router/health.svg)](https://phpackages.com/packages/rareloop-router)
```

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.9k19.5M1.8k](/packages/cakephp-cakephp)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k15](/packages/tempest-framework)[symfony/symfony

The Symfony PHP framework

31.4k87.2M2.2k](/packages/symfony-symfony)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)[slim/slim

Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs

12.3k52.9M1.4k](/packages/slim-slim)[rareloop/lumberjack-core

A powerful MVC framework for the modern WordPress developer. Write better, more expressive and easier to maintain code

42161.2k21](/packages/rareloop-lumberjack-core)

PHPackages © 2026

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