API Documentation
This document provides a detailed reference for the public API of the codesaur/router package.
Table of Contents
RouterInterface
Namespace: codesaur\Router
The full router contract for the codesaur ecosystem. Requires 4 methods:
match()- request matchinggenerate()- reverse routing (route name -> URL)pattern()- client-side substitution patterngetRoutes()- introspection
Purpose:
- Provides an implementation-independent boundary so HTTP applications and Raptor controllers stay decoupled from any specific router
- Allows third-party routers (FastRoute, Symfony Routing, AltoRouter, etc.) to be adapted and used with
codesaur/http-application - Keeps concrete Router's internal API (
registerName(),registerMiddleware()) out of the interface
Methods
match(string $path, string $method): ?array
Find a route matching the incoming URL path and HTTP method.
Parameters:
string $path- The URL path to match (e.g./news/123)string $method- HTTP method (GET, POST, PUT, DELETE, PATCH...)
Returns: array|null
On a match, returns a fixed 3-element tuple array:
[0]- the callable to execute (Closure or[Class, 'method'])[1]- parameters extracted from the pattern (['id' => 10], etc.)[2]- middleware list ([]if none)
Returns null when no route matches.
Example - basic usage:
$result = $router->match('/news/10', 'GET');
if ($result === null) {
http_response_code(404);
return;
}
[$callable, $params, $middleware] = $result;
call_user_func_array($callable, $params);
Example - custom router emitting middleware:
class MyRouter implements RouterInterface
{
public function match(string $path, string $method): ?array
{
return [
$callable, // [0] callable
['id' => 10], // [1] params
[AuthMiddleware::class, RBACMiddleware::class] // [2] middleware
];
}
}
Integration with
codesaur/http-application: HTTP-Application forwards the entire match() result as the request'smatchattribute. Additional contextual data should be attached as request attributes from middleware, not as tuple extras.
generate(string $routeName, array $params = []): string
Reverse routing - build a URL from a route name.
Returns: string - Generated URL path (raw, not URL-encoded)
Throws:
\OutOfRangeException- if route name not found\InvalidArgumentException- if a parameter type does not match
NOTE: Encoding contract: parameter values are substituted raw - no percent-encoding. This is intentional and avoids double-encoding when handed to PSR-7 UriInterface::withPath().
Example:
$url = $router->generate('news-view', ['id' => 10]); // '/news/10'
pattern(string $routeName): string
Returns a URL pattern with filter prefixes stripped - ready for client-side substitution.
Returns: string - Pattern with filter prefixes stripped
Throws: \OutOfRangeException - if route name not found
Example:
$pattern = $router->pattern('news-view'); // '/news/{id}/{slug}'
getRoutes(): array
Returns the list of all registered routes (for introspection).
Structure - 2-tuple [callable, middleware] per (pattern, method):
[
pattern => [
method => [
[0] callable, // the handler
[1] middleware, // middleware list registered on this route
]
]
]
The pattern is the outer key (it already carries the params info) - no params placeholder is needed inside the entry. Match-time params are only computed by match().
Use cases:
- Listing all routes in an admin panel
- Auto-generating a sitemap
- Auto-generating API documentation
Example:
foreach ($router->getRoutes() as $pattern => $methods) {
foreach ($methods as $method => [$callable, $middleware]) {
echo "$method $pattern\n";
}
}
Route names: Naming is a separate concern - it's not returned by getRoutes(). For name introspection, use generate($name) to build a URL or pattern($name) to get a client-side pattern.
Router
Namespace: codesaur\Router
The lightweight Router class of the codesaur ecosystem.
Implements: RouterInterface
Description
This Router supports:
- Registering routes via dynamic
__call($router->GET('/news', ...)syntax) - Typed parameters:
{int:id},{float:price},{uint:page},{slug},{utf8:text} - Matching request path + HTTP method via
match() - Reverse routing via
generate()(name -> URL) - Per-route middleware via
Route::middleware()
Small, stable, framework-independent, usable standalone.
Constants
| Name | Value | Purpose |
|---|---|---|
FILTERS_REGEX | `'/{(int: | uint: |
INT_REGEX | '(-?\d+)' | Negative and positive integers |
UNSIGNED_INT_REGEX | '(\d+)' | Positive integers only (0 and up) |
FLOAT_REGEX | `'(-?\d+ | -?\d*.\d+)'` |
DEFAULT_REGEX | '([A-Za-z0-9%_,!~&)(=;\'\$.*][\@-]+)' | URL-safe characters (no spaces) |
UTF8_REGEX | '([A-Za-z0-9%_,!~&)(=;\'\$.*][\@ \x80-\xFF-]+)' | UTF-8 multibyte (Cyrillic, CJK, etc.) + spaces |
Methods
__call(string $method, array $properties): Route
Magic method - registers GET, POST, PUT, DELETE, etc. routes.
Parameters:
string $method- HTTP method name (GET,POST, ..., or compoundGET_POST)array<mixed> $properties-[0]=> route pattern (string)[1]=> callable (Closure or[Class, 'method'])
Returns: Route - Immutable Route object representing the registered route. Used for fluent APIs like ->name(...).
Throws:
\InvalidArgumentException- if pattern or callback is empty/invalid
Example:
$router->GET('/news/{int:id}', [NewsController::class, 'view'])->name('news-view');
$router->POST('/users', function() { ... });
$router->GET_POST('/api/data', [ApiController::class, 'data']); // multiple methods
registerName(string $ruleName, string $pattern): void
Internal API for registering a route name -> pattern mapping. Typical usage goes through Route::name() automatically.
Parameters:
string $ruleName- Route name (must be unique)string $pattern- The route pattern
Notes:
- Normal flow:
$router->GET(...)->name('foo')-Route::name()calls this internally - Direct usage: only when you want to attach a name post-hoc, after the route is registered
- Re-registering the same name overrides the previous pattern
Example:
// Standard usage (via Route::name() automatically):
$router->GET('/news/{int:id}', ...)->name('news-view');
// Post-hoc registration:
$router->GET('/foo', $handler);
$router->registerName('foo', '/foo');
registerMiddleware(string $pattern, string $method, array $middleware): void
Internal API for attaching middleware to a (pattern, method) route. Typical usage goes through Route::middleware([...]) automatically.
Parameters:
string $pattern- The route patternstring $method- HTTP method (supports compound'GET_POST')list<class-string|callable|\Psr\Http\Server\MiddlewareInterface> $middleware
Notes:
- Scope is bound to the (pattern, method) pair - isolated from middleware on other methods
- Compound methods (
GET_POST) fan out to each constituent method - Append semantics - multiple calls accumulate the middleware list
- Returned in match() tuple at position
[2] - Normal flow:
Route::middleware([...])calls this internally - Direct usage: base class auto-attach, or post-hoc additions
Example:
// Standard flow:
$router->POST('/api/users', $handler)
->middleware([CsrfMiddleware::class]);
// Post-hoc:
$router->GET('/foo', $handler);
$router->registerMiddleware('/foo', 'GET', [AuthMiddleware::class]);
match(string $path, string $method): ?array
Implementation of RouterInterface::match(). See the RouterInterface::match section above for the tuple shape and return contract.
codesaur Router implementation additions:
- HEAD -> GET auto-fallback (RFC 7231 sec. 4.3.2): If no explicit HEAD route is registered, HEAD requests automatically dispatch to the GET handler. The consumer is responsible for stripping the response body for HEAD requests.
generate(string $ruleName, array $params = []): string
Implementation of RouterInterface::generate(). See the RouterInterface::generate section above for full details.
pattern(string $ruleName): string
Implementation of RouterInterface::pattern(). See the RouterInterface::pattern section above for full details.
Template + JS usage example:
<script>
const URL = '<?= $router->pattern('news-view') ?>';
fetch(URL.replace('{id}', 42).replace('{slug}', 'hello'));
</script>
Route
Namespace: codesaur\Router
Immutable value object returned by Router::__call() when registering a route. Provides the ->name(...) fluent API.
Description
The Route class exists to keep the Router stateless:
- A registered route carries its own pattern context, so
name()andmiddleware()fluent methods operate without ambiguity - PHP 8.2
readonlyclass - immutable
Properties
public readonly string $pattern
The pattern of this route (e.g. /news/{int:id}). Read-only, cannot be modified.
Methods
name(string $ruleName): self
Attach a name to this route.
Parameters:
string $ruleName- Route name (must be unique)
Returns: self - This Route object (for chaining)
Notes:
- Strict mode - reassigning the same name to a different pattern throws
\LogicException - Internally calls
Router::registerName()
Example:
$router->GET('/news/{int:id}', $handler)->name('news.view');
middleware(array $middleware): self
Attach a list of middleware to this route. They run before the route handler.
Parameters:
list<class-string|callable|\Psr\Http\Server\MiddlewareInterface> $middleware- Middleware list
Returns: self - This Route object (for chaining)
Supported middleware types:
- class-string - PSR-15 MiddlewareInterface class (HTTP-Application instantiates at runtime)
- callable / Closure - function with signature
function($request, $handler) - MiddlewareInterface instance - pre-instantiated object
Notes:
- Append semantics - multiple calls accumulate the middleware list
- Internally calls
Router::registerMiddleware() - Returned in match() tuple at position
[2]
Example:
// Simple usage
$router->POST('/api/users', $handler)
->middleware([CsrfMiddleware::class, RBACMiddleware::class]);
// Append semantics
$router->GET('/admin', $handler)
->middleware([AuthMiddleware::class])
->middleware([AdminOnlyMiddleware::class]);
// Registered: [Auth, AdminOnly]
// Closure middleware
$router->GET('/test', $handler)
->middleware([
function($req, $handler) {
return $handler->handle($req->withAttribute('test', true));
}
]);