PHP Luminova: Method-Based Routing
Method-Based Routing with URI prefixes simplifies route management, improving performance and organization by using method calls instead of attributes for request handling.
Luminova Method-Based Routing allows you to define routes by calling methods on the Luminova\Routing\Router class.Each route is mapped using an HTTP request verb (GET, POST, PUT, etc.) or CLI-specific methods, instead of using attributes.
This approach is simple, explicit, and easy to debug.
Routes are strictly defined inside the /routes/ directory.You can either:
Place routes in the default files:
/routes/web.php/routes/api.php/routes/cli.php(for CLI commands)
- Or split routes into multiple files, where the file name represents the URL prefix. This improves performance by loading only what is needed.
Prefix Configuration
Method-Based Routing supports URI prefixes.Prefixes allow the router to load only the relevant route file instead of loading every route into memory and searching through them.
This reduces overhead and improves performance, especially in large applications.
Prefixes are configured in /public/index.php, using either:
- An Array Configuration: for simplicity and performance.
- Or
Luminova\Routing\Prefixclass class for IDE friendly.
The configuration is then passed to the router using the context() method in the main entry file.
Important Notes:
- The prefix name must match the route file name inside
/routes/.For example, a prefix ofapimust map to/routes/api.php.- Any configured error handler (such as
onApiError) must exist in the target controller class (for example,App\Errors\Controllers\ErrorControlleror your custom error controller).
Examples
This approach keeps your codebase clean by separating routing concerns by context.Each context clearly defines its URI prefix and error handler, making the routing setup easy to scan and understand at a glance.
Using the Prefix Class
When you use the Prefix class, any request that starts with a given prefix is automatically routed to the matching file inside /routes/.
For example, all requests starting with /api will load routes from /routes/api.php.
The Prefix class accepts two arguments:
$prefix (
string)The URI prefix name.$onError (
Closure | array<int, string> | null)An optional 404 error handler for this route context.
// /public/index.php
use Luminova\Boot;
use Luminova\Routing\Prefix;
use App\Errors\Controllers\ErrorController;
Boot::http()->router->context(
new Prefix(Prefix::WEB, [ErrorController::class, 'onWebError']),
new Prefix(Prefix::API, [ErrorController::class, 'onApiError']),
new Prefix('admin', [ErrorController::class, 'onAdminError']),
new Prefix(Prefix::CLI),
// ...
)->run();This method is explicit and easy to follow, especially when working with a small to medium number of route groups.
Using Array Configuration
You can also define prefixes using associative arrays.This avoids creating Prefix objects at runtime and can be more efficient when working with many route contexts.
Each array supports the following keys:
prefix (
string)The URI prefix name.error (
Closure | array | null)Optional 404 error handler for this context.
// /public/index.php
use Luminova\Boot;
use App\Errors\Controllers\ErrorController;
Boot::http()->router->context(
[
'prefix' => 'web',
'error' => [ErrorController::class, 'onWebError']
],
[
'prefix' => 'api',
'error' => [ErrorController::class, 'onApiError']
],
[
'prefix' => 'admin',
'error' => [ErrorController::class, 'onAdminError']
],
[
'prefix' => 'cli'
],
// ...
)->run();Tip
- Use Prefix objects when you want clearer intent, type safety, and better IDE support.
- Use array configuration when optimizing for performance with many prefixes.
Both approaches work the same way at runtime—choose the one that best fits your project size and style.
HTTP Request Handling
HTTP Controller Class
You can define a controller class without using attributes.Controllers handle requests for a specific context or route prefix.
// /app/Controllers/Http/AdminController.php
namespace App\Controllers\Http;
use Luminova\Base\Controller;
class AdminController extends Controller
{
/**
* Optional setup when the controller is created.
* Here, we set a subfolder for admin-related templates.
* All templates will resolve from "/resources/Views/admin/".
*/
protected function onCreate(): void
{
$this->tpl->setFolder('admin');
}
/**
* Handle login requests.
*
* @return int Status code indicating success or failure.
*/
public function login(): int
{
// Perform login logic
return STATUS_SUCCESS;
}
/**
* Display the dashboard view.
*
* @return int Status code
*/
public function dashboard(): int
{
return $this->view('dashboard');
}
}
- Methods in the controller map to routes defined in the route file (e.g.,
/routes/admin.php).onCreate()is optional and is useful for setup like specifying a template folder.
HTTP Route Prefix Context File
After registering a route prefix in public/index.php, you must create a matching route file inside the /routes/ directory.The file name must be the same as the prefix name for the router to load it correctly.
For example, if you register the prefix admin, create the file /routes/admin.php.
// /routes/admin.php
<?php
/**
* The following variables are available in this file:
*
* @var \Luminova\Routing\Router $router The router instance.
* @var \Luminova\Foundation\Core\Application $app The application instance.
* @var string $context The current route context name.
*/
use Luminova\Routing\Router;
Router::get('/admin/', 'AdminController::login');
Router::get('/admin/dashboard', 'AdminController::dashboard');Examples:
Once this file exists, any request that starts with /admin will automatically load and use the routes defined in admin.php.
https://example.com/adminhttps://example.com/admin/foohttps://localhost/example.com/public/admin(Development)
CLI Command Handling
The CLI commands uses a dedicated controller class and route context, similar to HTTP controllers but designed for command-line usage.
CLI Controller Class
A CLI controller defines the group, command name, description, usages, and methods for handling commands.
// /app/Controllers/Cli/CommandController.php
namespace App\Controllers\Cli;
use Luminova\Base\Command;
use Luminova\Command\Terminal;
class CommandController extends Command
{
/**
* CLI command group name.
* Commands in the same group are organized together.
*/
protected string $group = 'demo';
/**
* Command name.
*/
protected string $name = 'demo-command';
/**
* Usage instructions.
* Shows users how to execute this command.
*/
protected array|string $usages = [
'php index.php demo <command> <arguments>'
];
/**
* Command description.
* Brief explanation of what the command does.
*/
protected string $description = 'This CLI demo command.';
/**
* Display help message for the command.
*
* @param array $helps Help arguments or options.
* @return int STATUS_ERROR to use default CLI help behavior.
*/
public function help(array $helps): int
{
return STATUS_ERROR;
}
/**
* Demo "hello" command.
*
* @return int STATUS_SUCCESS if command succeeds, STATUS_ERROR on failure
*/
public function hello(): int
{
$message = $this->input->getAnyOption('message', 'm', 'Hello World!');
// Output message to terminal (optionally colorized)
Terminal::write($message);
return STATUS_SUCCESS;
}
}CLI Route Prefix Context File
The /routes/cli.php file registers CLI commands to their controller methods using the router.
// /routes/cli.php
<?php
/**
* Available variables in this file:
*
* @var \Luminova\Routing\Router $router The router instance.
* @var \Luminova\Foundation\Core\Application $app The application instance.
* @var string $context The current route context name.
*/
use Luminova\Routing\Router;
Router::group('demo', static function(): void {
Router::command('hello', 'CommandController::hello');
});Usage:
The CLI command is executed using:
cd /public
php index.php demo hello --message="Command Example Message"This example allows:
- Grouping related CLI commands (
demogroup in this case) - Mapping commands to controller methods
- Automatic handling of CLI arguments and options
Prefix Class Definition
- Namespace:
Luminova\Routing\Prefix - Final class: cannot be subclassed
The Prefix class defines a routing context for a specific URI prefix, linking it to a route file and optional error handler.
Constants
Predefined Prefixes
Luminova reserves some common URI prefixes. You can define your own, but these built-ins cover most standard scenarios:
| Constant | Value | Reserved | Description |
|---|---|---|---|
WEB | web | Yes | Default prefix for standard HTTP request URIs. |
CLI | cli | Yes | Default prefix for all CLI commands. |
API | api | No | Suggested prefix for API routes (/api). |
PANEL | panel | No | Suggested prefix for control panel routes. |
ADMIN | admin | No | Suggested prefix for admin routes (/admin). |
CONSOLE | console | No | Suggested prefix for console routes. |
WEBHOOK | webhook | No | Suggested prefix for webhook endpoints. |
WEB
This prefix applies to all HTTP URIs without a specific prefix.
- Reserved: cannot be renamed
- Route file:
/routes/web.php(fixed)
Example:https://example.com/ → routes handled by /routes/web.php.
API
This handles HTTP URIs starting with api
- Reserved: cannot be renamed
- Route file:
/routes/api.php(customizable)
Example:https://example.com/api/users → routes handled by /routes/api.php.
Note:
If you want to change the API prefix, you must also set the new prefix in the environment file (
app.api.prefix).This ensures that Luminova correctly detects the API prefix when calling methods likeLuminova::isApiPrefix()and that API request validation works as expected.
CLI
Default prefix to handle all command-line routes.
- Reserved: cannot be renamed
- Route file:
/routes/cli.php(fixed)
Example:php index.php migrate → routes handled by /routes/cli.php.
Methods
constructor
Creates a new Prefix context for routing.
public function __construct(string $prefix, \Closure|array|null $onError = null)Parameters:
| Parameter | Type | Description |
|---|---|---|
$prefix | string | The URI prefix to handle (e.g., blog). |
$onError | Closure|array<int,string>|null | Optional error handler for this prefix. Can be any callable or [ClassName, 'methodName']. |
The
$onErrorhandler is used when a route is not found within this prefix. It can be a closure or a two-element array:[ControllerClass::class, 'methodName'].