rkr / simple-router
A simple wildcard matcher
Installs: 92
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 3
Forks: 0
Open Issues: 0
pkg:composer/rkr/simple-router
Requires
- php: >= 8.1
- psr/http-message: *
- rkr/php-ioc-contract: *
- rkr/php-structure-locator: ^1.0.0
- rkr/recursive-array-accessor: 3.*
- symfony/http-foundation: ^7.3
- symfony/routing: ^7.3
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.0
- rkr/php-di-ioc-adapter: *
- zircote/swagger-php: >= 6.0.0
Suggests
- zircote/swagger-php: Generate OpenAPI specs from PHP attributes/annotations.
This package is auto-updated.
Last update: 2026-02-10 09:01:42 UTC
README
A small, framework-agnostic router built on Symfony Routing that matches PSR-7 requests and provides optional dispatch helpers.
Status
- PHP: >= 8.1
- Extensions: ext-json (used for JSON body parsing)
- PSR: psr/http-message >= 1.0 (PSR-7 interfaces)
- Core dependencies: symfony/routing ^7.3, symfony/http-foundation ^7.3
- Frameworks: framework-agnostic (Symfony Routing used internally)
- Stability: not explicitly stated; pin versions in production
- Style: minimal, unopinionated routing + optional dispatch layer
Installation
composer require rkr/simple-router
Quick start
<?php declare(strict_types=1); require __DIR__ . '/vendor/autoload.php'; use Kir\Http\Routing\Common\ServerRequest; use Kir\Http\Routing\Common\Uri; use Kir\Http\Routing\RouterBuilder; $routerBuilder = new RouterBuilder(); $routerBuilder->get( name: 'hello', pattern: '/hello/{name}', callable: fn (string $name) => "Hello {$name}", params: ['secure' => true] ); $request = new ServerRequest( method: 'GET', uri: new Uri('http://example.test/hello/Ada'), queryParams: [], parsedBody: [] ); $router = $routerBuilder->build(); $route = $router->lookup($request); if ($route === null) { http_response_code(404); exit('Not found'); } $params = $route->allParams(); $result = ($route->callable)($params['name']); echo $result;
Usage
Register routes
$routerBuilder->add( name: 'article.show', methods: ['GET', 'HEAD'], pattern: '/articles/{id}', callable: fn (string $id) => "Article {$id}", params: ['cache' => true] ); $routerBuilder->post( name: 'session.create', pattern: '/login', callable: fn () => 'Logged in', params: [] );
Lookup and params
$router = $routerBuilder->build(); $route = $router->lookup($request); if ($route !== null) { $allParams = $route->allParams(); // Route params and query params are merged into $allParams. // Raw body data is available under $allParams['httpData']. }
Dispatch with RouteHandler
use Ioc\MethodInvoker; use Kir\Http\Routing\RouteHandler; use Kir\Http\Routing\Router; use Kir\Http\Routing\Common\ServerRequest; use Kir\Http\Routing\Common\Uri; use Kir\Http\Routing\ResponseTypes\HtmlResponse; $methodInvoker = /* your MethodInvoker implementation */; $handler = new RouteHandler($methodInvoker); $handler->getRouter()->get( name: 'home', pattern: '/', callable: fn () => new HtmlResponse('<h1>Hi</h1>'), params: [] ); $request = new ServerRequest( method: 'GET', uri: new Uri('http://example.test/'), queryParams: [], parsedBody: [] ); $response = $handler->dispatch($request, RouterBuilder::createResponse());
Public API overview
RouterBuilder
- Purpose: register routes and build a
Router. - Stability: not explicitly stated (see project-level stability).
- Constructor:
__construct()creates an empty route collection. - Methods:
add(string $name, array $methods, string $pattern, callable $callable, callable|array|object $params): selfregisters a route and returns the router for chaining.get/post/put/delete(...)are convenience wrappers aroundadd().addDefinitions(array $definitions): selfregisters routes from a config array.build(): Routercreates a router instance for lookups.createServerRequestFromEnv(?Uri $uri = null): ServerRequestbuilds a request from globals; throwsRuntimeExceptionon invalid JSON input.createResponse(): Responsereturns a basic PSR-7 response with an emptyStreambody.
Router
- Purpose: match a PSR-7 request to a
Route. - Stability: not explicitly stated (see project-level stability).
- Constructor: built via
RouterBuilder::build(). - Methods:
lookup(ServerRequestInterface $request): ?Routereturns a matched route ornull(no exception on missing routes).
RouteHandler
- Purpose: dispatch a matched route using a
MethodInvoker, and translateAbstractHttpResponseresults into PSR-7 responses. - Stability: not explicitly stated (see project-level stability).
- Constructor:
__construct(MethodInvoker $methodInvoker)registers default post-processors for built-in response types. - Methods:
dispatch(ServerRequestInterface $request, ResponseInterface $response): ResponseInterfaceruns the route callable and returns a PSR-7 response; throws routing or post-processing exceptions (see Error handling).addPreProcessor(callable $fn): voidregisters hooks executed before dispatch.setPostProcessor(string $className, Closure $handler): voidmaps response types to PSR-7 responses.setErrorHandler(callable $errorHandler): voidcustomizes error handling.
Route
- Purpose: the matched route with params and attributes.
- Stability: not explicitly stated (see project-level stability).
- Constructor: value object with
name,method,queryParams,postValues,rawParsedBody,callable,attributes. - Methods:
allParams(): arraymerges route params, query params, and parsed body and provideshttpDatametadata.
Common PSR-7 helpers
Common\ServerRequest,Common\Response,Common\Uri,Common\Stream: lightweight PSR-7 implementations used by the router (you can also pass any PSR-7 request/response types).
Response types
Built-in ResponseTypes implement AbstractHttpResponse and are handled by the default RouteHandler post-processors:
HtmlResponse,JsonResponse,BinaryContentResponse,MimeTypeContentResponse,NotFoundResponse- Redirect helpers:
RedirectToResponse,RedirectToHttpsResponse,RedirectToRefererResponse,RedirectToSelfResponse,RedirectionResponse CSVDownloadGeneratorResponse,ViewResponse
Configuration
- No global configuration required.
- Route attributes are passed via the
$paramsargument and exposed asRoute::$attributes. - Customize dispatch behavior via
RouteHandlerpre-processors, post-processors, and the error handler.
OpenAPI integration
This project can generate a route configuration from OpenAPI PHP attributes (via zircote/swagger-php).
The generator scans your PHP code for OpenAPI HTTP method attributes (e.g. #[OA\Get], #[OA\Post])
and writes a route config file that can be loaded into RouterBuilder.
Generate route config from OpenAPI attributes
php bin/generate-openapi-routes.php \ --index-path var/openapi-index.xml \ --route-config-path var/openapi-routes.php \ src tests
src/testsare the scan paths (you can pass multiple).--index-pathand--route-config-pathare required unless you setOPENAPI_INDEX_PATHand/orOPENAPI_ROUTE_CONFIG_PATH.- The output file is a PHP array with
routesentries you can load as definitions.
Load the generated routes
/** @var array{routes?: array<int, array<string, mixed>>} $definitions */ $definitions = require __DIR__ . '/var/openapi-routes.php'; $routerBuilder = new RouterBuilder(); $routerBuilder->addDefinitions($definitions); $router = $routerBuilder->build();
OpenAPI metadata in routes
OpenAPI-specific metadata (e.g. security) is stored under
$route->attributes['openapi']. This lets you enforce auth or other policies
at dispatch time without mixing them into your normal route params.
Required OpenAPI info
swagger-php requires an Info definition in the scanned code, e.g.:
use OpenApi\Attributes as OA; #[OA\Info(title: 'Example API', version: '1.0.0')] final class OpenApiSpec {}
Error handling
Router::lookup()returnsnullwhen no route matches or the matcher fails.RouterBuilder::createServerRequestFromEnv()throwsRuntimeExceptionif JSON input cannot be read or decoded.RouteHandler::dispatch()sets HTTP status codes and rethrows exceptions for:- Route not found (
RouteNotFoundException, 400) - Method not registered (
MethodNotRegisteredException, 400) - Invalid return type or missing post-processor (
InvalidReturnTypeException,NoPostProcessorDefinedForTypeException) - Unknown errors (
UndefinedRouterExceptionor the original exception, 500)
- Route not found (
Testing
composer install vendor/bin/phpunit -c tests.xml composer phpstan
Contributing and support
- Issues and pull requests are welcome via the repository's issue tracker.
License
MIT. See LICENSE.