A simple, lightweight PHP routing system available as a Composer package.
- Static routes –
/about,/contact,/api/v1/status - Dynamic routes –
/products/$product-slug,/users/$id,/users/$userId/posts/$postId - All common HTTP methods:
GET,POST,PUT,PATCH,DELETE, andany() - Fluent (chainable) API
- Customisable 404 (Not Found) and 405 (Method Not Allowed) handlers
- Path normalisation: trailing slashes, query strings, and dot-segments (
.,..) are handled automatically
- PHP 8.0 or higher
composer require tandrezone/zroute<?php
require 'vendor/autoload.php';
use zRoute\Router;
$router = new Router();
// Static route
$router->get('/', fn($p) => print "Home page\n");
// Dynamic route — parameter syntax: $paramName
$router->get('/products/$product-slug', function (array $params) {
echo "Product: " . htmlspecialchars($params['product-slug']) . "\n";
});
// 404 handler
$router->notFound(function (string $path) {
http_response_code(404);
echo "404 – Not found: " . htmlspecialchars($path) . "\n";
});
// Dispatch the current HTTP request
$router->run();Dynamic segments start with $. The name may contain letters, digits, underscores _, and hyphens -.
| Pattern | Example URL | $params |
|---|---|---|
/about |
/about |
[] |
/users/$id |
/users/42 |
['id' => '42'] |
/products/$product-slug |
/products/red-sneakers |
['product-slug' => 'red-sneakers'] |
/users/$userId/posts/$postId |
/users/7/posts/99 |
['userId' => '7', 'postId' => '99'] |
Dynamic segments match one path segment only (they never cross a /).
$router->get(string $pattern, callable $handler): static
$router->post(string $pattern, callable $handler): static
$router->put(string $pattern, callable $handler): static
$router->patch(string $pattern, callable $handler): static
$router->delete(string $pattern, callable $handler): static
$router->any(string $pattern, callable $handler): static // all common methods
$router->addRoute(string $method, string $pattern, callable $handler): staticEvery method returns $this so calls can be chained:
$router
->get('/', fn($p) => 'home')
->get('/about', fn($p) => 'about')
->post('/users', fn($p) => 'create user');// Called when no route matches the path (HTTP 404)
$router->notFound(function (string $path): void {
http_response_code(404);
echo "404 Not Found";
});
// Called when the path matches a route but the method does not (HTTP 405)
$router->methodNotAllowed(function (string $method, string $path): void {
http_response_code(405);
echo "405 Method Not Allowed";
});// Dispatch the real HTTP request (reads $_SERVER)
$router->run(): mixed
// Dispatch a request manually (useful in tests, CLI scripts)
$router->dispatch(string $method, string $path): mixedSee examples/index.php for a complete working example.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]location / {
try_files $uri $uri/ /index.php?$query_string;
}composer install
./vendor/bin/phpunitMIT