Luminova Framework

PHP Luminova: Method-Based Routing

Last updated: 2026-01-29 19:47:22

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\Prefix class 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 of api must map to /routes/api.php.
  • Any configured error handler (such as onApiError) must exist in the target controller class (for example, App\Errors\Controllers\ErrorController or 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/admin
  • https://example.com/admin/foo
  • https://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 (demo group 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:

ConstantValueReservedDescription
WEBwebYesDefault prefix for standard HTTP request URIs.
CLIcliYesDefault prefix for all CLI commands.
APIapiNoSuggested prefix for API routes (/api).
PANELpanelNoSuggested prefix for control panel routes.
ADMINadminNoSuggested prefix for admin routes (/admin).
CONSOLEconsoleNoSuggested prefix for console routes.
WEBHOOKwebhookNoSuggested 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 like Luminova::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:

ParameterTypeDescription
$prefixstringThe URI prefix to handle (e.g., blog).
$onErrorClosure|array<int,string>|nullOptional error handler for this prefix. Can be any callable or [ClassName, 'methodName'].

The $onError handler is used when a route is not found within this prefix. It can be a closure or a two-element array: [ControllerClass::class, 'methodName'].