Middleware & Session Lab Instructions
Additional Resources
Section titled “Additional Resources”Lab Instructions: Implementing Session Middleware
Section titled “Lab Instructions: Implementing Session Middleware”In this lab, you will implement session management for your Slim 4 application using middleware. By the end of this lab, you will have a fully functional session system with security features and helper methods.
Learning Objectives
Section titled “Learning Objectives”By completing this lab, you will:
- Understand how middleware works in Slim 4.
- Create a SessionManager class to handle session configuration.
- Implement SessionMiddleware to automatically start sessions for all routes.
- Learn best practices for secure session management.
- Test your implementation with a practical example.
Prerequisites
Section titled “Prerequisites”Before starting this lab, ensure you have:
- A working Slim 4 application.
- Basic understanding of PHP sessions.
- Familiarity with PSR-7 request/response objects.
- Your development environment set up and running.
Lab Steps
Section titled “Lab Steps”Step 1: Create the SessionManager Helper Class
Section titled “Step 1: Create the SessionManager Helper Class”Objective: Create a helper class that will manage all session operations with built-in security features.
Instructions:
- Navigate to your app/Helpers/directory
- Create a new file named SessionManager.php
- Copy and paste the following code:
<?php
namespace App\Helpers;
class SessionManager{    /**     * Start the session with secure configuration.     */    public static function start(): void    {        if (session_status() === PHP_SESSION_NONE) {
            // Configure session to last 1 day (good for local development)            $sessionLifetime = 24 * 60 * 60; // (hours * minutes * seconds) -> 1 day in seconds -> 86400 seconds
            // Set session cookie parameters.            session_set_cookie_params([                'lifetime' => $sessionLifetime,                'path' => '/',                'domain' => '',                'secure' => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on',                'httponly' => true,                'samesite' => 'Strict'            ]);
            // Configure garbage collection (automatic cleanup)            ini_set('session.gc_maxlifetime', $sessionLifetime);            ini_set('session.gc_probability', 1);            ini_set('session.gc_divisor', 100);
            // Security settings.            ini_set('session.use_strict_mode', 1);            ini_set('session.cookie_httponly', 1);
            session_start();
            // Regenerate session ID every 15 minutes for security.            if (!isset($_SESSION['last_regeneration'])) {                $_SESSION['last_regeneration'] = time();                session_regenerate_id(true);            } elseif (time() - $_SESSION['last_regeneration'] > 900) {                $_SESSION['last_regeneration'] = time();                session_regenerate_id(true);            }
            // Browser fingerprinting for security (detect session hijacking).            $fingerprint = self::generateFingerprint();            if (!isset($_SESSION['fingerprint'])) {                $_SESSION['fingerprint'] = $fingerprint;            } elseif ($_SESSION['fingerprint'] !== $fingerprint) {                // Different browser detected - destroy and restart session.                session_destroy();                session_start();                $_SESSION['fingerprint'] = $fingerprint;                $_SESSION['last_regeneration'] = time();            }        }    }
    /**     * Generate a unique browser fingerprint for security.     */    private static function generateFingerprint(): string    {        $factors = [            $_SERVER['HTTP_USER_AGENT'] ?? '',            $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '',            $_SERVER['HTTP_ACCEPT_ENCODING'] ?? '',            $_SERVER['REMOTE_ADDR'] ?? ''        ];
        return hash('sha256', implode('|', $factors));    }
    /**     * Store a value in the session.     */    public static function set(string $key, $value): void    {        $_SESSION[$key] = $value;    }
    /**     * Retrieve a value from the session.     */    public static function get(string $key, $default = null)    {        return $_SESSION[$key] ?? $default;    }
    /**     * Check if a key exists in the session.     */    public static function has(string $key): bool    {        return isset($_SESSION[$key]);    }
    /**     * Remove a key from the session.     */    public static function remove(string $key): void    {        unset($_SESSION[$key]);    }
    /**     * Clear all session data.     */    public static function clear(): void    {        session_unset();    }
    /**     * Destroy the session completely.     */    public static function destroy(): void    {        session_destroy();    }}- Save the file
What you just created:
- A SessionManager class with secure session configuration
- Helper methods for common session operations (set,get,has,remove,clear,destroy)
- Built-in security features (session ID regeneration, browser fingerprinting)
- Automatic cleanup of old session files
Step 2: Create the SessionMiddleware Class
Section titled “Step 2: Create the SessionMiddleware Class”Objective: Create middleware that will automatically start sessions for every request.
Instructions:
- Navigate to your app/Middleware/directory
- Create a new file named SessionMiddleware.php
- Copy and paste the following code:
<?php
namespace App\Middleware;
use App\Helpers\SessionManager;use Psr\Http\Server\MiddlewareInterface;use Psr\Http\Message\ResponseInterface as Response;use Psr\Http\Message\ServerRequestInterface as Request;use Psr\Http\Server\RequestHandlerInterface as Handler;
class SessionMiddleware implements MiddlewareInterface{    public function process(        Request $request,        Handler $handler    ): Response    {        // Start session using SessionManager        SessionManager::start();
        // Continue to next middleware/route        return $handler->handle($request);    }}- Save the file
What you just created:
- Middleware that implements the PSR-15 MiddlewareInterface
- Automatic session initialization using SessionManager for every request
- Proper PSR-7 request/response handling
Step 3: Register the Middleware
Section titled “Step 3: Register the Middleware”Objective: Add the SessionMiddleware to your application so it runs on every request.
Instructions:
- Open your config/middleware.phpfile
- Add the following import at the top of the file (if not already present):
use App\Middleware\SessionMiddleware;- Add the middleware to your application by adding this line:
// Add the session middleware to the application (applies to ALL routes)$app->add(SessionMiddleware::class);- Save the file
Important Notes:
- This middleware will run for every route in your application
- Sessions will now be automatically available in all controllers and routes
- You don’t need to call session_start()manually anymore
Step 4: Test Your Implementation
Section titled “Step 4: Test Your Implementation”Objective: Verify that sessions are working correctly by creating a test route.
Instructions:
- Open your app/Routes/web-routes.phpfile
- Add the following import at the top:
use App\Helpers\SessionManager;- Add this test route:
$app->get('/test-session', function ($request, $response) {    // Get current counter, increment it    $counter = SessionManager::get('counter', 0) + 1;    SessionManager::set('counter', $counter);
    $response->getBody()->write("Counter: " . $counter);
    return $response;});- Save the file
- Start your development server (if not already running)
- Visit http://localhost/[your-project-name]/test-sessionin your browser
Expected Results:
- First visit: “Counter: 1”
- Refresh page: “Counter: 2”
- Refresh again: “Counter: 3”
- Counter continues to increment with each refresh
If the counter always shows “1”:
- Check that SessionMiddleware is properly registered in config/middleware.php
- Verify your browser accepts cookies
- Check browser console for errors
- Try clearing browser cookies and testing again
Step 5: Implement Session Usage in a Controller
Section titled “Step 5: Implement Session Usage in a Controller”Objective: Practice using sessions in a real controller with the BaseController pattern.
Instructions:
- Create a new controller called DemoController.phpinapp/Controllers/
- Implement the following functionality:
<?php
namespace App\Controllers;
use App\Helpers\SessionManager;use DI\Container;use Psr\Http\Message\ResponseInterface as Response;use Psr\Http\Message\ServerRequestInterface as Request;
class DemoController extends BaseController{    public function __construct(Container $container)    {        parent::__construct($container);    }
    // Show a simple counter page    public function counter(Request $request, Response $response, array $args): Response    {        // Get and increment counter        $counter = SessionManager::get('page_visits', 0) + 1;        SessionManager::set('page_visits', $counter);
        return $this->render($response, 'demo/counterView.php', [            'counter' => $counter,            'title' => 'Visit Counter Demo'        ]);    }
    // Reset the counter    public function resetCounter(Request $request, Response $response, array $args): Response    {        SessionManager::remove('page_visits');
        return $this->redirect($request, $response, 'demo.counter');    }}- Add routes for this controller in web-routes.php:
use App\Controllers\DemoController;
$app->get('/demo/counter', [DemoController::class, 'counter'])->setName('demo.counter');$app->post('/demo/reset', [DemoController::class, 'resetCounter'])->setName('demo.reset');- Create a new folder in views/nameddemo/, then inside it create a view file calledcounterView.php:
<!DOCTYPE html><html><head>    <title><?= htmlspecialchars($title) ?></title></head><body>    <h1>Visit Counter</h1>    <p>You have visited this page <strong><?= $counter ?></strong> times.</p>
    <form method="POST" action="reset">        <button type="submit">Reset Counter</button>    </form></body></html>- Test your implementation by visiting http://localhost/[your-project-name]/demo/counter
Verification Checklist
Section titled “Verification Checklist”Before submitting your lab, verify the following:
-  SessionManager.php exists in app/Helpers/directory
-  SessionMiddleware.php exists in app/Middleware/directory
-  SessionMiddleware is registered in config/middleware.php
-  Test route /test-sessionshows incrementing counter
- Counter persists across page refreshes
- Browser cookies are being set (check browser dev tools)
- (Optional) DemoController works with counter and reset functionality
Understanding What You Built
Section titled “Understanding What You Built”SessionManager Helper Methods
Section titled “SessionManager Helper Methods”| Method | Description | Example | 
|---|---|---|
| set($key, $value) | Store data (key-value pair) in the session | SessionManager::set('user_id', 123) | 
| get($key, $default) | Retrieve data (with optional default) | SessionManager::get('user_id', null) | 
| has($key) | Check if key exists in the session | if (SessionManager::has('user_id')) | 
| remove($key) | Remove specific key from the session | SessionManager::remove('cart_items') | 
| clear() | Clear all data (keep session alive) from the session | SessionManager::clear() | 
| destroy() | Destroy the session completely | SessionManager::destroy() | 
Security Features Implemented
Section titled “Security Features Implemented”- 
Secure Cookie Settings - HttpOnly flag prevents JavaScript access to the session cookie
- SameSite=Strict prevents CSRF attacks
- Secure flag for HTTPS connections only
 
- 
Session ID Regeneration - Regenerates the session ID every 15 minutes
- Prevents session fixation attacks
 
- 
Browser Fingerprinting - Detects if session cookie is stolen
- Automatically destroys compromised sessions
 
- 
Garbage Collection - Automatically cleans up old session files
- 1% chance per request (configurable)
 
Common Issues and Solutions
Section titled “Common Issues and Solutions”Issue 1: “Headers already sent” error
Section titled “Issue 1: “Headers already sent” error”Cause: Output before session_start()
Solution:
- Ensure no whitespace before <?phptags
- Check for echo/print statements before middleware runs
- Verify no BOM in PHP files
Issue 2: Session not persisting
Section titled “Issue 2: Session not persisting”Cause: Cookies not being saved in the browser
Solution:
- Check browser privacy settings
- Verify session save path is writable
- Check that middleware is registered
Issue 3: Counter resets unexpectedly
Section titled “Issue 3: Counter resets unexpectedly”Cause: Browser fingerprint mismatch
Solution:
- For development, comment out fingerprinting code (lines 86-96 in SessionManager.php)
- For production, keep it enabled for security