PHPackages                             mindplay/timber - 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. [HTTP &amp; Networking](/categories/http)
4. /
5. mindplay/timber

ActiveLibrary[HTTP &amp; Networking](/categories/http)

mindplay/timber
===============

Timber is a request router with regular expression support, high performance, and a developer-friendly, human-readable API

3.0.0(2y ago)648.4k↓22.7%1[3 issues](https://github.com/mindplay-dk/timber/issues)BSD-3-ClausePHPPHP ^8.0

Since May 28Pushed 2y ago3 watchersCompare

[ Source](https://github.com/mindplay-dk/timber)[ Packagist](https://packagist.org/packages/mindplay/timber)[ RSS](/packages/mindplay-timber/feed)WikiDiscussions master Synced 1mo ago

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

mindplay/timber
===============

[](#mindplaytimber)

[![PHP Version](https://camo.githubusercontent.com/487804500a039aee09e5d93e41b745b4cd68dcc0fdf801e67d26d30b93f83358/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d382e302532422d626c75652e737667)](https://packagist.org/packages/mindplay/timber)[![Build status](https://github.com/mindplay-dk/timber/actions/workflows/ci.yml/badge.svg)](https://github.com/mindplay-dk/timber/actions/workflows/ci.yml)

Timber is a router library with regular expression support, high performance, and a developer-friendly, human-readable API.

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

[](#installation)

Install the latest version with `composer require mindplay/timber`

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

[](#introduction)

This package provides a minimal router facility: a place to register path patterns, and a means of resolving these to controller names.

Usage
-----

[](#usage)

In the examples below, we assume that handlers (attached using `get()`, `post()`, etc.) are controller class-names - in your application, they might be component IDs for a DI container, file-names, or whatever else you like.

Basic usage of the router looks like this:

```
use mindplay\timber\Router;
use mindplay\timber\Result;
use mindplay\timber\Error;

require __DIR__ . '/vendor/autoload.php';

$router = new Router();

// Defining route for one HTTP method
$router->route('news')->get(ListNews::class);

// Defining route for several HTTP methods
$router->route('/')->get(ShowHomePage::class)->post(PostComments::class);

// Defining a route with regular expression param
$news_route = $router->route('news/')->get(ShowNews::class);

// Defining another route with symbolic param
$router->route('users/')->get(ShowUser::class);

// Defining static route that conflicts with previous route, but static routes have high priority
$router->route('news/all')->get(ShowAllNews::class);

// Defining a wildcard route, matching e.g. "categories/foo", "categories/foo/bar", etc.:
$router->route('categories/')->get(ShowCategory::class);

// Resolve HTTP method and URL:

$method = 'GET';
$url = '/news/1';

$result = $router->resolve($method, $url);

if ($result instanceof Error) {
    header("HTTP/1.1 {$result->status} ${result->message}");
    // ...error response here...
    return;
} else {
    // ...dispatch $result->handler with $result->params...
}
```

If you're building a set of routes under the same parent route, you can continue building from a parent route - for example:

```
$admin = $router->route('admin')->get(AdminMenu::class);

$admin->route('users')->get(AdminUserList::class);
$admin->route('groups')->get(AdminGroupList::class);
```

This example will route `/admin` to `AdminMenu`, and `/admin/users` to `AdminUserList`, etc.

This also feature enables modular reuse of route definitions - for example:

```
$build_comment_routes = function (Route $parent) {
    $parent->route('comments')->get(ShowComments::class);
    $parent->route('comments/new')->get(ShowCommentForm::class)->post(PostComment::class);
}

$build_comment_routes($router->route('articles/'));
$build_comment_routes($router->route('products/'));
```

This example creates two identical sets of routes for displaying and posting comments for two different parent routes.

Optimization
------------

[](#optimization)

You can save and restore the defined routes:

```
use mindplay\timber\Router;

require __DIR__ . '/vendor/autoload.php';

$router = new Router();
$router->route('/')->get(ShowHomePage::class);
$router->route('/news')->get(ShowNews::class);

$routes = $router->getRoutes();

$anotherRouter = new Router();
$anotherRouter->setRoutes($routes);

$method = 'GET';
$url = '/news';

$result = $anotherRouter->resolve($method, $url);
```

The point is, you can serialize/unserialize the routes that have been built, and store them in a cache somewhere, to avoid the initialization overhead. For most projects, this would be considered a micro-optimization - the overhead of building an extremely high number of routes (hundreds or thousands) may make this worthwhile in a very large modular project.

Hacking
-------

[](#hacking)

To run the test-suite, navigate to the project root folder and type:

```
php test/test.php

```

Design Notes
------------

[](#design-notes)

During the development of this library, a [design problem](commit/8bb93921c0a8b90d97f0143c0eebdf4ba44b0294)was identified, which required us to make a trade-off. This library did at one point have URL creation as a feature, but after carefully weighing the pros and cons, it was decided to forego this feature, in favor of simpler implementation and support for caching.

Use-cases for [three different approaches](https://gist.github.com/mindplay-dk/feb4768dbb118c651ba0)were explored and evaluated - our [whiteboard](https://goo.gl/photos/CZLk7iJCzeJfS3A58) summarizes the pros and cons as we saw them, and the approach without URL creation was unanimously our favorite, as it leads to the greatest simplicity, both in the library and in the use-case, and supports caching.

The first trade-off is that we don't get to use closures (which can't be serialized) and thereby do not get any direct static coupling between the route and controller/action/params - we do get static coupling to the controller class-name, by using the `::class` constant.

The other trade-off is that we can't have a URL creation feature within the router itself, as this leads to either complexity (with the addition of a named route registry as per [case 1](https://gist.github.com/mindplay-dk/feb4768dbb118c651ba0#file-router-1-php)) or prevents caching (as per [case 2](https://gist.github.com/mindplay-dk/feb4768dbb118c651ba0#file-router-2-php) - after some discussion, we decided URL creation provides only a small benefit, guaranteeing that URL creation is consistent with defined patterns; but also, we value the freedom to fully customize URL creation on a case-by-case basis using simpler code (as per [case 3](https://gist.github.com/mindplay-dk/feb4768dbb118c651ba0#file-router-3-php)) and as such the absence of URL creation can actually be seen as a benefit.

Acknowledgements
----------------

[](#acknowledgements)

Timber started as a fork of [TreeRoute](https://github.com/baryshev/TreeRoute) by [Vadim Baryshev](https://github.com/baryshev), the API and feature-set quickly grew into something else entirely. What does carry over from the original fork, is great performance.

###  Health Score

37

—

LowBetter than 83% of packages

Maintenance10

Infrequent updates — may be unmaintained

Popularity35

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity75

Established project with proven stability

 Bus Factor1

Top contributor holds 68.3% 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 ~322 days

Recently: every ~696 days

Total

11

Last Release

782d ago

Major Versions

v1.1.0 → 2.0.02015-07-27

2.3.1 → 3.0.02024-03-28

PHP version history (3 changes)v1.0.0PHP &gt;=5.4.0

2.3.1PHP ^7.3 || ^8.0

3.0.0PHP ^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/9445f567f43ee7a963270651e40e533634586f959e4df3d5398d001b1cb49be8?d=identicon)[mindplay.dk](/maintainers/mindplay.dk)

---

Top Contributors

[![mindplay-dk](https://avatars.githubusercontent.com/u/103348?v=4)](https://github.com/mindplay-dk "mindplay-dk (28 commits)")[![baryshev](https://avatars.githubusercontent.com/u/552706?v=4)](https://github.com/baryshev "baryshev (12 commits)")[![Nerixel](https://avatars.githubusercontent.com/u/4003914?v=4)](https://github.com/Nerixel "Nerixel (1 commits)")

---

Tags

routerrouting

### Embed Badge

![Health badge](/badges/mindplay-timber/health.svg)

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

###  Alternatives

[symfony/routing

Maps an HTTP request to a set of configuration variables

7.6k789.4M1.8k](/packages/symfony-routing)[nikic/fast-route

Fast request router for PHP

5.3k92.4M668](/packages/nikic-fast-route)[altorouter/altorouter

A lightning fast router for PHP

1.3k3.4M68](/packages/altorouter-altorouter)[aura/router

Powerful, flexible web routing for PSR-7 requests.

5231.5M67](/packages/aura-router)[aplus/routing

Aplus Framework Routing Library

2491.6M3](/packages/aplus-routing)[pmjones/auto-route

Automatically routes HTTP request to action classes.

20158.6k6](/packages/pmjones-auto-route)

PHPackages © 2026

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