PHPackages                             onnerby/doeroute - 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. onnerby/doeroute

ActiveLibrary[Framework](/categories/framework)

onnerby/doeroute
================

A fast and intuitive Router

2.1.2(5y ago)0118[1 PRs](https://github.com/onnerby/doeroute/pulls)MITPHP

Since Sep 4Pushed 1y agoCompare

[ Source](https://github.com/onnerby/doeroute)[ Packagist](https://packagist.org/packages/onnerby/doeroute)[ Docs](https://github.com/onnerby/doeroute)[ RSS](/packages/onnerby-doeroute/feed)WikiDiscussions master Synced 2mo ago

READMEChangelog (1)Dependencies (6)Versions (8)Used By (0)

doeroute
========

[](#doeroute)

A fast and intuitive PHP Router. Don't make things more complicated than they needs to be. But maybe a little more flexible than [this awesome simple router](https://www.taniarascia.com/the-simplest-php-router/)

\\Doe\\Router
-------------

[](#doerouter)

The Doe\\Router is a (single file) router where you build your routes using subpaths and closures. The advantage is that the closures are only called if the subpath match which makes it SUPER FAST and easy to follow. It also makes it very easy to delegate specific paths to some kind of controller/action-pattern. After I wrote Doe\\Router I found [FastRoute](https://github.com/nikic/FastRoute) that is really fast and very similar to this router and probably a bit more flexible when it comes to multiple variables embedded in the path. They are similar in many ways, but the pattern is slightly different with both pros and cons.

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

[](#installation)

```
composer require onnerby/doeroute

```

### Basic example

[](#basic-example)

```
$router = new \Doe\Router(['GET', 'POST']);
$router->path('blog', function($router) {
	// This closure is called if the route starts with /blog

	$router->path('list', 'GET', function ($router) {
		// This is returned when route goes to "/blog/list"
		return "List of all posts";
	});
	$router->path('tags', 'GET', function ($router) {
		// This is returned when route goes to "/blog/tags"
		return "List of all tags";
	});
	$router->pathVariable('/^([0-9]+)$/', function ($router, $postId) {
		// This is returned when route goes to something like "/blog/1234"
		return "Post " . $postId;
	});
});

// Find route and output the results
echo $router->route($_SERVER['REQUEST_METHOD'], $_SERVER['DOCUMENT_URI']);
```

### Controller example

[](#controller-example)

If you are building bigger webapps you may like to delegate routes to some kind of controller. The Doe\\Router is not connected to any kind of pattern for this - but it's still super simple to delegate the route.

```
// Main app
$router = new \Doe\Router(['GET', 'POST']);
// Route everything starting with /project to our \Controller_Project::route
$router->path('project', ['Controller_Project', 'route']);

...
```

Controller:

```
class Controller_Project
{
	public static function route($router)
	{
		$controller = new self;

		$router->path('list', 'GET', [$controller, 'list'] );

		$router->pathVariable('/^([0-9]+)$/', function ($router, $projectId) use ($controller) {

			// Any generic method needed for everything
			$controller->getProject($projectId);

			$router->path('overview', 'GET', [$controller, 'overview'] );
			$router->path('save', 'POST', [$controller, 'save'] );

		});

		// Lets also map the "/project" path to the controllers "list" action
		$router->pathEmpty('GET', [$controller, 'list']);

		$router->pathNotFound([$controller, 'error']);

	}

	private function getProject($projectId) { /* Get the project somehow from a database? */ }

	public function list($router)
	{
		// Full path to this route is "/project/list"
		return 'List projects';
	}

	public function overview($router, $projectId)
	{
		// Full path to this route is "/project/1234/overview"
		return 'Project ' . $projectId . ' overview';
	}

	public function save($router, $projectId)
	{
		// Full path to this route is "/project/1234/save"
		return 'Saved project ' . $projectId;
	}

	public function error($router)
	{
		// Anything not found under "/project/xxxxx"
		return 'You look lost. How can I help?';
	}

}
```

### Filters

[](#filters)

You may also use filters to execute stuff before the routes.

```
// In main app
function authorize($router, $verb) {
	// Authorize user somehow
	if (!($user = getUser())) {
		// Returning anything in "before"-filters will interrupt the route.
		return 'You do not have access to this area.';
	}
}

$router = new \Doe\Router(['GET', 'POST']);
$router->filter('authorize', function($router) {
	$router->path('restrictedarea', function ($router) {
		return "Warning: Resticted area. Authorized personnel only.";
	});
});
...
```

Why another router?
===================

[](#why-another-router)

Most routers I've used overcomplicate things. A router is used on the web for parsing the path of the URL to some kind of action. So lets just do that.

A [URL](https://en.wikipedia.org/wiki/URL) is separated by a `/` and while most routers will define all routes for all paths at once, I decided to parse one segment at a time. I mean - you probably want to do something on each segment anyway in the end. For instance if I build the following routes:

- `/user` listing all users
- `/user/123` showing a users profile page
- `/user/123/badges` showing a users profile page with the users "badges"
- `/user/123/projects` showing a users profile page with the users projects

etc, etc. You will not go directly to define `/user/[0-9]+/project` but will define the different segments down to the whole final path.

The upside with this is that we don't need to define all routes for each request. Instead we look for the first segments and once that's found, we parse the next segment. There is also the advantage that everything inside `/user/[0-9]+` has its own callable where we can check access to the user before sending the route futher down the next segment.

###  Health Score

30

—

LowBetter than 64% of packages

Maintenance30

Infrequent updates — may be unmaintained

Popularity10

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity63

Established project with proven stability

 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

Every ~147 days

Total

5

Last Release

1857d ago

Major Versions

1.0.0 → 2.0.02019-09-09

### Community

Maintainers

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

---

Top Contributors

[![onnerby](https://avatars.githubusercontent.com/u/1788794?v=4)](https://github.com/onnerby "onnerby (27 commits)")

---

Tags

closuresfiltersrouterphprouter

###  Code Quality

TestsPHPUnit

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/onnerby-doeroute/health.svg)

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

###  Alternatives

[leafs/leaf

Elegant PHP for modern developers

1.3k44.3k9](/packages/leafs-leaf)[scrawler/router

An Fully Automatic RESTful PHP Router.

552.2k3](/packages/scrawler-router)[viames/pair

lightweight PHP framework as the air

103.0k1](/packages/viames-pair)

PHPackages © 2026

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