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