PHPackages                             webshr/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. [API Development](/categories/api)
4. /
5. webshr/router

ActiveLibrary[API Development](/categories/api)

webshr/router
=============

A custom PHP router for the WP HTMX plugin, based on the Rareloop router package

0.1.0(2y ago)161MITPHPPHP ^8.0

Since Apr 4Pushed 2y agoCompare

[ Source](https://github.com/webshr/router)[ Packagist](https://packagist.org/packages/webshr/router)[ Docs](https://github.com/webshr/router)[ RSS](/packages/webshr-router/feed)WikiDiscussions main Synced 1mo ago

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

Webshr Router
=============

[](#webshr-router)

[![Latest Stable Version](https://camo.githubusercontent.com/9fb56e1daec1f316b278631c8d77a3cc03f943b67036c12f7693519a275311a9/68747470733a2f2f706f7365722e707567782e6f72672f7765627368722f726f757465722f762f737461626c65)](https://packagist.org/packages/webshr/router)[![CI](https://github.com/webshr/router/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/webshr/router/actions/workflows/ci.yml/badge.svg?branch=master)[![Coverage Status](https://camo.githubusercontent.com/c49ea5e2a7b4b6026108c5158aa4ce3ea24665d405ac7bdc642cfc29edd7f561/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f6769746875622f776562736872726f757465722f62616467652e737667)](https://coveralls.io/github/webshr/router)

A custom PHP Router for the WP HTMX WordPress plugin based on [Rareloop Router](https://github.com/Rareloop/router).

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

[](#installation)

```
composer require webshr/router

```

Usage
-----

[](#usage)

### Creating Routes

[](#creating-routes)

#### Map

[](#map)

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

```
use Webshr\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 `Webshr\Router\Controller` base class.

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

```
use Webshr\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 Webshr\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 Webshr\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
});
```

Contributing
------------

[](#contributing)

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

Credits
-------

[](#credits)

- [Rareloop Router](https://github.com/Rareloop/router)

License
-------

[](#license)

MIT

###  Health Score

19

—

LowBetter than 10% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity7

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity36

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% 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

Unknown

Total

1

Last Release

774d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/e08ba1b41e17610fdac64a99d5a1f32ffc9a065cc0e1bd1ea2f0eff8215e5f35?d=identicon)[enriquo](/maintainers/enriquo)

---

Top Contributors

[![enriquo](https://avatars.githubusercontent.com/u/11009257?v=4)](https://github.com/enriquo "enriquo (1 commits)")

---

Tags

wordpressrouterhtmx

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

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

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

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.8k18.5M1.6k](/packages/cakephp-cakephp)[rareloop/lumberjack-core

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

42155.0k19](/packages/rareloop-lumberjack-core)[thecodingmachine/graphqlite

Write your GraphQL queries in simple to write controllers (using webonyx/graphql-php).

5723.1M30](/packages/thecodingmachine-graphqlite)[rareloop/router

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

92178.9k4](/packages/rareloop-router)[los/los-rate-limit

Rate Limit Middleware for PHP

2737.9k1](/packages/los-los-rate-limit)[rubix/server

Deploy your Rubix ML models to production with scalable stand-alone inference servers.

632.3k](/packages/rubix-server)

PHPackages © 2026

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