PHPackages                             haszi/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. [Utility &amp; Helpers](/categories/utility)
4. /
5. haszi/router

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

haszi/router
============

A learning exercise of a router/dispatcher

1.0.0(2y ago)06PHPPHP &gt;=8.0

Since Nov 27Pushed 2y ago1 watchersCompare

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

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

Router / Dispatcher
===================

[](#router--dispatcher)

A router and dispatcher written to learn about routers/dispatchers and the difficulties in writing these.

### Requirements

[](#requirements)

PHP 8.0+

### Installation

[](#installation)

```
composer require haszi/router

```

Features
--------

[](#features)

### Supported

[](#supported)

- GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS HTTP request methods
    - shorthand methods for each of these
- static URIs
- route parameters
- Closure and 'controller@method' type handlers
- route groups
- custom handler for routes not found
- before route middleware

Getting started
---------------

[](#getting-started)

### Basic usage

[](#basic-usage)

```
use Haszi\Router\Router;
use Haszi\Router\Dispatcher;

$router = new Router();
$router->addRoute('*', '/greet', function () { echo 'Hello World!'; });
$router->get('/users', 'Users@list');

$dispatcher = new Dispatcher($router);

$dispatcher->dispatch('GET', '/greet);
```

### HTTP Methods

[](#http-methods)

The router supports the following HTTP methods:

- GET
- HEAD
- POST
- PUT
- DELETE
- PATCH
- OPTIONS

Using \* for HTTP method will add a route for each of the above HTTP methods.

For each of the above methods there is also a corresponding shorthand method in the router. The shorthand for \* is the method 'any'.

```
$router->get('/route', $handler);
$router->head('/route', $handler);
$router->post('/route', $handler);
$router->put('/route', $handler);
$router->delete('/route', $handler);
$router->patch('/route', $handler);
$router->options('/route', $handler);

$router->any('/route', $handler);
```

Multiple HTTP methods for one path can also be defined using an array of HTTP method strings.

```
$router->addRoute(['GET','HEAD'], '/route', $handler);
```

### Handlers

[](#handlers)

The router accepts a closure or a string in the form of 'controller@method' as a handler. The latter will be stored as a closure that will try to call the method on the class statically (if the method is available on the class, is static, non abstract and public) or will try to instantiate the class and call the method on it. On multiple route matches, the dispatcher will only call the handler of the first registered route.

```
// Closures
$router->get('/ping', fn () => 'pong');

$router->get('/sum/{firstNum}/{secondNum}', function ($first, $second) {
    return $first + $second;
});

// Using the controller@method notation
$router->get('/users', 'Users@list');
```

### Routes

[](#routes)

#### Static Routes

[](#static-routes)

Static routes are routes that do not have a dynamic, variable component. These routes will be matched exactly against the URI.

```
$router->get('/login', 'Login@login');
```

#### Dynamic Routes

[](#dynamic-routes)

Dynamic routes are routes of which certain parts can be variable. The router supports dynamic routes by using placeholder ({} notation) or PCRE regular expressions.

Please note that placeholders will accept any input, i.e. are the equivalent of a (.\*?) regular expression. On dispatching a route, All placeholder values will be passed to the matching route's handler.

When using regular expressions, all variables returned by capturing groups will be passed to the matching route's handler.

```
// 'id' which can be one ore more of any of characters
// will be passed to Users::update() / Users->update()
$router->put('/users/{id}', 'Users@update');

// 'id' which will be one or more digits
// will be passed to Users::update() / Users->update()
$router->get('/artist/(\d+)', 'Users@update');
```

##### Optional parameters

[](#optional-parameters)

Parts of a route can be made optional by making a portion of the regular expression optional by using the ? token. (/\\d+)?

```
// will match /albums/2023 or /albums/acdc or /albums
$router->get('/albums/{year}?', $handler);

// will match /albums/2023 or /albums
$router->get('/albums(/d+)?', $handler);
```

### Route groups

[](#route-groups)

Routes can be defined as a group which will apply the same prefix to each route defined in that group.

```
$router->group('/users/{id}', function ($router) use ($id) {
    $router->get('/posts', 'Users@getPosts');
    $router->get('/comments', 'Users@getComments');
});

// is equivalent to
$router->get('/users/{id}/posts', 'Users@getPosts');
$router->get('/users/{id}/comments', 'Users@getComments');
```

### Handler for unknown routes

[](#handler-for-unknown-routes)

A custom handler can be defined in the router for when no routes were found. If there is such a handler registered, the dispatcher will call this handler when no matching routes are found. There can only one handler be defined at a time. When a new handler is set, the previous one is replaced.

```
$this->router->setRouteNotFoundHandler(fn () => 'Route not found');

// returns 'Route not found'
$this->router->getRouteNotFoundHandler();
```

### Before route middleware

[](#before-route-middleware)

Before route middleware are handlers that are executed before the actual route handler is called. Please note that all registered middleware will be executed for a route, and they will be executed in the order they have been registered.

```
$router->before('GET', '/hello-world', fn () => 'Hello ');
$router->before('GET', '/hello-world', fn () => 'World!');

$beforeRoute = $router->getBeforeMiddleware('GET', 'hello-world');

$result = '';
foreach ($beforeRoute as $middleware) {
    $result .= ($middleware['route']->getHandler())();
}
// $result contains 'Hello World!'
```

### Dispatcher

[](#dispatcher)

The dispatcher accompanying the router is a basic implementation that exercises all the functionalities of the router. I.e. it executes the registered middleware, 'route not found' handler if one registered (or throws an exception otherwise) and calls the registered route handler with the optional route parameters, and returns its result to the caller.

```
$dispatcher = new Dispatcher($router);

$dispatcher->dispatch('GET', '/about-us);
```

Acknowledgments / Credits
-------------------------

[](#acknowledgments--credits)

The concepts behind and implementation of this router/dispatcher was inspired and influenced by [bramus/router](https://github.com/bramus/router) and [FastRoute](https://github.com/nikic/FastRoute). Additional inspiration was drawn from [Symfony](https://symfony.com/doc/current/routing.html) and [Laravel](https://laravel.com/docs/10.x/routing).

###  Health Score

21

—

LowBetter than 18% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity4

Limited adoption so far

Community4

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

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

903d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/78c63df1a4fbe5fd0177db633a2a9bb44f72b35b54803c40e49a22dba1e92772?d=identicon)[haszi](/maintainers/haszi)

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Psalm

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[suncat/mobile-detect-bundle

Symfony2/3/4 bundle for detect mobile devices, managing mobile view types, redirect to mobile version.

4035.3M7](/packages/suncat-mobile-detect-bundle)[snowdog/frontools

Set of front-end tools for Magento 2, based on Gulp.js

4241.3M1](/packages/snowdog-frontools)[spatie/laravel-dashboard

A dashboard for Laravel

568156.1k94](/packages/spatie-laravel-dashboard)[brick/schema

Schema.org library for PHP

5163.7k1](/packages/brick-schema)[ibericode/vat-bundle

Bundle for using ibericode/vat in a Symfony environment

21254.5k](/packages/ibericode-vat-bundle)[sylius/taxonomy

Taxonomies - categorization of domain models in PHP projects.

14435.6k10](/packages/sylius-taxonomy)

PHPackages © 2026

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