Signatur
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();
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.';
// Wichtig · Fallstricke
Sicherheitshinweise:
- Session-IDs sollten niemals aus Benutzereingaben übernommen werden. Verwende stets die von PHP generierte ID (
session_id()). - Die
read()- undwrite()-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 explizitemsession_write_close()). Achte darauf, dass Datenbankverbindungen zu diesem Zeitpunkt noch verfügbar sind. - Beim zweiten Parameter
trueinsession_set_save_handler()wirdsession_write_close()automatisch beim Shutdown registriert — dies wird empfohlen.