SkillAgentSearch skills...

Router

A PHP Router that uses ReactPHP/HTTP under the hood

Install / Use

/learn @cmdstr-graveyard/Router
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

This is now abandonware!

If you still need a router in React/Http, checkout Tnapf/Router


~~CommandString/Router~~

A router package built that uses ReactPHP/HTTP under the hood

Table of Contents

Installation

composer require commandstring/router

Getting started

You first need to create a ReactPHP SocketServer

$socket = new \React\Socket\SocketServer("127.0.0.1:8000");

Then create a router instance

$router = new \Router\Http\Router($socket, true);

The second parameter is whether dev mode should be enabled or not you can read about dev mode here

create some routes then begin listening for requests

$router->listen();

Routing

You can then add routes by using the match method

use Router\Http\Methods;

$router->match([Methods::GET], "/", function() { /* ... */ });

You can listen for more methods by adding them to the array

$router->match([Methods::GET, Methods::POST], "/", function() { /* ... */ });

Routing Shorthands

Shorthands for single request methods are provided

$router->get('pattern', function() { /* ... */ });
$router->post('pattern', function() { /* ... */ });
$router->put('pattern', function() { /* ... */ });
$router->delete('pattern', function() { /* ... */ });
$router->options('pattern', function() { /* ... */ });
$router->patch('pattern', function() { /* ... */ });
$router->head('pattern', function() { /* ... */ });

You can use this shorthand for a route that can be accessed using any method:

$router->all('pattern', function() { /* ... */ });

Route Patterns

Route Patterns can be static or dynamic:

  • Static Route Patterns contain no dynamic parts and must match exactly against the path part of the current URL.
  • Dynamic Route Patterns contain dynamic parts that can vary per request. The varying parts are named subpatterns and are defined using either Perl-compatible regular expressions (PCRE) or by using placeholders

Static Route Patterns

A static route pattern is a regular string representing a URI. It will be compared directly against the path part of the current URL.

Examples:

  • /about
  • /contact

Usage Examples:

$router->get('/about', function($req, $res) {
    $res->getBody()->write("Hello World");
    return $res;
});

Dynamic PCRE-based Route Patterns

This type of Route Pattern contains dynamic parts which can vary per request. The varying parts are named subpatterns and are defined using regular expressions.

Examples:

  • /movies/(\d+)
  • /profile/(\w+)

Commonly used PCRE-based subpatterns within Dynamic Route Patterns are:

  • \d+ = One or more digits (0-9)
  • \w+ = One or more word characters (a-z 0-9 _)
  • [a-z0-9_-]+ = One or more word characters (a-z 0-9 _) and the dash (-)
  • .* = Any character (including /), zero or more
  • [^/]+ = Any character but /, one or more

Note: The PHP PCRE Cheat Sheet might come in handy.

The subpatterns defined in Dynamic PCRE-based Route Patterns are converted to parameters that are passed into the route handling function. The prerequisite is that these subpatterns need to be defined as parenthesized subpatterns, which means that they should be wrapped between parenthesis:

// Bad
$router->get('/hello/\w+', function($req, $res, $name) {
    $res->getBody()->write('Hello '.htmlentities($name));
    return $res;
});

// Good
$router->get('/hello/(\w+)', function($req, $res, $name) {
    $res->getBody()->write('Hello '.htmlentities($name));
    return $res;
});

Note: The leading / at the very beginning of a route pattern is not mandatory, but is recommended.

When multiple subpatterns are defined, the resulting route handling parameters are passed into the route handling function in the order they are defined:

$router->get('/movies/(\d+)/photos/(\d+)', function($req, $res, $movieId, $photoId) {
    $res->getBody()->write('Movie #'.$movieId.', photo #'.$photoId);
    return $res;
});

Dynamic Placeholder-based Route Patterns

This type of Route Pattern is the same as Dynamic PCRE-based Route Patterns, but with one difference: they don't use regexes to do the pattern matching but they use the more easy placeholders instead. Placeholders are strings surrounded by curly braces, e.g. {name}. You don't need to add parens around placeholders.

Examples:

  • /movies/{id}
  • /profile/{username}

Placeholders are easier to use than PRCEs, but offer you less control as they internally get translated to a PRCE that matches any character (.*).

$router->get('/movies/{movieId}/photos/{photoId}', function($req, $res, $movieId, $photoId) {
    $res->getBody()->write('Movie #'.$movieId.', photo #'.$photoId);
    return $res;
});

