Sessions in PHP - Datenbank basierte Sessionverwaltung
Autor
Flitze
Klicks 207412
Keywords:
PHP-Session-Verwaltung, PHP Sessions verständlich erklärt, Sessionfunktionen, Probleme mit PHP Sessions, session_set_save_handler, Datenbank, Sessionverwaltung, session_set_save_handler, Datenbank basierte Session,PHP Session in Datenbank speichern
Klicks 207412
Rating für Sessions in PHP
8.7 von 10
Bewertungen135
Stand
12.06.2013
8.7 von 10
Bewertungen135
Keywords:
PHP-Session-Verwaltung, PHP Sessions verständlich erklärt, Sessionfunktionen, Probleme mit PHP Sessions, session_set_save_handler, Datenbank, Sessionverwaltung, session_set_save_handler, Datenbank basierte Session,PHP Session in Datenbank speichern
Breadcrumb:
Workshops » Sessions in PHP » Sessions in PHP - Datenbank basierte Sessionverwaltung
4. Datenbank basierte Sessionverwaltung
[ADSENSE_LINE]Ich stand vor der Problematik, dass ich den Login meiner User durch die Session ID verifiziere. Diese Session bleibt dem User bis zu seinem Logout erhalten, da darin z.B. seine bereits gelesenen Forenthreads gespeichert werden. Um das zu realisieren setzte ich die Lifetime der Sessions sowie die der gesetzten Cookies auf mehr als ein Jahr, so dass ich sicher sein konnte, dass der User auch wirklich eingeloggt bleibt. Dabei bedachte ich nicht, dass für jeden User eine Session gestartet wird, egal ob registrierter User, der sich einloggt, oder Gast, der nur kurz auf der Seite vorbeischaut. Die Folge dieses 'unsauberen' Managments wurde mir klar, als ich den tmp Ordner kontrollierte, in dem die Session gespeichert werden. Auf 1200 Besucher kamen 1492 Sessions von denen 1490 'tot' waren, also keine Daten enthielten. Normalerweise kümmert sich die garbage collection darum, doch dadurch, dass ich die Lifetime der Session auf 1 Jahr gesetzt hatte, hätte das noch einige Zeit gedauert.An dieser Stelle musste ich mich intensiver mit Sessions und deren Handling vetraut machen. Dabei stieß ich auf die Möglichkeit, Sessions per Datenbank zu verwalten. Das hat den riesen Vorteil, dass man beliebig viele zusätzliche Daten zu einer Session speichern kann, wie z.B. dass eine Session nach einer Stunde als Müll gilt und entsorgt wird, aber eine andere hingegen niemals.
Zu dem Verständnis ein solches Managment zu realisieren, haben mir vor allem drei Quellen geholfen, die ich an dieser Stelle nennen möchte:
1. session_set_save_handler Referenz auf PHP.net
2. Custom Session Handling von Zend.com
3. Guru Speak: Storing Sessions in a Database
Diese Quellen sind jedoch mit Ausnahme von php.net alle auf Englisch verfasst. Allerdings sind von dieser Quelle eigentlich nur die Kommentare interessant.. und die sind wiederum auf Englisch
Wie dem auch sei, zumindest habe ich kein ausführliches Tutorial zu dieser Problematik auf Deutsch gefunden und das soll sich nun ändern.
Warum soll ich mein Sessionmanagment über eine Datenbank verwalten?
Zunächst einmal stellt sich berechtigterweise die Frage nach dem Sinn der Implementierung eines eigenen, Datenbank basierten Sessionmanagments. Die Antwort lautet: Weitsicht und Funktionalität. Durch 'eigenes' wird es eigentlich schon deutlich, denn ihr bekommt dadurch die volle Kontrolle über eurer Sessions, so dass es einfacher ist, neue Dinge zu implementieren. Ein Beispiel dafür habe ich bereits in der Einleitung gegeben.Ein weiterer Grund wäre die Erweiterbarkeit des Projektes. Sobald euer Projekt größere Ausmaße erreicht und ein Server evtl. nicht mehr ausreicht, so dass ihr einen weiteren Server benötigt und die beiden aufeinander abstimmt könnt ihr das File basierte Session Managment vergessen. Dem entgegen muss man bei der Datenbanklösung lediglich die entsprechende MYSQL-Connection in der Session laden und das ganze funktioniert wieder reibungslos.
Es gibt sicherlich noch weitere Vorteile, aber die werdet ihr selbst herausfinden. Let's get it on..
Die MySQL-Tabelle
Die Sessions werden über eine Datenbank verwaltet, also brauchen wir auch eine entsprechende Tabelle. Diese sieht folgendermaßen ausCode:
+-----------+------------------+------+-----+---------+-------+ | Spalte | Typ | Null | Key | Default | Extra | +-----------+------------------+------+-----+---------+-------+ | ses_id | VARCHAR(32) | | PRI | | | | ses_time | INT | | | 0 | | | ses_value | MEDIUMTEXT | | | '' | | +-----------+------------------+------+-----+---------+-------+
Der Name der Tabelle lautet Sessions. Zum erstellen könnt ihr den folgenden Code verwenden
PHP:
<?php
$sql = 'CREATE TABLE `Sessions` ('
. ' `ses_id` VARCHAR(32) NOT NULL PRIMARY KEY, '
. ' `ses_time` INT NOT NULL DEFAULT \'0\', '
. ' `ses_value` MEDIUMTEXT NOT NULL DEFAULT \'\' '
. ' )';
mysql_query($sql) OR die($sql.mysql_error());
?>
Erläuterungen:
ses_id enthält die Session ID
ses_time enthält einen Timestamp des letzten Zugriffs
ses_value enthält die serialisierten Daten der Session
Die Basis für die Sessions ist damit geschaffen und wir müssen PHP nun noch klar machen, dass er die Sessions auch gefälligst dort zu verwalten hat.
Die Konfiguration von session_set_save_handler
session_set_save_handler() erwartet 6 Parameter, die alle angegeben werden müssen:session_set_save_handler
(
callback öffnen,
callback schließen,
callback lesen,
callback schreiben,
callback löschen,
callback gc
)
Diese 6 Funktionen müssen wir nun mit Leben füllen, deshalb werde ich zu jeder Funktion eine kurze Erklärung geben und einen entsprechenden Quellcode bereitstellten.
Vorbedingung
Da wir mit Datenbanken arbeiten muss zum Arbeiten mit den Sessions als erstes eine Datenbankverbindung aufgebaut werden, sofern dies nicht schon an anderer Stelle geschehen ist.
PHP:
<?php
$MYSQL_HOST = 'localhost';
$MYSQL_USER = 'Flitze';
$MYSQL_PASS = 'blablubb';
$MYSQL_DATA = 'MeineDatenbank';
@mysql_connect($MYSQL_HOST,
$MYSQL_USER,
$MYSQL_PASS) OR die("Error: ".mysql_error());
mysql_select_db($MYSQL_DATA) OR die("Error: ".mysql_error());
?>
öffnen
Diese Funktion wird als erstes aufgerufen wenn eine Session gestartet wird, bzw. konkreter, wenn session_start aufgerufen wird.
Per Definition werden ihr 2 Parameter übergeben, der Speicherpfad der Session und der Name der Session. Diese beiden Parameter werden in der php.ini definiert und zwar als session.save_path und session.name. Wir müssen sie auch in unserer Funktion definieren, auch wenn wir sie letztendlich nicht nutzen werden . Der Rückgabewert der Funktion muss vom Typ bool sein, also entweder TRUE oder FALSE.
Unsere Funktion sieht schlicht und ergreifend folgendermaßen aus
PHP:
<?php
function ses_open($path, $name) {
return TRUE;
}
?>
schließen
Diese Funktion wird als letztes aufgerufen. Sie erwartet keine Parameter. Der Rückgabewert der Funktion muss wieder vom Typ bool sein. Auch diese Funktion müssen wir nicht behandeln.
PHP:
<?php
function ses_close() {
return TRUE;
}
?>
lesen
Wie der Name schon sagt, wird diese Funktion zum Auslesen der Daten aufgerufen. Sie enthält als Parameter die Session ID mittels derer wir einen SQL-Query formulieren können, um die entsprechenden Sessiondaten zu laden. Der Rückgabewert der Funktion muss vom Typ String sein. Wenn also ein Fehler auftritt muss dennoch ein string zurückgegeben werden und nicht etwa false.
PHP:
<?php
function ses_read($ses_id) {
$sql = "SELECT
ses_value
FROM
Sessions
WHERE
ses_id = '".mysql_real_escape_string($ses_id)."'
";
$result = @mysql_query($sql);
// Fehler im Query, return leeren String
if (!$result)
return '';
// Session nicht gefunden, return leeren String
if (!mysql_num_rows($result))
return '';
// Session gefunden, return Daten der Session
$row = mysql_fetch_assoc($result);
return $row["ses_value"];
}
?>
Anmerkungen:
mysql_real_escape_string() wird benutzt, da der User die Session ID theoretisch manipulieren kann, indem er an seinem Cookie herumspielt.
Intern werden die Daten beim return unserialisiert.
schreiben
Diese Funktion fügt einer Session neue Daten hinzu. Als Parameter werden die Session ID und alle Werte der Session übergeben. Durch die ID können wir nun die Daten über einen entsprechenden Query hinzufügen. Der Rückgabewert muss vom Typ bool sein.
PHP:
<?php
function ses_write($ses_id, $data) {
$sql = "REPLACE INTO
Sessions
(ses_id,
ses_time,
ses_value)
VALUES ('".mysql_real_escape_string($ses_id)."',
'".time()."',
'".mysql_real_escape_string($data)."')
";
return (bool)@mysql_query($sql);
}
?>
Anmerkungen:
mysql_real_escape_string() wird bei den Daten benutzt, da wir uns natürlich gegen jegliche SQL-Injektion absichern müssen. Außerdem würde es sonst einen Fehler geben, wenn wir in den Daten single und double quotes ( ' )( " ) verwenden würden, da der Query zerstört würde.
Ich verwende den REPLACE Befehl da es zu Fehlern kommen kann, wenn man zuerst prüft, ob die Session bereits existiert und die existierende Session dann updatet bzw. eine neue Session erstellt, falls keine Session zum Updaten gefunden wurde. Diese Fehler treten auf, wenn man sehr schnell refresht. Intern wurde $data bereits vor Übergabe als Parameter serialisiert. Die Methode dazu wird übrigens in session.serialize_handler definiert.
Außerdem 'update' ich bei jedem Aufruf die Zeit der Session, was später bei der garbage collection von Bedeutung ist.
löschen
Diese Funktion wird aufgerufen, wenn man eine Sessions mittels session_destroy zerstört. Als Parameter hat sie die Session ID und als Rückgabewert wird TRUE oder FALSE erwartet. In unserem Fall bedeutet das, die betreffende Session aus der Datenbank zu löschen.
PHP:
<?php
function ses_destroy($ses_id) {
$sql = "DELETE FROM
Sessions
WHERE
ses_id = '".mysql_real_escape_string($ses_id)."'
";
return (bool)@mysql_query($sql);
}
?>
gc
Das Aufräumkommando unter den Funktionen. Ihr Aufrufe hängt von den Werten session.gc_probability und session.gc_divisor ab, die eine Wahrscheinlichkeit für den Aufruf dieser Funktion definieren. Inhaltlich sorgt diese Funktion dafür, dass alte sprich inaktive, Sessions gelöscht werden. Dazu wird als Parameter eine Zeit übergeben, die in session.gc_maxlifetime gesetzt ist.
PHP:
<?php
function ses_gc($life) {
$ses_life = time()-$life;
$sql = "DELETE FROM
Sessions
WHERE
ses_time < ".$ses_life."
";
return (bool)@mysql_query($sql);
}
?>
Anmerkungen:
$ses_life enthält einen Timestamp der mit der letzten Zugriffszeit der Session, ses_time, verglichen wird. Wird ein Datensatz gefunden, bei dem dies zutrifft, so wird er gelöscht.
Nachdem nun alle benötigten Funktionen definiert wurden, kann session_set_save_handler aufgerufen werden und im Anschluss daran kann die Session gestartet werden.
PHP:
<?php
session_set_save_handler ('ses_open',
'ses_close',
'ses_read',
'ses_write',
'ses_destroy',
'ses_gc');
session_start();
?>
Anwendung der Sessions
Sessions können nun weiter wie gewohnt verwendet werden, als User bemerkt man keinen Unterschied. Um das ganze noch zu optimieren zeige ich nun nochmal den kompletten Code samt dem Setzen einiger php.ini Werte.PHP:
<?php
$MYSQL_HOST = 'localhost';
$MYSQL_USER = 'Flitze';
$MYSQL_PASS = 'blablubb';
$MYSQL_DATA = 'MeineDatenbank';
@mysql_connect($MYSQL_HOST,
$MYSQL_USER,
$MYSQL_PASS) OR die("Error: ".mysql_error());
mysql_select_db($MYSQL_DATA) OR die("Error: ".mysql_error());
// Lifetime auf eine Stunde setzen
ini_set('session.gc_maxlifetime', 3600);
// gc mit einer Wahrscheinlichkeit von 1% aufrufen
ini_set('session.gc_probability', 1);
ini_set('session.gc_divisor', 100);
function ses_open($path, $name) {
return TRUE;
}
function ses_close() {
return TRUE;
}
function ses_read($ses_id) {
$sql = "SELECT
ses_value
FROM
Sessions
WHERE
ses_id = '".mysql_real_escape_string($ses_id)."'
";
$result = @mysql_query($sql);
// Fehler im Query, return leeren String
if (!$result)
return '';
// Session nicht gefunden, return leeren String
if (!mysql_num_rows($result))
return '';
// Session gefunden, return Daten der Session
$row = mysql_fetch_assoc($result);
return $row["ses_value"];
}
function ses_write($ses_id, $data) {
$sql = "REPLACE INTO
Sessions
(ses_id,
ses_time,
ses_value)
VALUES ('".mysql_real_escape_string($ses_id)."',
'".time()."',
'".mysql_real_escape_string($data)."')
";
return (bool)@mysql_query($sql);
}
function ses_destroy($ses_id) {
$sql = "DELETE FROM
Sessions
WHERE
ses_id = '".mysql_real_escape_string($ses_id)."'
";
return (bool)@mysql_query($sql);
}
function ses_gc($life) {
$ses_life = time()-$life;
$sql = "DELETE FROM
Sessions
WHERE
ses_time < ".$ses_life."
";
return (bool)@mysql_query($sql);
}
session_set_save_handler ('ses_open',
'ses_close',
'ses_read',
'ses_write',
'ses_destroy',
'ses_gc');
session_start();
?>
Diese Datei kann nun als mySess.php gespeichert werden. Nun muss nur noch jedes session_start() in euren Skripten durch
PHP:
<?php
require('mySess.php');
?>
ersetzt werden und wir sind fertig. Eine Beispieldatei zum Testen sieht so aus:
PHP:
<?php
// Session starten
require('mySess.php');
$_SESSION['counter'] = 1;
for($i=0; $i<10; $i++){
$_SESSION['counter']++;
echo $_SESSION['counter'];
}
// Session zerstören
session_destroy();
?>
Zurück zur vorigen Seite:
Sessions in PHP - php.ini Bewerten