PHPackages                             weew/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. weew/router

ActiveLibrary[Framework](/categories/framework)

weew/router
===========

Simple router.

v2.8.0(9y ago)12835MITPHP

Since Aug 21Pushed 9y ago1 watchersCompare

[ Source](https://github.com/weew/router)[ Packagist](https://packagist.org/packages/weew/router)[ RSS](/packages/weew-router/feed)WikiDiscussions master Synced 4w ago

READMEChangelogDependencies (8)Versions (33)Used By (5)

Simple router
=============

[](#simple-router)

[![Build Status](https://camo.githubusercontent.com/47393cec8b7afdf53b9a830b59ef8ee2add44d90199c63f13f51a74215e48ddd/68747470733a2f2f696d672e736869656c64732e696f2f7472617669732f776565772f726f757465722e737667)](https://travis-ci.org/weew/router)[![Code Quality](https://camo.githubusercontent.com/317b0dc9ba76e36adf154241c0e3a5eeee2e031c0f38f9ddf3fbeb1772896922/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f672f776565772f726f757465722e737667)](https://scrutinizer-ci.com/g/weew/router)[![Test Coverage](https://camo.githubusercontent.com/de06879b2f810aa8848055f626e8e47bc82918f32e25966cc122ebddfbc97d8b/68747470733a2f2f696d672e736869656c64732e696f2f636f766572616c6c732f776565772f726f757465722e737667)](https://coveralls.io/github/weew/router)[![Version](https://camo.githubusercontent.com/34db6be1acc883faa1c2cfcad6b18742a3ac03df1df8ef20f651064b1d8585fb/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f776565772f726f757465722e737667)](https://packagist.org/packages/weew/router)[![Licence](https://camo.githubusercontent.com/bab7ad245797fa542cf6f08b1f54a3465c7fac1a5f83efdbf5f481cd654f2975/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f776565772f726f757465722e737667)](https://packagist.org/packages/weew/router)

Table of contents
-----------------

[](#table-of-contents)

- [Installation](#installation)
- [Introduction](#introduction)
- [Registering routes](#registering-routes)
- [Route parameters](#route-parameters)
- [Matching routes](#matching-routes)
- [Custom patterns](#custom-patterns)
- [Firewalls](#firewalls)
- [Parameter resolvers](#parameter-resolvers)
- [Rules](#rules)
- [Grouping routes](#grouping-routes)
- [Complete example](#complete-example)
- [Extensions](#extensions)

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

[](#installation)

`composer require weew/router`

Introduction
------------

[](#introduction)

What the router basically does is matching a URL to a list of registered routes and returns you a route upon a successful match. If there was no match,you'll get null. A route can contain any value you want, since it's up to you to create a response based on the route after all. This gives you the flexibility to use the router together with any other existing dependency injection containers or any other components. The router doesn't do anything but matching a URL to a route.

Registering routes
------------------

[](#registering-routes)

Below you'll see the basic methods for route registration. Route path may contain some placeholders for expected values, like `{id}`. If the placeholder ends with a `?`, like here `{alias?}`, it is considered optional. As the second argument you may pass anything you want. You'll have access to that value later, so you can create a response or similar.

```
$router = new Router();
$router->get('/', 'home')
    ->post('login', 'SomeController::handleLogin')
    ->put('users/{id}', function() {})
    ->patch('users/{id}', '')
    ->update('some/path/{optional?}', '')
    ->delete('users/{id}/{alias?}', '')
    ->options('you/wont/need/it', '');
```

Mostly you are going to use this definition format for your route definitions:

```
$router = new Router();
$router
    ->get('/', [SampleController::class, 'getHome'])
    ->get('about', [SampleController::class, 'getAbout'])
    ->get('contact', [SampleController::class, 'getContact']);
```

As you see in this example, you've got to write the `SampleController` class over and over again. You can avoid this by setting a controller class on the router itself. Doing so, will create a new [nested router](#grouping-routes). Example below does exactly the same as the example above, except you have less boilerplate code.

```
$router = new Router();
$router
    ->setController(SampleController::class)
        ->get('/', 'getHome')
        ->get('about', 'getAbout')
        ->get('contact', 'getContact');
```

Route parameters
----------------

[](#route-parameters)

Let's say you've defined some routes that expect a parameter to be set in the url, here you'll see how you can access them.

```
$router = new Router();
$router->get('home/{greeting?}', 'home');

$route = $router->match(HttpRequestMethod::GET, new Url('home/welcome'));
echo $route->getParameter('greeting');
// welcome
```

Matching routes
---------------

[](#matching-routes)

By now you have registered all of your routes and want to find the one that matches the specified url.

```
$router = new Router();
$router->get('home/{greeting?}', 'home');
$route = $router->match(HttpRequestMethod::GET, new Url('home/hello-there'));

if ( ! $route === null) {
    echo $route->getAction();
    // home
    echo $route->getParameter('greeting');
    // hello-there
} else {
    // no route found, thow a 404?
}
```

Custom patterns
---------------

[](#custom-patterns)

In some situations you wont to be able to specify custom patterns for route parameters. For example, lets say you want your ids to consist of numerical characters only.

```
$router = new Router();
$router
    ->addPattern('id', '[0-9]+')
    ->get('users/{id}', '');
```

Firewalls
---------

[](#firewalls)

It is very easy to protect routes with custom filters. In general, if a single filter fails (returns `false` or throws an exception), no further filters will be called. Same goes the other way around. If a filter explicitly says that everything is ok (returns `true`), no further filters will be called.

```
$router = new Router();
$router->addFilter('auth', function(IRoute $route) {
    // returning false indicates that filter has failed, no other filters will be called
    return false;
});

$router->addFilter('guest', function(IRoute $route) {
    // explicitly returning true indicates that this route is ok, no other filters will be called
    return true;
});

$router->enableFilter('auth');
$router->enableFilter('guest');

// or

 $router->enableFilter(['auth', 'guest']);
```

A filter has to return a boolean value to indicate whether the affected routes are good to go or rather should be ignored. Filter work best with groups, see below.

Sometimes you might want to throw an exception that would hold the reason why a filter did not pass. If you simply throw a regular exception, this would kill the program flow, and even if you catch this exception somewhere, it would kill the whole routing process. Even though a particular route did not match, because a filter failed, there might be another one that would fit. After a regular exception was thrown, there is no way another route might match. To work around this you might simply wrap you exception in the `FilterException`. This would ensure that the routing process finishes as supposed to and gives a chance for another route to match. If no route was found after all, you original exception will be thrown.

```
$router = new Router();
$router->addFilter('auth', function(IRoute $route) {
    throw new FilterException(
        new UnauthorizedException()
    );
});
```

Now, failure of a filter will not break the routing process. If a route gets matched after all, there will be no exception thrown. But if there was no other route that could take it's place (be matched instead), the `UnauthorizedException` will be thrown.

Parameter resolvers
-------------------

[](#parameter-resolvers)

Often you might want to process a route parameter and replace it with another one. For example when you're using models. This route `/users/{id}` would always hold the id of the requested user. Wouldn't it be cool if it would hold the user model instead?

```
$router = new Router();
$router->addResolver('user', function($parameter) {
    return new User(); // for the sake of the example lets just return a new model
});

$router->get('users/{user}', function(User $user) {
    // work with the user model
});
```

User's id has been magically resolved to it's model. Now you can use it in your route handlers.

Rules
-----

[](#rules)

You might also want to specify additional routing restrictions based on the current url. For example, limiting your routes to a subdomain or protocol.

```
$router = new Router();
$router
    ->restrictSubdomain('api')
    ->get('users/{id}', '');
```

There are many other restrictions that you might find useful.

```
$router = new Router();
$router->restrictProtocol('https')
    ->restrictDomain(['domain1', 'domain2'])
    ->restrictTLD(['com', 'net'])
    ->restrictSubdomain('api')
    ->restrictHost(['domain1.com', 'domain2.net'])
```

Grouping routes
---------------

[](#grouping-routes)

Sometimes you have an obvious boundary between your routes. Lets say you want your api routes to be available from the `api` subdomain and over https only. All the other routes should remain as is.

```
$router = new Router();
$router->get('/', 'home');
$router->group(function(IRouter $router) {
    $router->restrictProtocol('https')
        ->restrictSubdomain('api')
        ->get('users/{id}', '');
});
$router->get('about', 'about');
```

Complete example
----------------

[](#complete-example)

A complete example of how you might use the router out of the box. The router itself is very flexible and at the end it comes down to your preference on how you will use it. Basically all it does is returning a route. After that it's up to you how you want to handle it. You might dynamically resolve the controller, or event combine it with a dependency injection container.

```
$router = new Router();

$router->get('/', 'home');
$router->get('about', 'about');
$router->post('login', 'login');

$router->addFilter('auth', function(IRoute $route) {
    return fasle; // not logged in
});

$router->addResolver('user', function($id) {
    return new User($id);
});

$router->group(function(IRouter $router) {
    $router->setPrefix('api/v1');
    $router->restrictProtocol('https');
    $router->restrictSubdomain('api');
    $router->enableFilter('auth');

    $router->get('users/{user}/{alias?}', function(IRoute $route) {
        $response = new JsonResponse(HttpRequestMethod::GET, [
            'id' => $route->getParameter('user')->id,
            'alias' => $route->getParameter('alias', 'no alias')
        ]);
        $response->send();
    });
});

$router->group(function(IRouter $router) {
    $router->setPrefix('api/v2');
    $router->addPattern('id', '[a-zA-Z]+');

    $router->get('users/{user}/{alias?}', function(IRoute $route) {
        $response = new JsonResponse(HttpRequestMethod::GET, [
            'id' => $route->getParameter('user')->id,
            'alias' => $route->getParameter('alias', 'no alias')
        ]);
        $response->send();
    });
});

// recommended way to work with the request
$request = new CurrentRequest();
$route = $router->match($request->getMethod(), $request->getUrl());

// create a response based on the route
if ($route instanceof IRoute) {
    $abstract = $route->getAction();

    if (is_callable($abstract)) {
        $abstract($route);
    } else {
        echo $abstract;
    }
} else {
    echo '404';
}
```

Extensions
----------

[](#extensions)

There are several extensions available:

- [weew/router-container-aware](https://github.com/weew/router-container-aware)
- [weew/router-entities-resolver](https://github.com/weew/router-entities-resolver)
- [weew/router-routes-invoker-container-aware](https://github.com/weew/router-routes-invoker-container-aware)
- [weew/router-configurator](https://github.com/weew/router-configurator)

###  Health Score

33

—

LowBetter than 72% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity13

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity74

Established project with proven stability

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 ~11 days

Recently: every ~24 days

Total

32

Last Release

3626d ago

Major Versions

v0.0.13 → v1.0.02015-11-16

v1.2.0 → v2.0.02016-01-19

### Community

Maintainers

![](https://www.gravatar.com/avatar/10b2b854b5829dd13a15967c000ed2119b5faef67aca24d94c653c8ac550d85e?d=identicon)[weew](/maintainers/weew)

###  Code Quality

TestsPHPUnit

### Embed Badge

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

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

###  Alternatives

[laravel/dusk

Laravel Dusk provides simple end-to-end testing and browser automation.

1.9k39.6M290](/packages/laravel-dusk)[magento/magento2-functional-testing-framework

Magento2 Functional Testing Framework

15312.0M36](/packages/magento-magento2-functional-testing-framework)[nineinchnick/edatatables

Grid widget for the Yii Framework, wrapper for the DataTables jQuery plugin

173.2k](/packages/nineinchnick-edatatables)[link-cloud/fast-hyperf

LinkCloud Fast Hyperf

241.2k1](/packages/link-cloud-fast-hyperf)

PHPackages © 2026

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