DALT.PHP
Framework Deep Dive4. Session & State

Session Helper Class

How the Session class wraps PHP's $_SESSION for cleaner code

Wrapping the Session Superglobal

PHP's $_SESSION is a global array that stores user data between requests. DALT.PHP wraps it in a Session class for cleaner, more expressive code.

Think of it like this:

  • $_SESSION is the raw storage box
  • Session class is the organized filing system

The Session Class

class Session
{
    public static function has(string $key): bool
    {
        return isset($_SESSION['_flash'][$key]) || isset($_SESSION[$key]);
    }
    
    public static function put($key, $value)
    {
        $_SESSION[$key] = $value;
    }

    public static function get($key, $default = null)
    {
        return $_SESSION['_flash'][$key] ?? $_SESSION[$key] ?? $default;
    }

    public static function flash($key, $value)
    {
        $_SESSION['_flash'][$key] = $value;
    }

    public static function unflash()
    {
        unset($_SESSION['_flash']);
    }

    public static function flush()
    {
        $_SESSION = [];
    }

    public static function destroy()
    {
        $cookieName = session_name();
        static::flush();
        if (session_status() === PHP_SESSION_ACTIVE) {
            session_destroy();
        }
        $params = session_get_cookie_params();
        setcookie($cookieName, '', time() - 3600, 
            $params['path'], $params['domain'], 
            $params['secure'], $params['httponly']);
    }
}

All methods are static - you call them without creating an instance:

Session::put('user', $userData);  // Not: $session->put(...)

The Core Methods

put() - Store Data

public static function put($key, $value)
{
    $_SESSION[$key] = $value;
}

Usage:

Session::put('user', ['email' => 'user@example.com']);
Session::put('cart', ['item1', 'item2']);
Session::put('theme', 'dark');

What it does:

  • Stores data in $_SESSION
  • Data persists across requests
  • Available until session ends or is destroyed

Direct equivalent:

$_SESSION['user'] = ['email' => 'user@example.com'];

Why use put() instead?

  • ✅ More expressive (reads like English)
  • ✅ Consistent API across the framework
  • ✅ Could add logging/validation later
  • ✅ Easier to mock in tests

get() - Retrieve Data

public static function get($key, $default = null)
{
    return $_SESSION['_flash'][$key] ?? $_SESSION[$key] ?? $default;
}

Usage:

$user = Session::get('user');
$theme = Session::get('theme', 'light');  // Default to 'light'

The Priority Chain:

This line is important:

$_SESSION['_flash'][$key] ?? $_SESSION[$key] ?? $default

It checks in this order:

  1. Flash data first ($_SESSION['_flash'][$key])
  2. Regular session data ($_SESSION[$key])
  3. Default value ($default)

Why check flash first?

Flash data is temporary and should override regular data for one request:

// Request 1: Form submission fails
Session::flash('errors', ['email' => 'Invalid']);
redirect('/register');

// Request 2: Show form
$errors = Session::get('errors');  // Gets flash data
// Flash takes priority over any regular 'errors' key

The ?? operator:

  • Called "null coalescing operator"
  • Returns first non-null value
  • Short for: isset($a) ? $a : $b

has() - Check if Key Exists

public static function has(string $key): bool
{
    return isset($_SESSION['_flash'][$key]) || isset($_SESSION[$key]);
}

Usage:

if (Session::has('user')) {
    echo "User is logged in";
}

if (!Session::has('cart')) {
    echo "Cart is empty";
}

How it works:

  1. Uses PHP's native isset() function to check the _flash array.
  2. If it's not there, it checks the normal $_SESSION array.
  3. Returns a clean boolean.

Why isset() instead of checking the truthiness of a value?

Imagine a scenario where a session value is mathematically 0, or an empty string "":

Session::put('count', 0);
Session::put('name', '');

If the logic used (bool) static::get('count'), it would evaluate to false because 0 is a falsy value in PHP. By using isset(), DALT natively checks if the key actually exists in the array, regardless of whether the value itself is falsy.


flush() - Clear All Session Data

