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

ActiveLibrary[Framework](/categories/framework)

tccl/router
===========

A simple routing library

v5.0.1(2y ago)016MITPHPPHP &gt;=7.3.0

Since Apr 3Pushed 1y ago1 watchersCompare

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

READMEChangelogDependencies (1)Versions (19)Used By (0)

TCCL Routing Library
====================

[](#tccl-routing-library)

This project provides a class library for routing requests to a PHP application. The library is designed to be small and minimal.

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

[](#installation)

This library is available as a composer package. Require `tccl/router` in your composer.json file and then install.

```
composer require tccl/router
```

Interfaces
----------

[](#interfaces)

### Classes

[](#classes)

NameDescription`TCCL\Router\Router`Provides core routing functionality`TCCL\Router\RequestHandler`Provides interface for class-based request handlers`TCCL\Router\RouterException`Provides type for handling request handler exceptions### Traits

[](#traits)

NameDescription`TCCL\Router\RouterExceptionHandling`Optional trait to add exception handling to a router subclass`TCCL\Router\RouterMethodHandling`Optional trait to add method handling support to a router subclass`TCCL\Router\RouterRESTHandling`Optional trait to add REST API support to a router subclassUsage
-----

[](#usage)

### Creating a router

[](#creating-a-router)

Router provides a mechanism for routing control to a handler based on an input URI. It is very easy to set up and use. Just create an instance of type `TCCL\Router\Router`. The constructor takes two arguments:

1. A handler used as the default when a route does not match
2. The router base path (optional, defaults to none)

```
use TCCL\Router\Router;

function not_found(Router $router) {
	$router->contentType = Router::CONTENT_TEXT;
	$router->flush();

	echo "Not found\n";
}

$router = new Router('not_found');
```

#### Router Base path

[](#router-base-path)

When you create a router, you may specify a base path as the second constructor argument. This is useful for when an application is running under a sub-directory of the document root. The router will automatically convert the request URIs relative to the configured base path when matching against the routing table.

> You can also set the base path using the protected method `setBasePath` if you are subclassing `TCCL\Router\Router`.

Pro Tip: To allow your application to work arbitrarily under any sub-directory of the document root, calculate the base path using the path of the directory containing the entry point script and the value of the `DOCUMENT_ROOT` server variable.

```
// Given __FILE__:"/path/to/www/app/index.php"

$entryPointPath = dirname(__FILE__);
$documentRoot = $_SERVER['DOCUMENT_ROOT'];
$basePath = substr($entryPointPath,strlen($documentRoot));

// Given entryPointPath:"/path/to/www/app" and documentRoot:"/path/to/www",
// then we get basePath:"/app"

$router = new Router('not_found',$basePath);
```

This trick works if you have an entry point script (e.g. `index.php`) that is called for each route; the entry point script must be installed at the root of the project tree.

### Adding routes

[](#adding-routes)

Now you can add routes using the `addRoute()` or `addRoutesFromTable()` methods. Each route maps a request method and route specifier to a handler specifier.

```
// Method: Router::HTTP_GET (i.e. 'GET')
// Route Specifier: '/help'
// Handler Specifier: 'generate_help_page'

$router->addRoute(Router::HTTP_GET,'/help','generate_help_page');
$router->addRoute('GET','/\/help\/topics\/([0-9]+)/','generate_help_topic');

// The addRoutesFromTable() method allows multiple routes
// to be added from an array in one call.
$router->addRoutesFromTable([
	Router::HTTP_GET => [
		'/help' => 'generate_help_page',
		'/\/help\/topics\/([0-9]+)/' => 'generate_help_topic',
	],
]);
```

### Route specifiers

[](#route-specifiers)

A route specifier indicates the URI that identifies a route. This can either be an exact match or a regex match.

```
$router->addRoute(Router::HTTP_GET,'/page/home','load_home_page');

// The following route specifier matches URIs such as:
//   /page/1
//   /page/27
//   /page/33

// NOTE: forward-slashes in the URI spec must be properly escaped
// when using forward slashes to bracket the regex.
$router->addRoute(Router::HTTP_GET,'/\/page\/([0-9]+)/','load_page');
```

If the regex specified match groups, the matched values can be accessed via the `Router::$matches` property during request handling.

### Handler specifier

[](#handler-specifier)

A handler specifier identifies an executable context that can handle the route (i.e. the handler). The canonical implementation supports the following handler specifiers:

Specifier TypeExplanationCallableAnything that can be called as a function in PHPClass nameThe name of a class that implements `TCCL\Router\RequestHandler`, or, for sub-routing, `TCCL\Router\Router`Object instanceAn instance of a class that implements `TCCL\Router\RequestHandler`, or, for sub-routing, `TCCL\Router\Router`#### Handler specifier: callable

[](#handler-specifier-callable)

The simplest handler specifier is a callable. This can be a function or class method (static or non-static). Consult the [PHP manual on callables](https://www.php.net/manual/en/language.types.callable.php) for more.

#### Handler specifier: class name or instance of `TCCL\Router\RequestHandler`

[](#handler-specifier-class-name-or-instance-of-tcclrouterrequesthandler)

Typically, you will use a handler that implements the `TCCL\Router\RequestHandler` interface, as it provides more robust functionality. If you specify a class name, the router will create a new instance of the class if and when the route is executed. Otherwise, if you provide an instance, that instance will be used as-is when the route is executed.

When a route executes with a `RequestHandler`, it will invoke the `run()` method, passing the executing `Router` as the parameter.

```
use TCCL\Router\Router;
use TCCL\Router\RequestHandler;

class PageHandler implements RequestHandler {
	public static function not_found(Router $router) {

	}

	public function run(Router $router) {
		$pageNumber = (int)$router->matches[1];

	}
}

$router = new Router("PageHandler::not_found");
$router->addRoute(Router::HTTP_GET,'/\/page\/([0-9]+)/','PageHandler');
```

#### Handler specifier: sub-router

[](#handler-specifier-sub-router)

If the handler specifier is a class name or instance of type `TCCL\Router\Router`, then the router will invoke a sub-router. Sub-routers work well for cases when you want to classify a set of routes under a common prefix. The route specifier for a sub-router should be a regex that matches a common prefix, and the `Router::HTTP_ALL` special method specifier should be used to match any request method.

```
// Let 'APIRouter' process all URIs having a prefix of /api.
$router->addRoute(Router::HTTP_ALL,'/^\/api\//','APIRouter');
```

### Executing the router

[](#executing-the-router)

To actually route a request, you must execute the router using its `route` method. You must specify the HTTP method and URI to route. These values may be obtained from `_$SERVER` depending on your PHP SAPI.

```
$router->route($_SERVER['REQUEST_METHOD'],$_SERVER['REQUEST_URI']);
```

### Subclassing `Router`

[](#subclassing-router)

For non-trivial applications, subclassing the `TCCL\Router\Router` class can better encapsulate routing functionality.

```
use TCCL\Router\Router;
use TCCL\Router\RouterException;

class MyRouter extends Router {
	public function __construct() {
		parent::__construct(function(){
            throw new RouterException(
                404,
                'Not Found',
                'The specified resource was not found on this server.'
            );
        });

		$this->addRoutesFromTable([/* ... */]);
	}
}
```

### Additional router handling

[](#additional-router-handling)

The library provides additional handling support to account for a number of common use cases including:

- Exception handling
- Method handling
- REST handling

Additional handling is implemented as traits that you import into a custom `TCCL\Router\Router` subclass.

#### Exception handling

[](#exception-handling)

Router exception handling allows the router to handle exceptions in a convenient and streamlined manner. To add exception handling to a `Router` subclass, use trait `TCCL\Router\RouterExceptionHandling`.

You must also implement the `handleServerError` and `handleRouterError` methods to handle exceptions. The later method is for exceptions of type `TCCL\Router\RouterException` and the former is for any other exception.

#### Method handling

[](#method-handling)

Router method handling allows you to define a single handler class with multiple methods that handle each request. To add method handling to a `Router` subclass, use trait `TCCL\Router\RouterMethodHandling`.

Once method handling has been added, you can add a method name to a class name handler specifier (e.g. `Namespace\Class::methodName`). You do not need to implement `RequestHandler` in the handler class when using method handling. Handler methods may be either static or non-static and receive a single `Router` argument. For a static method, use the syntax `@class::method`.

```
use TCCL\Router\Router;
use TCCL\Router\RouterException;
use TCCL\Router\RouterMethodHandling;

class MyRouter extends Router {
	use RouterMethodHandling;

	public function __construct() {
		parent::__construct(function(){
            throw new RouterException(
                404,
                'Not Found',
                'The specified resource was not found on this server.');
        });

		$this->addRoute(Router::HTTP_GET,'/time','Handler::getTime');
		$this->addRoute(Router::HTTP_GET,'/favnum','@Handler::getFavoriteNumber')
	}
}

class Handler {
	public function getTime(MyRouter $router) {
		/* ... */
	}

    public static function getFavoriteNumber(MyRouter $router) {
	    /* ... */
    }
}
```

#### REST handling

[](#rest-handling)

REST handling adds additional functionality to a custom `Router` subclass that makes writing REST API endpoints using JSON more convenient. The handling allows your request handler to return a payload that is then automatically converted into JSON. The correct HTTP headers are also applied.

If the handler returns `null`, then HTTP `204` No Content is returned. The handler may return `false` to prevent any handling of the return value; this is useful for when the handler needs to perform its own response generation.

To add REST handling to a `Router` subclass, use trait `TCCL\Router\RouterRESTHandling`.

> Note: REST handling works well when paired with method handling.

```
use TCCL\Router\Router;
use TCCL\Router\RouterException;
use TCCL\Router\RouterMethodHandling;
use TCCL\Router\RouterRESTHandling;

class MyRouter extends Router {
	use RouterMethodHandling;
	use RouterRESTHandling;

	public function __construct() {
		parent::__construct(function(){
            throw new RouterException(
                404,
                'Not Found',
                'The specified resource was not found on this server.');
        });

		$this->addRoute(Router::HTTP_GET,'/time','Handler::getTime');
	}
}

class Handler {
	public function getTime(MyRouter $router) {
		$dt = new \DateTime;
		$repr = [
			'year' => (int)$dt->format('Y'),
			'month' => (int)$dt->format('m'),
			'date' => (int)$dt->format('d'),
			'hour' => (int)$dt->format('H'),
			'minute' => (int)$dt->format('i'),
			'second' => (int)$dt->format('s'),
			'tz' => $dt->getTimezone()->getOffset(),
		];

		return $repr;
	}
}
```

### Request Payload Verification

[](#request-payload-verification)

The library provides a mechanism for verifying a request payload that can avoid tedious boilerplate in the implementation of a request handler. Verification also helps to sanitize user input.

Payload verification functionality is defined in the `TCCL\Router\PayloadVerify` class, but is primarily accessed via the `TCCL\Router\Router::getPayloadVerify()` method.

#### Background

[](#background)

Payload verification validates a request parameter payload using a format argument.

```
$format = [
	'name' => 's',
	'email' => 's',
];
$payload = $router->getPayloadVerify($format);
```

The payload is generated from the request parameters. This works for any type of HTTP request method.

> Note: For HTTP `GET` requests, the data type for each parsed parameter will be `string`. You can apply promotions via payload verification, but you will want to make sure you are only type validating for `string`. For other request methods (e.g. `POST`), the content type of the request payload can allow for other data types to be encoded.

If verification fails, then a `TCCL\Router\PayloadVerifyException` is thrown. The `PayloadVerifyException` class is a sub-type of a `TCCL\Router\RouterException` having status code `400`. You can catch these exceptions and call `printDebug()` to obtain diagnostic information about how the payload failed verification.

#### Verification options

[](#verification-options)

The payload verification functionality has a number of options that can be configured:

Option NameDescriptionDefault Value`checkExtraneous`Determines if extraneous items in the payload cause verification to fail`true````
$format = [
	'name' => 's',
	'email' => 's',
];
$options = [
	'checkExtranous' => false,
];
$payload = $router->getPayloadVerify($format,$options);
```

#### Verification format

[](#verification-format)

The verification format argument indicates the names and expected data types for the request parameters in the payload. The format can also indicate other actions to perform such as type promotions and constraint checks.

The format argument is an associative array mapping the parameter names to verification specifier values.

```
$format = [
	'name' => [
		'first' => 's',
		'last' => 's',
	],
	'job_title' => 's',
	'address_lines' => ['s'],
	'age?' => 'i',
];
```

If a parameter name (e.g. `age`) contains a trailing `?` character (e.g. `age?`), then the parameter may be omitted (or `null`) and the payload will still verify correctly. See the entry for `age` in the above example.

If a scalar value is expected for a parameter, then the specifier is a string indicating the accepted types and any promotions/checks to apply. See the entry for `job_title` in the above example.

If an indexed array of values is expected for a parameter, then the specifier is an indexed singleton array containing a scalar specifier. This specifier is applied to each element in the input value. See the entry for `address_lines` in the above example.

If a nested associative array structure is expected for a parameter, then the specifier is a nested format array that is processed recursively. See the entry for `name` in the above example.

#### Building scalar format strings

[](#building-scalar-format-strings)

A scalar format string tells the verification system to perform a number of actions to verify that a scalar value is correct. A format string has the following base structure:

```
 [PROMOTION-SPECIFIER] [CHECK-SPECIFIER] ['?']

```

**Type Specifiers**: The type specifier portion of the string is the concatenation of one or more type specifier characters representing the set of types that are allowed. If the value does *not* match one of these types, then verification fails.

Type specifier characters are always lower-case letter or a symbol. The following type specifiers are provided in the current implementation:

Specifier CharacterTypeNotes`b`Boolean`s`String`i`Integer`f`Float`d`DoubleNote that double is functionally equivalent to floatExample type specifiers:

SpecifierMeaning`i`Allow only integer`si`Allow string or integer**Promotion Specifiers**: The promotion specifier indicates an optional promotion operation to perform after the type validation. A promotion is generally an operation that converts the value; most of the core promotions provided by the current implementation are type promotions.

The promotion specifier portion of the format string is a single upper-case letter or symbol. The following promotions are provided in the current implementation:

Specifier CharacterMeaning`B`Convert to boolean`S`Convert to string`I`Convert to integer`F`Convert to float`D`Convert to double (alias of float)`^`Trim string value (will promote to string)Example specifiers with promotion:

SpecifierMeaning`siS`Allow string or integer and promote to string`ibB`Allow integer or boolean and promote to boolean`s^`Allow string and trim**Check Specifiers**: The check specifier performs one or more additional validation actions on the value after the promotion. Check actions are predicate actions that fail validation when they return `false`.

The check specifier portion of the format string is the concatenation of one or more non-letter characters. The following check actions are provided in the current implementation:

Specifier CharacterMeaning`!`Ensures a string is not empty`+`Ensures a numeric value is positive`-`Ensures a numeric value is negative`*`Ensures a numeric value is non-negative`%`Ensures a numeric value is non-zeroExample specifiers with checks:

SpecifierMeaning`siI+`Ensures value is positive integer, accepts string representation of integer`s^!`Ensures trimmed string value is not empty#### Custom Types, Promotions and Checks

[](#custom-types-promotions-and-checks)

Using the `TCCL\Router\PayloadVerify` class, you can register custom type, promotion and check specifiers as described in the previous section. Example:

```
use TCCL\Router\PayloadVerify;

function is_user_id($value) : bool {
    if (!is_numeric($value)) {
	    return false;
    }

     $id = (int)$value;
     return $id >= 1;
}

PayloadVerify::registerType('u','is_user_id');
```

Note that when you register custom type, promotion or check specifiers, you must be careful to not override an existing specifier if you intend to keep using it. The `PayloadVerify` class will validate the specifiers to ensure there are no conflicts. Specifiers must meet the requirements listed below:

ItemRequirementsType SpecifierMust be lower-case letter `a..z`Promotion SpecifierMust be upper-case letter `A..Z` or a non-letter character; the set of non-letter promotion specifier characters must be mutually exclusive of the set of non-letter check specifier charactersCheck SpecifierMust be a non-letter character; the set of check specifier characters must be mutually exclusive of the set of non-letter promotion specifier characters

###  Health Score

29

—

LowBetter than 59% of packages

Maintenance26

Infrequent updates — may be unmaintained

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity67

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

Recently: every ~323 days

Total

18

Last Release

785d ago

Major Versions

v3.1.1 → v4.0.02018-01-22

v4.7.0 → v5.0.02023-06-14

PHP version history (2 changes)v4.3.1PHP &gt;=5.6.0

v5.0.0PHP &gt;=7.3.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/bbd14c78592d7f3654645137b4bc857040fd1ea093661a092219d10edc89fac7?d=identicon)[Roger Gee](/maintainers/Roger%20Gee)

---

Top Contributors

[![RogerGee](https://avatars.githubusercontent.com/u/5503540?v=4)](https://github.com/RogerGee "RogerGee (82 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

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

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

###  Alternatives

[laravel/telescope

An elegant debug assistant for the Laravel framework.

5.2k67.8M192](/packages/laravel-telescope)[spiral/roadrunner

RoadRunner: High-performance PHP application server and process manager written in Go and powered with plugins

8.4k12.2M84](/packages/spiral-roadrunner)[nolimits4web/swiper

Most modern mobile touch slider and framework with hardware accelerated transitions

41.8k177.2k1](/packages/nolimits4web-swiper)[laravel/dusk

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

1.9k36.7M259](/packages/laravel-dusk)[laravel/prompts

Add beautiful and user-friendly forms to your command-line applications.

708181.8M596](/packages/laravel-prompts)[cakephp/chronos

A simple API extension for DateTime.

1.4k47.7M121](/packages/cakephp-chronos)

PHPackages © 2026

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