PHP/Die eigene Sitzungsverwaltung

Aus Mikiwiki
< PHP
Wechseln zu: Navigation, Suche

PHP 5 bietet die Möglichkeit, eigene Funktionen zur Behandlung der Datenein- und -auslagerung zu definieren, die beim Aufruf von "session_register" und "session_unregister" bzw. bei Veränderungen des Arrays "$_SESSION" aufgerufen werden. Diese können eingesetzt werden, um Daten in einer Datenbank zu speichern, die möglicherweise flexibler mit den Informationen umgeht als ein Dateisystem.

Beim Einsatz eigener Sitzungsfunktionen werden nicht die Aufrufe zu "session_register" usw. ersetzt, sondern die dahinterliegenden Funktionen. An den bereits mit Sitzungen arbeitenden Skripts muss nichts geändert werden. Die Definition erfolgt mit zwei Funktionen.

Funktion Beschreibung
session_module_name("user") Abschalten des internen Handlers.
session_set_save_handler Definition, welche Funktionen nun für die Ausführung zuständig sind. Insgesamt können sechs Funktionen definiert werden: Öffnen, Schliessen, Zurückholen von registrierten Variablen, Registrieren von Variablen, Zerstören der Sitzung und eine Funktion, die von der Garbage Collection aufgerufen wird. Nicht definierte Funktionen werden durch eine leere Zeichenkette ersetzt ("").

Das kann beispielsweise so aussehen.

session_module_name("user");
session_set_save_handler("ms_open", "ms_close", "ms_read", "ms_write", "ms_destroy", "ms_gc");

Die folgende Lösung basiert auf Datenbankzugriffen. Das Skript ist so ausgelegt, dass die nötige Tabelle beim ersten Zugriff automatisch angelegt wird, sodass man es auch ohne Datenbankkentnisse laufen lassen kann. Voraussetzung ist lediglich, dass die Übungsdatenbank "php5test" im lokalen Mysql-Server vorhanden ist. Die verwendete Tabelle "currentsession" hat folgende Struktur. Sie erlaubt zusätzlich die Zuordnung einer Benutzer-ID, die registrierte Benutzer einer Sitzung zuordnet. So gehen Daten nicht verloren, die bereits anonym erfasst wurden, wenn der Benutzer sich später anmeldet.

CREATE TABLE currentsession (
  sessionID varchar(32) NOT NULL,
  usrID     bigint(20)  NOT NULL,
  variables text        NOT NULL,
  laccess   int(14),
  PRIMARY KEY (sessionID),
  KEY usrID (usrID)
);

Das folgende Skript "session_dbsess.php" beinhaltet nur die Sitzungsklasse "SessionManager". Die Konstanten zu Beginn dienen der Konfiguration und sind je nach verwendeter Datenbank anzupassen. Um zu sehen, wann welche Funktion aufgerufen wird, eignen sich passende "echo"-Befehle in den einzelnen Funktionen.

<?php
## Name      session_dbsess.php

