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();
}
}
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
The incoming request is matched to a route.
Assigned middleware aliases are resolved from
app/config/middleware.php.Middleware are executed in the order they are listed.
Each middleware calls
$next()to pass control to the next stage.If a middleware does not call
$next(), execution stops immediately — no further middleware or route callback runs.The final route callback or controller method executes.
Middleware Method Reference
Method / Usage |
Description |
|---|---|
|
Required method on every middleware class. Call |
|
Runs an array of middleware keys then executes |
|
Attaches a single middleware alias to the last registered route. |
|
Attaches multiple middleware aliases to the last registered route. |
|
Attaches middleware to all routes inside a |
|
Attaches multiple middleware to all routes inside a |
Common Middleware Patterns
Use Case |
Middleware Name |
What it checks |
|---|---|---|
Require login |
|
|
Require admin role |
|
|
Redirect logged-in users |
|
|
Role-based access |
|
User role is in an allowed list |
CSRF protection |
|
Token in POST matches session token |
Request logging |
|
Logs request method and URI before calling |
Tips and Best Practices
Register all middleware in
app/config/middleware.phpusing 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']).