HTTP Routing
Basic Routing
You will define All the routes for your application in the routes/web.php file. The most basic PHP-Framework routes simply accept a URI and a controller mapping string:
$router->get('foo', 'HomeController@index');
$router->post('foo', 'HomeController@store');
<?php
namespace App\Http\Controllers;
use MacropaySolutions\Kernel\Http\Request;
use MacropaySolutions\Kernel\Http\Response;
class HomeController extends \MacropaySolutions\Framework\Routing\Controller
{
public function store(Request $request): Response
{
//
}
}
Cache Routing
These commands are available in PHP-Framework template. Routes are auto-cached on --no-dev composer environments via an Ahead-of-Time (AOT) compiler.
php run route:cache
php run route:clear
Domain Routing
PHP-Framework enables restricting routes to specific domain and generating absolute URLs for named routes via route function.
To enable domain enforcement and absolute URL generation, the domain parameter must include valid URLs without trailing slash or port.
-
Correct:
‘domain’ => ‘https://api.example.com’ ‘domain’ => [‘https://api.example.com’, ‘https://api-custom.example.com’]
-
Incorrect:
‘domain’ => ‘api.example.com’ ‘domain’ => ‘https://api.example.com/’ ‘domain’ => ‘https://api.example.com:80’
If a domain is provided without a scheme (e.g., api.example.com) or app.url has no scheme:
- named routes will fall back to relative paths (e.g.,
/users) because the system cannot safely assume the intended protocol. - the domain check will be skipped, and the route will resolve on any host hitting the application.
When a valid URL is provided in the route group OR app.url config, all named routes (route('name')) will automatically return the full absolute URL. Example: https://api.example.com/v1/profile
The matching logic is port-agnostic. A configuration for http://localhost will correctly match a request to http://localhost:8000, making it safe for local microservice development.
Note:
All aliases and URIs must be unique despite having multiple domains! Use prefixes for route uri to accomplish that.
When domains is a list of urls,
route('alias')will use/return the last url from the list.When same uri is registered multiple times
route('alias')will use/return the last url from the last registration.In debug mode same alias used multiple times will be logged.
The
domainkey can be used in route groups.Create and use/uncomment the GLOBAL (mandatory GLOBAL) middleware \App\Http\Middleware\TrustProxies::class, in \App\Application::registerExplicitBindingsMap or in bootstrap/app.php:
$app->middleware([
\App\Http\Middleware\TrustProxies::class,
]);
This is needed for proper domain check from current request. See requests for more details. Failing to do this will silently result in 404 responses!
Available Router Methods
The router allows you to register routes that respond to any HTTP verb using target controller mapping actions:
$router->query($uri, $action);
$router->get($uri, $action);
$router->post($uri, $action);
$router->put($uri, $action);
$router->patch($uri, $action);
$router->delete($uri, $action);
$router->options($uri, $action);
$router->match(['GET', 'POST'], $uri, $action);
The Action Parameter
The $action parameter can be a simple controller mapping string, or an array containing the target controller and additional route attributes (like route names, middleware, or specific domains):
// String Action
$router->get('users', 'UserController@index');
// Array Action
$router->get('users', [
'uses' => 'UserController@index',
'as' => 'users.index',
'middleware' => 'auth',
'domain' => 'https://api.example.com'
]);
Route Parameters
Required Parameters
Of course, sometimes you will need to capture segments of the URI within your route. For example, you may need to capture a user’s ID from the URL. You may do so by defining route parameters:
$router->get('user/{id}', 'UserController@show');
You may define as many route parameters as required by your route:
$router->get('posts/{postId}/comments/{commentId}', 'CommentController@show');
Route parameters are always encased within “curly” braces. The parameters will be passed into your controller’s method when the route is executed.
Note: Route parameters cannot contain the
-character. Use an underscore (_) instead.
Optional Parameters
You may define optional route parameters by enclosing part of the route URI definition in [...]. So, for example, /foo[bar] will match both /foo and /foobar. Optional parameters are only supported in a trailing position of the URI. In other words, you may not place an optional parameter in the middle of a route definition:
$router->get('user[/{name}]', 'UserController@index');
Regular Expression Constraints
You may constrain the format of your route parameters by defining a regular expression in your route definition. Standard dynamic regex filters do not impact the high-speed routing tier:
$router->get('user/{name:[A-Za-z]+}', 'UserController@showByName');
Named Routes
Named routes allow the convenient generation of URLs for specific routes. You may specify a name for a route using the as array key when defining the route:
$router->get('profile', ['as' => 'profile', 'uses' => 'UserController@showProfile']);
You may also specify route names for controller actions:
$router->get('profile', [
'as' => 'profile', 'uses' => 'UserController@showProfile'
]);
Generating URLs To Named Routes
Once you have assigned a name to a given route, you may use the route’s name when generating URLs via the global route function:
$url = route('profile');
$url = route('profile', ['id' => 3]);
If the named route defines parameters, you may pass the parameters as the second argument to the route function. The given parameters will automatically be inserted into the URL in their correct positions:
$router->get('user/{id}/profile', ['as' => 'profile', 'uses' => 'UserController@showProfile']);
$url = route('profile', ['id' => 1]);
Route Groups
Route groups allow you to share route attributes, such as middleware or namespaces, across a large number of routes without needing to define those attributes on each individual route. Shared attributes are specified in an array format as the first parameter to the $router->group method.
To learn more about route groups, we’ll walk through several common use-cases for the feature.
Middleware
To assign middleware to all routes within a group, you may use the middleware key in the group attribute array. Middleware will be executed in the order you define this array:
$router->group(['middleware' => 'auth'], function () use ($router) {
$router->get('/', 'DashboardController@index');
$router->get('user/profile', 'UserController@profile');
});
Namespaces
Another common use-case for route groups is assigning the same PHP namespace to a group of controllers. You may use the namespace parameter in your group attribute array to specify the namespace for all controllers within the group:
$router->group(['namespace' => 'Admin'], function() use ($router)
{
// Using The "App\Http\Controllers\Admin" Namespace...
$router->group(['namespace' => 'User'], function() use ($router) {
// Using The "App\Http\Controllers\Admin\User" Namespace...
});
});
Route Prefixes
The prefix group attribute may be used to prefix each route in the group with a given URI. For example, you may want to prefix all route URIs within the group with admin:
$router->group(['prefix' => 'admin'], function () use ($router) {
$router->get('users', 'AdminUserController@index');
});
You may also use the prefix parameter to specify common parameters for your grouped routes:
$router->group(['prefix' => 'accounts/{accountId}'], function () use ($router) {
$router->get('detail', 'AccountController@detail');
});
Note: The PHP-Kernel Hybrid Trie Engine
PHP-Kernel features a zero-regex tiered routing design architecture. Requests are resolved via an instant O(1) Hash Shield for static routes, and an O(K) Native Prefix Tree matching paths token-by-token.
1. Trailing Slash Normalization Because the Trie walking mechanism evaluates string delimiters natively, endpoints like
/users/1and/users/1/map to the identical execution leaf automatically without triggering extra middleware overhead.2. The 404 Firewall & 405 Behavior To maximize network scanning defense and eliminate lookup leaks, basic and dynamic routes handled by the native Trie engine will return a
404 Not Foundinstead of405 Method Not Allowed. However, Complex Routes handled by thenikic/fast-routefallback will still return a405to retain strict compliance.3. Understanding Complex Route Fallbacks During the
route:cacheAOT compilation phase, the builder separates standard hot-paths from “Complex Routes.” Pure dynamic paths (even those utilizing inline validation parameters like/user/{id:\d+}) are not complex and run on the raw Trie.True Complex Routes are isolated into an independent queue and delegated to a highly scoped instance of
FastRouteonly if the initial Trie walk misses:
- Complex Path Rules: Greedy wildcards (
/{any:.*}), multi-variable inline formats (/export-{date}.csv), or adjacent variables without explicit separation (/{code}{number}).For maximum throughput and immunity to resource-draining malicious routing requests, keep your routing file entirely clean of the Complex Queue.
CRITICAL ARCHITECTURAL WARNING
Because PHP-Framework’s high-performance router utilizes native
strtok()loops to parse URI nodes with zero memory allocations, this framework is strictly designed for a stateless, isolated PHP-FPM execution architecture.Do NOT run PHP-Framework with long-running, multithreaded, or coroutine-based application servers (such as Swoole, OpenSwoole, or RoadRunner).
Because
strtok()relies on a single global internal pointer within the PHP thread state, concurrent asynchronous requests sharing the same process worker will overwrite each other’s routing tokens mid-flight. This will result in critical security vulnerabilities, including routing desynchronization, cross-user data leaks, and authentication middleware bypasses.