Middleware

Middleware provides a convenient mechanism for filtering HTTP requests entering your application. Use middleware for authentication, logging, CORS, and more.

Creating Middleware

Using CLI (Recommended)

php mini make:middleware AuthMiddleware

This creates a middleware file at:

app/Middleware/AuthMiddleware.php

Manual Creation

<?php

namespace App\Middleware;

use Core\Middleware;
use Core\Request;
use Core\Response;

class AuthMiddleware extends Middleware
{
    /**
     * Handle the incoming request
     */
    public function handle(Request $request, \Closure $next)
    {
        // Your middleware logic here
        
        // Continue to next middleware or route handler
        return $next($request);
    }
}

Middleware Structure

All middleware must:

  • Extend the Core\Middleware class
  • Be in the App\Middleware namespace
  • Implement the handle() method
  • Call $next($request) to continue the request

Basic Middleware Template

public function handle(Request $request, \Closure $next)
{
    // Execute code BEFORE the route handler
    
    // Check conditions, modify request, etc.
    if (/* some condition */) {
        // Stop execution and return response
        return Response::text('Unauthorized', 401);
    }
    
    // Continue to next middleware or route handler
    $response = $next($request);
    
    // Execute code AFTER the route handler (optional)
    
    return $response;
}

Example: Authentication Middleware

<?php

namespace App\Middleware;

use Core\Middleware;
use Core\Request;
use Core\Response;

class AuthMiddleware extends Middleware
{
    /**
     * Handle the incoming request
     * Check if user is authenticated
     */
    public function handle(Request $request, \Closure $next)
    {
        // Check if user is logged in (example using session)
        session_start();
        
        if (!isset($_SESSION['user_id'])) {
            // Redirect to login page
            return Response::redirect('/login');
        }
        
        // User is authenticated, continue
        return $next($request);
    }
}

Registering Middleware Aliases

To use middleware with short names (like 'auth' instead of 'AuthMiddleware'), register aliases in your bootstrap file:

In public/index.php

<?php
// public/index.php
require __DIR__ . '/../core/App.php';

$app = new \Core\App();

// Register middleware aliases
$app->middlewareAlias('auth', 'AuthMiddleware');
$app->middlewareAlias('admin', 'AdminMiddleware');
$app->middlewareAlias('guest', 'GuestMiddleware');

// Or register multiple at once
$app->middlewareAliases([
    'auth' => 'AuthMiddleware',
    'admin' => 'AdminMiddleware',
    'guest' => 'GuestMiddleware'
]);

$app->run();
💡 Tip: Without aliases, you must use the full class name (e.g., 'AuthMiddleware'). With aliases, you can use short names (e.g., 'auth').

Applying Middleware

Single Route

// Apply middleware to a single route
Route::middleware('auth')
    ->get('/dashboard', 'DashboardController@index');

// Multiple middleware
Route::middleware(['auth', 'admin'])
    ->get('/admin', 'AdminController@index');

Route Groups

// Apply middleware to multiple routes
Route::group(['middleware' => 'auth'], function () {
    Route::get('/profile', 'UserController@profile');
    Route::get('/settings', 'UserController@settings');
    Route::get('/dashboard', 'DashboardController@index');
});

// Multiple middleware in group
Route::group(['middleware' => ['auth', 'verified']], function () {
    Route::get('/email/verify', 'VerificationController@show');
});

Middleware Execution Order

Middleware executes in the following order:

  1. Global Middleware (registered in App)
  2. Group Middleware (from route groups)
  3. Route Middleware (from individual routes)
  4. Route Handler (controller method)

Example Flow:

Request → Global Middleware → Group Middleware → Route Middleware → Controller → Response

Example

// Global middleware (in App)
$app->middleware('LoggingMiddleware');

// Route group
Route::group(['middleware' => 'auth'], function () {
    // Route with additional middleware
    Route::middleware('admin')
        ->get('/admin', 'AdminController@index');
});

// Execution order:
// 1. LoggingMiddleware
// 2. AuthMiddleware (from group)
// 3. AdminMiddleware (from route)
// 4. AdminController@index

More Middleware Examples

Admin Middleware

<?php

namespace App\Middleware;

use Core\Middleware;
use Core\Request;
use Core\Response;

class AdminMiddleware extends Middleware
{
    public function handle(Request $request, \Closure $next)
    {
        session_start();
        
        // Check if user is admin
        if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
            return Response::text('Forbidden', 403);
        }
        
        return $next($request);
    }
}

CORS Middleware

<?php

namespace App\Middleware;

use Core\Middleware;
use Core\Request;
use Core\Response;

class CorsMiddleware extends Middleware
{
    public function handle(Request $request, \Closure $next)
    {
        // Set CORS headers
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type, Authorization');
        
        // Handle preflight requests
        if ($request->method() === 'OPTIONS') {
            return Response::text('', 200);
        }
        
        return $next($request);
    }
}

Rate Limiting Middleware

<?php

namespace App\Middleware;

use Core\Middleware;
use Core\Request;
use Core\Response;

class RateLimitMiddleware extends Middleware
{
    public function handle(Request $request, \Closure $next)
    {
        $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        $key = "rate_limit_{$ip}";
        
        // Simple rate limiting (use Redis or database in production)
        $file = sys_get_temp_dir() . '/' . md5($key) . '.txt';
        
        if (file_exists($file)) {
            $data = json_decode(file_get_contents($file), true);
            $count = $data['count'] ?? 0;
            $time = $data['time'] ?? time();
            
            // Reset if more than 1 minute passed
            if (time() - $time > 60) {
                $count = 0;
                $time = time();
            }
            
            // Check limit (100 requests per minute)
            if ($count >= 100) {
                return Response::text('Too Many Requests', 429);
            }
            
            $count++;
        } else {
            $count = 1;
            $time = time();
        }
        
        file_put_contents($file, json_encode(['count' => $count, 'time' => $time]));
        
        return $next($request);
    }
}

Best Practices

  • Keep middleware focused: Each middleware should do one thing well.
  • Always call $next(): Unless you're returning an error response, always call $next($request).
  • Use descriptive names: Name middleware clearly (AuthMiddleware, AdminMiddleware, etc.).
  • Register aliases: Use short aliases for cleaner route definitions.
  • Handle errors gracefully: Return appropriate HTTP status codes (401, 403, 429, etc.).

Next Steps

Now that you understand middleware, learn about: