Skip to content

Session

Ray Fung edited this page Feb 26, 2026 · 3 revisions

Session

Razy provides a framework-native session system that is fully decoupled from PHP's built-in session_*() functions. It supports multiple storage backends, flash data, probabilistic garbage collection, and middleware integration.


Table of Contents


Overview

SessionInterface (contract)
  ?”â??€ Session (implementation)
        ?œâ??€ holds SessionConfig (immutable value object)
        ?”â??€ delegates to SessionDriverInterface (contract)
              ?œâ??€ FileDriver     ??filesystem (production)
              ?œâ??€ DatabaseDriver ??PDO-backed (production)
              ?œâ??€ ArrayDriver    ??in-memory (testing)
              ?”â??€ NullDriver     ??discard (stateless/API)

Key design decisions:

  • No dependency on PHP's native $_SESSION or session_start()
  • Strategy pattern for swappable storage backends
  • Two-generation flash data ??set this request, available next request, auto-deleted after
  • 40-character hex session IDs via bin2hex(random_bytes(20))

Quick Start

use Razy\Session\Session;
use Razy\Session\SessionConfig;
use Razy\Session\Driver\FileDriver;

// Create session with file storage
$config = new SessionConfig(
    name: 'MY_APP_SESSION',
    lifetime: 3600,
    secure: true,
    sameSite: 'Strict',
);

$driver = new FileDriver('/tmp/sessions');
$session = new Session($driver, $config);

// Start, use, save
$session->start();
$session->set('user_id', 42);
$session->flash('success', 'Welcome back!');
$session->save();

SessionConfig

Immutable value object for session cookie and GC configuration.

use Razy\Session\SessionConfig;

$config = new SessionConfig(
    name: 'RAZY_SESSION',       // cookie name (default)
    lifetime: 0,                 // 0 = browser session
    path: '/',                   // cookie path
    domain: '',                  // cookie domain
    secure: false,               // HTTPS only
    httpOnly: true,              // no JavaScript access
    sameSite: 'Lax',             // None | Lax | Strict
    gcMaxLifetime: 1440,         // GC: delete sessions older than N seconds
    gcProbability: 1,            // GC: probability numerator
    gcDivisor: 100,              // GC: probability denominator (1/100 = 1%)
);

Deriving New Configs

// Create a new config with overrides (original unchanged)
$apiConfig = $config->with([
    'name' => 'API_SESSION',
    'lifetime' => 7200,
    'sameSite' => 'None',
    'secure' => true,
]);

Session API

Lifecycle

// Start the session (loads data, runs GC probabilistically)
$session->start();

// Check if started
$session->isStarted();  // bool

// Save and close (writes data, ages flash data)
$session->save();

// Destroy session (deletes from storage, clears data)
$session->destroy();

Session ID

// Get current session ID
$id = $session->getId();  // 40-char hex string

// Set a specific ID (e.g., from cookie)
$session->setId($cookieSessionId);

// Regenerate ID (prevents session fixation)
$session->regenerate();              // keep old data
$session->regenerate(destroyOld: true);  // delete old session

Data Access

// Set values
$session->set('user_id', 42);
$session->set('preferences', ['theme' => 'dark']);

// Get values
$userId = $session->get('user_id');               // 42
$theme  = $session->get('missing', 'default');    // 'default'

// Check existence
$session->has('user_id');   // true
$session->has('missing');   // false

// Remove a value
$session->remove('user_id');

// Get all data
$all = $session->all();  // ['preferences' => [...]]

// Clear all data
$session->clear();

Flash Data

Flash data is available for one subsequent request only, then auto-deleted.

// Set flash data (available on the NEXT request)
$session->flash('success', 'Profile updated!');
$session->flash('errors', ['name' => 'Name is required']);

// Read flash data (on the next request)
$message = $session->getFlash('success');          // 'Profile updated!'
$errors  = $session->getFlash('errors', []);       // array or default

// Check if flash data exists
$session->hasFlash('success');  // true

// Keep all flash data for one more request
$session->reflash();

// Keep specific flash keys
$session->keep(['success']);

Flash lifecycle:

Request 1: flash('msg', 'Hi')  ??stored in _flash.new
Request 2: getFlash('msg')     ??'Hi' (moved to _flash.old)
Request 3: getFlash('msg')     ??null (deleted from _flash.old)

Session Drivers

FileDriver

Filesystem-based storage with atomic writes.

use Razy\Session\Driver\FileDriver;

$driver = new FileDriver(
    savePath: '/var/lib/razy/sessions',
    prefix: 'sess_',   // file prefix (default)
);

// Files created as: /var/lib/razy/sessions/sess_abc123def456...

Key features:

  • Atomic writes: writes to temp file, then rename() ??prevents corruption
  • Auto-creates the save path directory (mode 0700)
  • GC scans directory, deletes files with matching prefix older than maxLifetime
$driver->getSavePath();     // '/var/lib/razy/sessions'
$driver->getPrefix();       // 'sess_'
$driver->getLastGcCount();  // number of files deleted in last GC

DatabaseDriver

PDO-backed session storage. Works with MySQL, PostgreSQL, and SQLite.

