Middleware

Middleware provides a structured way to filter HTTP requests before they reach a route callback or controller action. Middleware are executed in sequence using a pipeline pattern — each middleware decides whether to continue execution by calling $next().

Middleware are commonly used for:

  • Authentication and session checks

  • Role-based authorization

  • CSRF and security enforcement

  • Request logging and preprocessing

  • Access control and route guards

Folder Structure

app/
├── middlewares/
│   ├── AuthMiddleware.php
│   ├── AdminMiddleware.php
│   └── RoleMiddleware.php
│
└── config/
    └── middleware.php

Configuration

Register all middleware in app/config/middleware.php. Each key is the alias you reference when attaching middleware to routes or controllers.

<?php
$config['middlewares'] = array(
    'auth'  => load_class('AuthMiddleware',  'middlewares'),
    'admin' => load_class('AdminMiddleware', 'middlewares'),
    'role'  => load_class('RoleMiddleware',  'middlewares'),
    'guest' => load_class('GuestMiddleware', 'middlewares'),
);

Note

Each value must be a class instance loaded via load_class(). The class file must exist in app/middlewares/ and define a handle(Closure $next) method.

Writing Middleware

Every middleware class must implement a single handle(Closure $next) method. Call $next() to pass execution to the next middleware or the final route callback. Omitting $next() stops the pipeline entirely.

Basic Example

<?php
class AuthMiddleware
{
    public function handle(Closure $next)
    {
        if (!isset($_SESSION['user'])) {
            redirect('login');
        }

        return $next();
    }
}

Role-Based Authorization

<?php
class AdminMiddleware
{
    public function handle(Closure $next)
    {
        if (!isset($_SESSION['user'])) {
            redirect('login');
        }

        if ($_SESSION['user']['role'] !== 'admin') {
            show_error('403 Forbidden', 'You do not have access to this page.', 'error_general', 403);
        }

        return $next();
    }
}

Dynamic Role Middleware

A single middleware that accepts a role parameter via the session or config:

<?php
class RoleMiddleware
{
    protected $allowed_roles = ['admin', 'editor'];

    public function handle(Closure $next)
    {
        $user = $_SESSION['user'] ?? null;

        if (!$user) {
            redirect('login');
        }

        if (!in_array($user['role'], $this->allowed_roles)) {
            show_error('403 Forbidden', 'Insufficient permissions.', 'error_general', 403);
        }

        return $next();
    }
}

Guest Middleware (redirect if already logged in)

<?php
class GuestMiddleware
{
    public function handle(Closure $next)
    {
        if (isset($_SESSION['user'])) {
            redirect('dashboard');
        }

        return $next();
    }
}

Attaching Middleware to Routes

Route Group Middleware

Apply one or more middleware to all routes inside a group in app/config/routes.php. Middleware keys are resolved from app/config/middleware.php.

<?php
// Single middleware on a group
$router->group(
    ['prefix' => '/dashboard', 'middleware' => 'auth'],
    function ($router) {
        $router->get('/home',     'DashboardController::index');
        $router->get('/settings', 'DashboardController::settings');
    }
);

// Multiple middleware on a group (executed in order)
$router->group(
    ['prefix' => '/admin', 'middleware' => ['auth', 'admin']],
    function ($router) {

        $router->get('/users',         'Admin\UserController::index');
        $router->get('/users/{id}',    'Admin\UserController::show');
        $router->delete('/users/{id}', 'Admin\UserController::destroy');

    }
);

Single Route Middleware

Attach middleware to an individual route by chaining ->middleware():

<?php
// Single middleware
$router->get('/admin/panel', function () {
    echo 'Admin panel';
})->middleware('admin');

// Multiple middleware chained
$router->get('/reports', 'ReportController::index')
       ->middleware(['auth', 'admin']);

// With parameter constraint
$router->get('/user/{id}', 'UserController::show')
       ->where_number('id')
       ->middleware('auth');

Middleware in Controllers

Middleware can also be run programmatically inside a controller method using $this->middleware->run():

<?php
class DashboardController extends Controller
{
    public function index()
    {
        return $this->middleware->run(
            ['auth'],
            function () {
                $this->call->view('dashboard/index');
            }
        );
    }

    public function admin_panel()
    {
        return $this->middleware->run(
            ['auth', 'admin'],
            function () {
                $this->call->view('admin/panel');
            }
        );
    }
}

Note

Running middleware inside a controller is useful when you need conditional or method-level protection without modifying routes.

Execution Flow

Incoming Request
     │
     ▼
Route Matched
     │
     ▼
Middleware #1 ──── fails? ──► Stop (redirect / error)
     │ $next()
     ▼
Middleware #2 ──── fails? ──► Stop (redirect / error)
     │ $next()
     ▼
Middleware #N ──── fails? ──► Stop (redirect / error)
     │ $next()
     ▼
Route Callback / Controller Action
     │
     ▼
Response
  1. The incoming request is matched to a route.

  2. Assigned middleware aliases are resolved from app/config/middleware.php.

  3. Middleware are executed in the order they are listed.

  4. Each middleware calls $next() to pass control to the next stage.

  5. If a middleware does not call $next(), execution stops immediately — no further middleware or route callback runs.

  6. The final route callback or controller method executes.

Middleware Method Reference

Method / Usage

Description

handle(Closure $next)

Required method on every middleware class. Call $next() to continue the pipeline.

$this->middleware->run($keys, $fn)

Runs an array of middleware keys then executes $fn if all pass.

->middleware('key')

Attaches a single middleware alias to the last registered route.

->middleware(['key1', 'key2'])

Attaches multiple middleware aliases to the last registered route.

['middleware' => 'key']

Attaches middleware to all routes inside a group().

['middleware' => ['key1', 'key2']]

Attaches multiple middleware to all routes inside a group().

Common Middleware Patterns

Use Case

Middleware Name

What it checks

Require login

AuthMiddleware

$_SESSION['user'] exists

Require admin role

AdminMiddleware

$_SESSION['user']['role'] === 'admin'

Redirect logged-in users

GuestMiddleware

$_SESSION['user'] does not exist

Role-based access

RoleMiddleware

User role is in an allowed list

CSRF protection

CsrfMiddleware

Token in POST matches session token

Request logging

LogMiddleware

Logs request method and URI before calling $next()

Tips and Best Practices

  • Register all middleware in app/config/middleware.php using short, descriptive aliases.

  • Apply middleware at the group level when multiple routes share the same protection — avoids repeating ->middleware() on every route.

  • Always call return $next() at the end of a middleware unless you explicitly intend to stop the pipeline (redirect, error, etc.).

  • Keep each middleware focused on a single responsibility — one class for auth, another for role checks, another for logging.

  • Avoid heavy database queries inside middleware; use the session or a lightweight cache check instead.

  • Order matters — list middleware from most fundamental to most specific (e.g., ['auth', 'admin'] not ['admin', 'auth']).