Appearance
Routing
Table of Contents
- Available methods
- Request object
- Route parameters
- Named routes
- Signed routes
- Route list command
- Route groups
- Route middlewares
Phenix routes are based on the amphp/http-server-router package, providing a simple and elegant syntax through a facade:
php
use Phenix\Facades\Route;
Route::get('/greetings', function () {
return response()->plain('Hello, world!');
});You can define your routes in the routes/api.php file. The routes are loaded through the RouteServiceProvider service provider. In the configuration file config/app.php, you can see the provider being referenced.
Available methods
The currently supported HTTP verbs are:
php
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);Request object
The server router automatically injects the request into the callback that is invoked by each route:
php
use Phenix\Facades\Route;
use Amp\Http\Server\Request;
Route::get('/users', function (Request $request) {
// ..
});Route parameters
You can pass parameters in routes, such as IDs of records in the database. The parameters are not injected into the callback but are contained in the Request object. The amphp/http-server-router package is based on the nikic/FastRoute package:
php
Route::get('/users/{user}', function (Request $request) {
$userId = $request->route()->integer('user');
// code
});Multiple route parameters
php
Route::get('/invoices/{invoice}/payments/{payment}', function (Request $request) {
$invoiceId = $request->route()->integer('invoice');
$paymentId = $request->route()->integer('payment');
// code
});Patterns in route parameters
php
Route::get('/users/{user:[0-9]+}', function (Request $request) {
// ..
});
Route::get('/users/{user:\d+}', function (Request $request) {
// ..
});Named routes
Route names will allow Phenix to generate URLs easily. You can assign each route a name:
php
Route::get('/users', function (Request $request) {
// ..
})->name('users.index');
Route::get('/users/{user}', function (Request $request) {
// ..
})->name('users.show');URL generation is available through:
route(...)helper for named routesurl(...)helper for path-based URLsPhenix\Facades\Urlfacade for advanced URL features (including signed URLs)
Signed routes
Signed routes let you generate tamper-resistant URLs using an HMAC signature based on application key encryption.
Phenix supports:
- permanent signed URLs
- temporary signed URLs with expiration
- request signature validation
Generating signed URLs
Use Url::signedRoute(...) to generate a signed URL:
php
use Phenix\Facades\Url;
$url = Url::signedRoute('unsubscribe', ['user' => 42]);Signature:
php
Url::signedRoute(
BackedEnum|string $name,
array $parameters = [],
DateTimeInterface|DateInterval|int|null $expiration = null,
bool $absolute = true
): stringTemporary signed URLs
Use Url::temporarySignedRoute(...) for expiring links.
php
use DateInterval;
use DateTimeImmutable;
use Phenix\Facades\Url;
// Expires in 5 minutes
$url1 = Url::temporarySignedRoute('downloads.show', 300, ['file' => 'report.pdf']);
// Expires in 1 hour
$url2 = Url::temporarySignedRoute('downloads.show', new DateInterval('PT1H'), ['file' => 'report.pdf']);
// Expires at an exact timestamp
$url3 = Url::temporarySignedRoute('downloads.show', new DateTimeImmutable('+30 minutes'), ['file' => 'report.pdf']);Signature:
php
Url::temporarySignedRoute(
BackedEnum|string $name,
DateTimeInterface|DateInterval|int $expiration,
array $parameters = [],
bool $absolute = true
): stringProtecting routes with ValidateSignature
Use ValidateSignature middleware to require a valid signature:
php
use Phenix\Facades\Route;
use Phenix\Routing\Middlewares\ValidateSignature;
Route::get('/signed/{user}', fn () => response()->plain('Ok'))
->name('signed.show')
->middleware(ValidateSignature::class);If the signature is invalid or missing, the middleware returns 403 with:
Invalid signature.
If the signature exists but is expired, it returns 403 with:
Signature has expired.
You can also validate manually via the URL facade:
php
use Amp\Http\Server\Request;
use Phenix\Facades\Url;
function isValid(Request $request): bool
{
return Url::hasValidSignature($request);
}Additional API:
php
Url::signatureHasNotExpired(Request $request): boolIgnoring query parameters during validation
When validating signatures, you can ignore extra tracking query params:
php
use Amp\Http\Server\Request;
use Phenix\Facades\Url;
$isValid = Url::hasValidSignature($request, ignoreQuery: ['tracking', 'utm_source']);You can also provide a closure:
php
$ignore = fn (): array => ['fbclid', 'utm_source'];
$isValid = Url::hasValidSignature($request, ignoreQuery: $ignore);Ignored parameters are excluded when reconstructing the URL used for signature comparison.
Route list command
Phenix includes a built-in console command to inspect all registered routes:
sh
php phenix route:listBy default, the command renders a table with the columns:
MethodPathNameMiddlewareParams
Filtering routes
You can filter the route list using command options:
sh
php phenix route:list --name=users
php phenix route:list --method=GET
php phenix route:list --path=/admin--name: filters by route name (partial match).--method: filters by HTTP method.--path: filters by path (partial match).
You can combine filters:
sh
php phenix route:list --method=GET --name=users --path=/adminJSON output
Use --json to export routes in JSON format:
sh
php phenix route:list --jsonThis is useful for tooling, scripting, or custom route inspection workflows.
Route groups
Route groups allow sharing middlewares, assigning route prefixes, and route name prefixes. You can create nested route groups and share their properties in the hierarchy in which the routes are defined.
php
use Phenix\Http\Middlewares\AcceptJsonResponses;
Route::middleware(AcceptJsonResponses::class)
->name('admin')
->prefix('admin')
->group(function (Route $route) {
$route->get('users', fn () => response()->plain('User index'))
->name('users.index');
$route->get('users/{user}', fn () => response()->plain('User details'))
->name('users.show');
$route->name('accounting')
->prefix('accounting')
->group(function (Route $route) {
$route->get('invoices', fn () => response()->plain('Invoice index'))
->name('invoices.index');
$route->prefix('payments')
->name('payments')
->group(function (Route $route) {
$route->get('pending', fn () => response()->plain('Payments index'))
->name('pending.index');
});
});
});Route middlewares
Route middlewares are the most specific middleware layer. Use them when behavior should run only for selected endpoints.
Scope model:
- Global (
app.middlewares.global): runs before the router handles the request and applies to any incoming request. - Router (
app.middlewares.router): applies to all routes. - Route/Group (
->middleware(...),Route::middleware(...)->group(...)): applies only to selected routes.
For middleware execution order and registration details in config/app.php, see the Middlewares guide.
php
Route::get('/users/{user}', function (Request $request) {
// ..
})->middleware([
SanitizeRequest::class,
CheckUserIsAdmin::class,
]);
Route::middleware(Authenticated::class)
->group(function (Route $route) {
$route->get('/profile', fn () => response()->plain('Profile'));
});