use Razy\Session\Driver\DatabaseDriver;

$driver = new DatabaseDriver(
    pdo: $pdo,
    table: 'sessions',   // default table name
);

// Create the sessions table (if it doesn't exist)
$driver->createTable();

Required table schema:

CREATE TABLE sessions (
    id VARCHAR(128) NOT NULL PRIMARY KEY,
    data TEXT NOT NULL DEFAULT '',
    last_activity INTEGER NOT NULL DEFAULT 0
);

Key features:

  • Upsert pattern: UPDATE first, INSERT if no rows affected ??driver-agnostic
  • Serialization: uses PHP serialize()/unserialize()
  • GC: DELETE WHERE last_activity < cutoff
  • All operations wrapped in try/catch ??failures return empty/false gracefully
$driver->getTable();         // 'sessions'
$driver->getPdo();           // PDO instance
$driver->getLastGcCount();   // rows deleted in last GC

ArrayDriver

In-memory storage for testing. No I/O, no side effects.

use Razy\Session\Driver\ArrayDriver;

$driver = new ArrayDriver();

// Test helpers
$driver->isOpened();     // bool
$driver->getSessions();  // raw session store
$driver->count();        // number of sessions

// Inject timestamp for GC testing
$driver->setSessionTime('session_id', time() - 7200);
$gcCount = $driver->gc(3600);  // clears sessions older than 1 hour
$driver->getLastGcCount();     // number deleted

NullDriver

Discards all data ??for stateless contexts like APIs or CLI.

use Razy\Session\Driver\NullDriver;

$driver = new NullDriver();
// All operations are no-ops: read() returns [], write() returns true, etc.

Custom Drivers

Implement SessionDriverInterface:

use Razy\Contract\SessionDriverInterface;

class RedisDriver implements SessionDriverInterface
{
    public function __construct(
        private readonly \Redis $redis,
        private readonly int $ttl = 3600,
    ) {}

    public function open(): bool
    {
        return $this->redis->isConnected();
    }

    public function close(): bool
    {
        return true;
    }

    public function read(string $id): array
    {
        $data = $this->redis->get("session:{$id}");
        return $data ? unserialize($data) : [];
    }

    public function write(string $id, array $data): bool
    {
        return $this->redis->setex("session:{$id}", $this->ttl, serialize($data));
    }

    public function destroy(string $id): bool
    {
        return (bool) $this->redis->del("session:{$id}");
    }

    public function gc(int $maxLifetime): int
    {
        return 0; // Redis TTL handles expiration
    }
}

Middleware Integration

Use SessionMiddleware to auto-start/save sessions per request:

use Razy\Session\SessionMiddleware;
use Razy\Distributor\MiddlewarePipeline;

$middleware = new SessionMiddleware($session);

$pipeline = new MiddlewarePipeline();
$pipeline->pipe($middleware);

$result = $pipeline->process($context, function (array $ctx) use ($session) {
    // Session is started and available here
    $userId = $session->get('user_id');

    // Flash data from previous request
    $message = $session->getFlash('success');

    return $controller->handle($ctx);
});
// Session is automatically saved after the handler completes (in finally block)

See Middleware for more details on the middleware system.


Garbage Collection

GC runs probabilistically on start():

// With default config: gcProbability=1, gcDivisor=100
// ??1% chance of GC on each session start

// High-traffic: lower probability
$config = new SessionConfig(gcProbability: 1, gcDivisor: 1000);  // 0.1%

// Development: always GC
$config = new SessionConfig(gcProbability: 1, gcDivisor: 1);  // 100%

// Disable GC (handle externally via cron)
$config = new SessionConfig(gcDivisor: 0);

GC deletes sessions older than gcMaxLifetime seconds.


API Reference

SessionConfig

Property Type Default Description
name string 'RAZY_SESSION' Cookie name
lifetime int 0 Cookie lifetime (0 = browser session)
path string '/' Cookie path
domain string '' Cookie domain
secure bool false HTTPS only
httpOnly bool true No JavaScript access
sameSite string 'Lax' SameSite policy
gcMaxLifetime int 1440 Session expiry (seconds)
gcProbability int 1 GC probability numerator
gcDivisor int 100 GC probability denominator
Method Signature
with (array $overrides): static

Session

Category Method Signature
Lifecycle start (): bool
save (): void
destroy (): void
isStarted (): bool
ID getId (): string
setId (string $id): void
regenerate (bool $destroyOld = false): bool
Data get (string $key, mixed $default = null): mixed
set (string $key, mixed $value): void
has (string $key): bool
remove (string $key): void
all (): array
clear (): void
Flash flash (string $key, mixed $value): void
getFlash (string $key, mixed $default = null): mixed
hasFlash (string $key): bool
reflash (): void
keep (array $keys): void
Access getConfig (): SessionConfig
getDriver (): SessionDriverInterface

SessionDriverInterface

Method Signature Returns
open (): bool Open session store
close (): bool Close session store
read (string $id): array Read session data
write (string $id, array $data): bool Write session data
destroy (string $id): bool Delete session
gc (int $maxLifetime): int Garbage collect, return count

??Previous: Middleware CSRF

Clone this wiki locally