class SessionManager
{
  const SESSIONNAME  = "SESSION";
  const SESSIONDB    = "php5test";
  const DBUSER       = "root";
  ## Passwort ist auch in Funktion SetMySQL" einzutragen
  const DBPASS       = "";
  const DBSERV       = "localhost";
  const DBDROP       = "DROP TABLE currentsession IF EXISTS";
  const DBDEFINITION = "CREATE TABLE currentsession (
                           sessionID varchar(32) NOT NULL,
                           usrID     bigint(20)  NOT NULL,
                           variables text        NOT NULL,
                           laccess   int(14),
                           PRIMARY KEY (sessionID),
                           KEY usrID (usrID)
                        );";
  private static $DB;

  ## Der Konstruktor baut die nötigen Bedingungen für eine benutzerdefinierte
  ## Sitzungsverwaltung auf.
  public function __construct()
  {
    #echo "KONSTRUKTOR<br />\n";
    ## Vorbereitung der MySQL-Datenbank mit der Methode "SetMySQL"
    $this->SetMySQL();
    ## Setzen der Parameter für die Sitzung
    ini_set("session.use_trans_sid",  1);
    ini_set("session.use_cookies",    0);
    ini_set("session.gc_probability", 1);
    session_module_name("user");
    ## Rückruffunktionen, hier als Methoden der Klasse "SessionManager"
    session_set_save_handler(array("SessionManager", "ms_open"), 
                             array("SessionManager", "ms_close"), 
                             array("SessionManager", "ms_read"),
                             array("SessionManager", "ms_write"), 
                             array("SessionManager", "ms_destroy"),
                             array("SessionManager", "ms_gc")
                            );
    session_name(self::SESSIONNAME);                
    session_start();
  }

  ## Prüfung, ob Tabelle "currentsession" vorhanden ist
  private function SetTable()
  {
    $tbl = mysql_list_tables(self::SESSIONDB, self::$DB);
    while ($t = mysql_fetch_assoc($tbl))
    {
      if ($t['Tables_in_'.self::SESSIONDB] == "currentsession")
      {
        return;
      }
    }
    mysql_query(self::DBDROP,       self::$DB);
    mysql_query(self::DBDEFINITION, self::$DB);
  }

  ## Prüfung, ob Datenbank "php5test" vorhanden ist
  private function SetMySQL()
  {    
    self::$DB = @mysql_connect("localhost", "root", "");
    if (self::$DB !== FALSE)
    {
      $dbs = mysql_list_dbs(self::$DB);
      while ($db = mysql_fetch_assoc($dbs))
      {
        if ($db['Database'] == self::SESSIONDB)
        {
          mysql_select_db(self::SESSIONDB, self::$DB);
          $this->SetTable();                    
        return;                    
        }
      }            
    }
    die ("Datenbank 'php5test' ist nicht vorhanden!");
  }

  ## Eröffnung der Sitzung
  function ms_open($sesspath, $sessname) 
  {
    #echo "OPEN \$sesspath = $sesspath; \$sessname = $sessname<br />\n";
    $time   = time();
    $sessid = session_id();
    $query  = "SELECT * FROM currentsession WHERE sessionID = '$sessid'";
    $RS     = mysql_query($query, self::$DB);        
    ## Erneuern der aktuellen Sitzung, indem der Zeitstempel neu gesetzt wird
    if (mysql_num_rows($RS) == 0) 
    {
      $query = "INSERT INTO currentsession (sessionID, laccess) 
                     VALUES ('$sessid', '$time')";
    }
    ## Ist keine Sitzung vorhanden, wird ein neuer Datenbankeintrag erzeugt
    else
    {
      $query = "UPDATE currentsession 
                   SET laccess   = '$time' 
                 WHERE sessionID = '$sessid'";
    }
    $RS = mysql_query($query, self::$DB);
    return $RS;
  }

  ## Lesen der bereits in der Sitzung befindlichen Variablen
  function ms_read($sessid) 
  {
    #echo "READ \$sessid = $sessid;<br />\n";
    $query = "SELECT *
                FROM currentsession
               WHERE sessionID='$sessid'";
    $RS    = mysql_query($query, self::$DB);        
    $arrRS = mysql_fetch_assoc($RS);
    ## Abruf des Felds "variables" in der Tabelle. Die Deserialisierung
    ## wird von PHP selbst erledigt
    if (is_array($arrRS)) 
    {
      return $arrRS['variables'];
    }
    else 
    {
      return FALSE;
    }
  }

  ## Sichern der in "$_SESSION" befindlichen Variablen. PHP bietet
  ## die Sitzungsvariablen bereits serialisiert an.
  function ms_write($sessid, $varis) 
  {
    #echo "WRITE \$sessid = $sessid; \$varis = $varis<br />\n";
    $query = "UPDATE currentsession 
                 SET variables = '$varis' 
               WHERE sessionID = '$sessid'";
    $RS    = mysql_query($query, self::$DB);
    return (bool) $RS;
  }       

  ## Wenn eine Sitzung ausdrücklich oder durch Befehle zerstört wird,
  ## entfernt die Funktion "ms_destroy"  den Eintrag aus der Datenbank.
  function ms_destroy($sessid)
  {
    #echo "DESTROY \$sessid = $sessis;<br />\n";
    $query = "DELETE FROM currentsession 
                    WHERE sessionID = '$sessid'";
    $RS    = mysql_query($query, self::$DB);
    return (bool) $RS;
  }

  ## Aufruf der Garbage Collection je nach Einstellung der Parameter.
  ## Veraltete Sitzungen werden aus der Datenbank entfernt.
  function ms_gc($sesslt) 
  {
    #echo "GC \$sesslt = $sesslt;<br />\n";
    $tStamp = time() - $sesslt;
    $query  = "DELETE FROM currentsession 
                     WHERE laccess < '$tStamp'";
    $RS     = mysql_query($query, self::$DB);
    return (bool) $RS;
  }

  ## Schliessen der Sitzung, was zum Schliessen der Datenbank benutzt wird.
  function ms_close() 
  {
    #echo "CLOSE<br />\n";
    mysql_close(self::$DB);
  }
}