public static function flush()
{
    $_SESSION = [];
}

Usage:

Session::flush();

What it does:

  • Empties the entire $_SESSION array
  • Doesn't destroy the session itself
  • Session ID remains the same
  • Session cookie still exists

When to use:

  • Clearing all data but keeping session active
  • Resetting state

Direct equivalent:

$_SESSION = [];

destroy() - Complete Session Termination

public static function destroy()
{
    $cookieName = session_name();
    static::flush();
    if (session_status() === PHP_SESSION_ACTIVE) {
        session_destroy();
    }
    $params = session_get_cookie_params();
    setcookie($cookieName, '', time() - 3600, 
        $params['path'], $params['domain'], 
        $params['secure'], $params['httponly']);
}

This is the nuclear option - completely removes the session.

Step-by-step breakdown:

1. Get the session cookie name

$cookieName = session_name();

Remember from Part 1.2, DALT sets a custom name:

$sessionName = 'daltphp_' . substr(sha1(BASE_PATH), 0, 8);
session_name($sessionName);

So $cookieName might be 'daltphp_a7f3c2e1'.

2. Clear session data

static::flush();

Empties $_SESSION.

3. Destroy the session

if (session_status() === PHP_SESSION_ACTIVE) {
    session_destroy();
}

What session_destroy() does:

  • Deletes the session file on the server
  • Example: /tmp/sess_abc123xyz is deleted
  • Session data is gone forever

Why check session_status()?

  • Can't destroy if session isn't active
  • Prevents errors

4. Delete the session cookie

$params = session_get_cookie_params();
setcookie($cookieName, '', time() - 3600, 
    $params['path'], $params['domain'], 
    $params['secure'], $params['httponly']);

What this does:

  • Gets current cookie parameters (path, domain, etc.)
  • Sets cookie with empty value
  • Sets expiry to 1 hour ago (time() - 3600)
  • Browser sees expired cookie and deletes it

Why all these steps?

Just calling session_destroy() isn't enough:

  • Server-side data is deleted
  • But browser still has the cookie
  • Next request would create a new session with same ID
  • Could cause confusion

By deleting the cookie too:

  • Browser forgets the session ID
  • Next request gets a fresh session
  • Clean slate

Usage in the Framework

During Login

// framework/Core/Authenticator.php

public function login($user)
{
    $_SESSION['user'] = [
        'email' => $user['email'],
    ];
    session_regenerate_id(true);
}

Note

This uses $_SESSION directly, not Session::put().

Why?

  • Authenticator is core framework code
  • Direct access is fine here
  • Session::put() is more for application code

Could be rewritten as:

Session::put('user', ['email' => $user['email']]);

During Logout

// framework/Core/Authenticator.php

public function logout()
{
    Session::destroy();
}

This completely removes the session.

In Controllers

// app/Http/controllers/dashboard/index.php

$user = Session::get('user');

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

view('dashboard/index.view.php', [
    'email' => $user['email']
]);

Session Lifecycle

Request 1: Login

Session::put('user', [...])

$_SESSION['user'] = [...]

Response sent

PHP writes session to file

Browser receives session cookie

Request 2: Dashboard

Browser sends session cookie

PHP reads session file

$_SESSION populated

Session::get('user')

Returns user data

Request 3: Logout

Session::destroy()

Session file deleted
Cookie deleted

Session gone

Key Takeaways

  1. Session class wraps $_SESSION - Cleaner API
  2. All methods are static - No instance needed
  3. get() checks flash first - Flash data has priority
  4. destroy() is thorough - Clears data, file, and cookie
  5. has() safely checks key existence - Treats falsy variables fairly using isset()

What's Good Here

✅ Clean, expressive API
✅ Static methods (convenient)
✅ destroy() is thorough (clears everything)
✅ Consistent with framework style ✅ Accurate key-existence checking in has()

Design Note

DALT keeps sessions intentionally simple and “visible”:

  • You can still read and debug $_SESSION directly at any time.
  • The helper is just a small wrapper to make common actions easier (get/put/flash/destroy).

On this page