Start · Sprachen · PHP · Referenz · SessionHandlerInterface

SessionHandlerInterface

Interface

Definiert den minimalen Prototyp für die Erstellung einer benutzerdefinierten Sessionverwaltung in PHP.

seit PHP 5.4.0 Kategorie: http

Signatur

interface SessionHandlerInterface

Beschreibung

SessionHandlerInterface ist eine eingebaute PHP-Schnittstelle, die alle Methoden vorschreibt, die eine eigene Klasse zur Sitzungsverwaltung implementieren muss. Eine Implementierung dieser Schnittstelle kann anschließend mit session_set_save_handler() als aktiver Session-Handler registriert werden, bevor session_start() aufgerufen wird.

Die Schnittstelle definiert sechs Pflichtmethoden: open(), close(), read(), write(), destroy() und gc(). Diese entsprechen den internen Callback-Funktionen des Standard-PHP-Session-Mechanismus und werden vom Session-Subsystem automatisch zum richtigen Zeitpunkt aufgerufen.

Typische Anwendungsfälle sind das Speichern von Sessions in einer Datenbank (z. B. MySQL, PostgreSQL), einem Key-Value-Store (z. B. Redis, Memcached) oder einem anderen externen System, anstatt den Standard-Dateispeicher zu verwenden. Dies ermöglicht horizontale Skalierung von Webanwendungen, da Sessions nicht mehr an einen einzelnen Server gebunden sind.

Ergänzend gibt es SessionIdInterface (für eigene Session-ID-Erzeugung) und SessionUpdateTimestampHandlerInterface (für Lazy-Write-Unterstützung), die optional zusammen mit SessionHandlerInterface implementiert werden können.

Beispiele

Datenbank-basierter Session-Handler

<?php
class DatabaseSessionHandler implements SessionHandlerInterface
{
    private PDO $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function open(string $savePath, string $sessionName): bool
    {
        // Verbindung besteht bereits über PDO
        return true;
    }

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

    public function read(string $id): string|false
    {
        $stmt = $this->pdo->prepare(
            'SELECT data FROM sessions WHERE id = :id AND expires_at > NOW()'
        );
        $stmt->execute([':id' => $id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return $row ? $row['data'] : '';
    }

    public function write(string $id, string $data): bool
    {
        $stmt = $this->pdo->prepare(
            'INSERT INTO sessions (id, data, expires_at)
             VALUES (:id, :data, DATE_ADD(NOW(), INTERVAL 30 MINUTE))
             ON DUPLICATE KEY UPDATE data = :data,
             expires_at = DATE_ADD(NOW(), INTERVAL 30 MINUTE)'
        );
        return $stmt->execute([':id' => $id, ':data' => $data]);
    }

    public function destroy(string $id): bool
    {
        $stmt = $this->pdo->prepare('DELETE FROM sessions WHERE id = :id');
        return $stmt->execute([':id' => $id]);
    }

    public function gc(int $maxLifetime): int|false
    {
        $stmt = $this->pdo->prepare('DELETE FROM sessions WHERE expires_at < NOW()');
        $stmt->execute();
        return $stmt->rowCount();
    }
}

// Handler registrieren und Session starten
$pdo = new PDO('mysql:host=localhost;dbname=app', 'user', 'secret');
$handler = new DatabaseSessionHandler($pdo);
session_set_save_handler($handler, true);
session_start();

$_SESSION['user_id'] = 42;
echo 'Session gestartet: ' . session_id();
Session gestartet: <session-id>

Redis-basierter Session-Handler

<?php
class RedisSessionHandler implements SessionHandlerInterface
{
    private Redis $redis;
    private int $ttl;

    public function __construct(Redis $redis, int $ttl = 1800)
    {
        $this->redis = $redis;
        $this->ttl   = $ttl;
    }

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

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

    public function read(string $id): string|false
    {
        $data = $this->redis->get('sess:' . $id);
        return $data !== false ? $data : '';
    }

    public function write(string $id, string $data): bool
    {
        return $this->redis->setex('sess:' . $id, $this->ttl, $data);
    }

    public function destroy(string $id): bool
    {
        $this->redis->del('sess:' . $id);
        return true;
    }

    public function gc(int $maxLifetime): int|false
    {
        // Redis übernimmt das Ablaufen via TTL automatisch
        return 0;
    }
}

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

session_set_save_handler(new RedisSessionHandler($redis), true);
session_start();

$_SESSION['cart'] = ['item1', 'item2'];
echo 'Session in Redis gespeichert.';
Session in Redis gespeichert.

// Wichtig · Fallstricke

Sicherheitshinweise:

  • Session-IDs sollten niemals aus Benutzereingaben übernommen werden. Verwende stets die von PHP generierte ID (session_id()).
  • Die read()- und write()-Methoden erhalten die serialisierten Rohdaten der Session. Speichere diese niemals unverschlüsselt in öffentlich zugänglichen Systemen.
  • Stelle sicher, dass die gc()-Methode abgelaufene Sessions zuverlässig entfernt, um Session-Fixation und unbefugten Zugriff auf alte Daten zu verhindern.
  • Die Methode write() wird nicht sofort beim Setzen von $_SESSION-Werten aufgerufen, sondern erst am Ende des Skripts (oder bei explizitem session_write_close()). Achte darauf, dass Datenbankverbindungen zu diesem Zeitpunkt noch verfügbar sind.
  • Beim zweiten Parameter true in session_set_save_handler() wird session_write_close() automatisch beim Shutdown registriert — dies wird empfohlen.