## Einmalige Instanziierung der Klasse "SessionManager"
new SessionManager();
?>

Das folgende Skript zeigt die Nutzung. Die Sitzungsfunktionen werden nach dem eigentlichen Skript aufgerufen. In der gezeigten Ausgabe sind die Zeitpunkte und die jeweils beteiligten Daten zu sehen.

<?php
## Name     session_dbsesstest.php
include("./session_dbsess.php");
?>
<b>Seitenstart</b><br />
<?php
$sessid = session_id();
$x = 122;
$s = "Test";
$a = array(1, 22, "acht");
$_SESSION['x'] = $x;
echo "Die Sitzungs-ID: $sessid<br />\n";
$_SESSION['s'] = $s;
$_SESSION['a'] = $a;
?>
<a href="session_dbsessread.php">Auslesen auf anderer Seite</a><br />
<b>Seitenende</b><br />

Seitenstart
Die Sitzungs-ID: 6e7c1295b62284cb359c73bcfc531694
Auslesen auf anderer Seite
Seitenende

Mit den oben erwähnten eingekommentierten "echo"-Befehlen sieht die Ausgabe wie folgt aus.

KONSTRUKTOR
OPEN $sesspath = /var/lib/php5; $sessname = SESSION
READ $sessid = 6e7c1295b62284cb359c73bcfc531694;

Seitenstart
Die Sitzungs-ID: 6e7c1295b62284cb359c73bcfc531694
Auslesen auf anderer Seite
Seitenende
WRITE $sessid = 6e7c1295b62284cb359c73bcfc531694; $varis = x|i:122;s|s:4:"Test";a|a:3:{i:0;i:1;i:1;i:22;i:2;s:4:"acht";}
CLOSE

Zum Beweis, dass Daten von Seite zu Seite erhalten bleiben, dient hier das Skript "session_dbsessread.php".

<?php
## Name     session_dbsessread.php

include("./session_dbsess.php");
?>
<b>Test</b><br />
<pre>
<?php
foreach ($_SESSION as $var => $val)
{
  echo "\$$var = ";
  var_export($val);
  echo ";<br />";
}
?>
</pre>
<p><a href="session_dbsesstest.php">Zurück</a></p>

KONSTRUKTOR
OPEN $sesspath = /var/lib/php5; $sessname = SESSION
READ $sessid = 6e7c1295b62284cb359c73bcfc531694;

Test

Zurück

WRITE $sessid = 6e7c1295b62284cb359c73bcfc531694; $varis =
CLOSE

Nach dem Klicken auf den Link "Zurück" zeigt die Ausgabe, dass die Daten vollständig wiederhergestellt werden konnten.

KONSTRUKTOR
OPEN $sesspath = /var/lib/php5; $sessname = SESSION
READ $sessid = 6e7c1295b62284cb359c73bcfc531694;

Seitenstart
Die Sitzungs-ID: 6e7c1295b62284cb359c73bcfc531694
Auslesen auf anderer Seite
Seitenende
WRITE $sessid = 6e7c1295b62284cb359c73bcfc531694; $varis = x|i:122;s|s:4:"Test";a|a:3:{i:0;i:1;i:1;i:22;i:2;s:4:"acht";}
CLOSE