Günther S: Probleme mit selfmade Session-Handler (DB-basiert)

Beitrag lesen

Hallo,

ich habe zwei Probleme mit meinem selbstgeschriebenen, datenbankbasierter (MySQL 5) PHP5-Session-Handler. In das Ganze involviert ist außerdem die PHP-eigene mysqli-Schnittstelle, welche sich eine MySQLi-Wrapper-Klasse zunutze macht. Bevor ich Genaueres schreibe, hier schonmal meine beiden Probleme:

  1. Die Garbage-Collector-Methode des Session-Handlers löscht alte Datenbank-Einträge erst nach 30 Stunden anstatt 30 Minuten (d.h. nach [eingestellte Zeit in Minuten] * 60). Das ist nicht nur Zufall: Über SHOW PROCESSLIST habe ich schon ein paar mal die Anfrage "... INTERVAL 1800 MINUTE ..." (30 * 60 = 1800) vom Garbage-Collector entdeckt. Wenn ich die Methode Session::_gc() aber "von Hand" aufrufe, steht im Datenbank-Query - wie erwünscht - "... INTERVAL 30 MINUTE ...".

  2. Sporadisch tritt die Fehlermeldung "Notice: Object of class mysqli_result could not be converted to int in [...]index.php on line 85" auf. In dieser Zeile steht nichts außer "session_start();". Das interessante ist, dass das wirklich nur sporadisch auftritt - bei ca. 98% aller Skriptaufrufe kommt diese Fehlermeldung nicht. Ich konnte bisher keinerlei Regelmäßigkeit hinter diesen Meldungen erkennen. Weder erscheint sie immer beim ersten Aufruf der Website, noch beim "manuellen" Ändern oder Weglassen/Hinzufügen der/einer Subdomain o.Ä.

Und nun einige Informationen zu dem Skript und den Session-Konfigurationsvariablen.

  
// Session-Konfigurationsvariablen laut phpinfo() (lokale und globale Werte sind jeweils identisch)  
session.auto_start Off  
session.bug_compat_42 Off  
session.bug_compat_warn On  
session.cache_expire 180  
session.cache_limiter nocache  
session.cookie_domain no value  
session.cookie_httponly Off  
session.cookie_lifetime 0  
session.cookie_path /  
session.cookie_secure Off  
session.entropy_file no value  
session.entropy_length 0  
session.gc_divisor 100  
session.gc_maxlifetime 1440  
session.gc_probability 0  
session.hash_bits_per_character 4  
session.hash_function 0  
session.name PHPSESSID  
session.referer_check no value  
session.save_handler files  
session.save_path /var/lib/php5  
session.serialize_handler php  
session.use_cookies On  
session.use_only_cookies Off  
session.use_trans_sid 0  

  
  
// Definition der unten eingebauten Konstanten (vor dem Einbinden + Aufruf der Session-Klasse)  
  
define('SESSION_LIFETIME',   30); // in minutes  
define('SESSION_GC_PROBABILITY', 1);  
define('SESSION_COOKIE_DOMAIN', ".meinedomain.de"); // hier steht natürlich im Original die echte Domain :-)  
  
  
  
  
// Session-Handler-Klasse  
  
ini_set('session.gc_maxlifetime', (int)SESSION_LIFETIME*60);  
ini_set('session.gc_probability', SESSION_GC_PROBABILITY);  
ini_set('session.gc_divisor', 100);  
ini_set('session.cookie_domain', SESSION_COOKIE_DOMAIN);  
  
$session = new Session();  
  
class Session  
{  
 public function __construct()  
 {  
  
  session_set_save_handler(array('Session', '_open'),  
        array('Session', '_close'),  
        array('Session', '_read'),  
        array('Session', '_write'),  
        array('Session', '_destroy'),  
        array('Session', '_gc'));  
 }  
  
 public function _open()  
 {  
  // DB-Verbindung besteht zu diesem Zeitpunkt bereits  
 }  
  
 public function _close()  
 {  
    // DB-Verbindung wird am Ende der Skriptlaufzeit geschlossen  
 }  
  
 public function _read($sess_id)  
 {  
  global $db;  
  
  $res = $db->query("SELECT sess_data FROM sessions WHERE sess_id = '" . mysqli_real_escape_string($sess_id) . "'");  
  
  $row = mysqli_fetch_assoc($res);  
  
  return array_pop($row);  
 }  
  
 public function _write($sess_id, $sess_data)  
 {  
  global $db;  
  
  if(!is_object($db)) $db = new Sqli(DBDSN);  
  
  return $db->query("REPLACE INTO sessions SET sess_id = '" . mysqli_real_escape_string($sess_id) . "', last_access = NOW(), sess_data = '" .mysqli_real_escape_string($sess_data) . "'");  
 }  
  
 public function _destroy($sess_id)  
 {  
  global $db;  
  
  return $db->query("DELETE FROM sessions WHERE sess_id = '" . mysqli_real_escape_string($sess_id) . "'");  
 }  
  
 public function _gc($sess_lifetime = SESSION_LIFETIME)  
 {  
  global $db;  
  
  $db->query("DELETE FROM sessions WHERE last_access < DATE_SUB(NOW(), INTERVAL " . $sess_lifetime . " MINUTE)");  
  
  return $db->query("OPTIMIZE TABLE ".DBPREFIX."sessions");  
 }  
}  
  

Hat jemand einen Denkanstoß für mich?

Gruß,
Günther