Note: the name of the placeholder does not need to match the name of the parameter that is passed into the route handling function:

$router->get('/movies/{foo}/photos/{bar}', function($req, $res, $movieId, $photoId) {
    $res->getBody()->write('Movie #'.$movieId.', photo #'.$photoId);
    return $res;
});

Optional Route Subpatterns

Route subpatterns can be made optional by making the subpatterns optional by adding a ? after them. Think of blog URLs in the form of /blog(/year)(/month)(/day)(/slug):

$router->get(
	'/blog(/\d+)?(/\d+)?(/\d+)?(/[a-z0-9_-]+)?',
	function($req, $res, $year = null, $month = null, $day = null, $slug = null) {
		if (!$year) { 
			$res->getBody()->write("Blog Overview");
			return $res;
		}
		
		if (!$month) {
			$res->getBody()->write("Blog year overview");
			return $res;
		}
		
		if (!$day) {
			$res->getBody()->write("Blog month overview");
			return $res;
		}
		
		if (!$slug) {
			$res->getBody()->write("Blog day overview");
			return $res;
		}
		
		$res->getBody()->write('Blogpost ' . htmlentities($slug) . ' detail');
		return $res;
	}
);

The code snippet above responds to the URLs /blog, /blog/year, /blog/year/month, /blog/year/month/day, and /blog/year/month/day/slug.

Note: With optional parameters, it is important that the leading / of the subpatterns is put inside the subpattern itself. Don't forget to set default values for the optional parameters.

The code snipped above unfortunately also responds to URLs like /blog/foo and states that the overview needs to be shown - which is incorrect. Optional subpatterns can be made successive by extending the parenthesized subpatterns so that they contain the other optional subpatterns: The pattern should resemble /blog(/year(/month(/day(/slug)))) instead of the previous /blog(/year)(/month)(/day)(/slug):

$router->get('/blog(/\d+(/\d+(/\d+(/[a-z0-9_-]+)?)?)?)?', function($req, $res, $year = null, $month = null, $day = null, $slug = null) {
    // ...
});

Note: It is highly recommended to always define successive optional parameters.

To make things complete use quantifiers to require the correct amount of numbers in the URL:

$router->get('/blog(/\d{4}(/\d{2}(/\d{2zz}(/[a-z0-9_-]+)?)?)?)?', function($req, $res, $year = null, $month = null, $day = null, $slug = null) {
    // ...
});

Controllers

When defining a route you can either pass an anonymous function or an array that contains a class along with a static method to invoke. Additionally your controller must return an implementation of the PSR7 Response Interface

Anonymous Function Controller

$router->get("/home", function ($req, $res) {
    $res->getBody()->write("Welcome home!");
    return $res;
});

Class Controller

I have a class with a static method, your handler MUST be a static method!

class Home {
	public static function handler($req, $res) {
		$res->getBody()->write("Welcome home!");
		return $res;
	}
}

I then replace the anonymous function with an array the first item being the class string and the second key being the name of the static method.

$router->get("/home", [Home::class, "handler"]);

404 Handler

Defining a 404 handler is required and is similar to creating a route. You can also have different 404 pages for different patterns.

To setup a 404 handler you can invoke the map404 method and insert a pattern for the first parameter and then your controller as the second.

$router->map404("/(.*)", function ($req, $res) {
	$res->getBody()->write("{$req->getRequestTarget()} is not a valid route");
	return $res;
});

500 handler

Defining a 500 handler is recommended and is exactly the same as mapping a 404 handler.

$router->map500("/(.*)", function ($req, $res) {
	$res->getBody()->write("An error has happened internally :(");
	return $res;
});

Note that when in development mode your 500 error handler will be overrode

Middleware

Middleware is software that connects the model and view in an MVC application, facilitating the communication and data flow between these two components while also providing a layer of abstraction, decoupling the model and view and allowing them to interact without needing to know the details of how the other component operates.

A good example is having before middleware that makes sure the user is an administrator before they go to a restricted page. You could do this in your routes controller for every admin page but that would be redundant. Or for after middleware, you may have a REST API that returns a JSON response. You can have after m

View on GitHub
GitHub Stars5
CategoryDevelopment
Updated4mo ago
Forks1

Languages

PHP

Security Score

87/100

Audited on Nov 22, 2025

No findings