Postanowiłem napisać własny system obsługi sesji oparty o bazę danych, po przejrzeniu kilku istniejących już rozwiązać stosowanych w skryptach open source doszedłem do wniosku że jest to za skomplikowane na moje potrzeby i napisałem coś takiego :
Najpierw okrojona wersja mojego sterownika do obsługi mysql'a(super improved myql


<?php class Error extends Exception{} class DB extends mysqli{ private function __construct($g_config){ @parent::__construct($g_config['host'], $g_config['user'], $g_config['pass'], $g_config['database']); if(mysqli_connect_error()){ throw new Error('<br /><br />Problem z nawiązaniem połączenia!<br /> '.mysqli_connect_error()); } } if(!$obiekt instanceof DB){ $obiekt = new DB($g_config); } return $obiekt; } public function query($query){ $wynik = parent::query($query); if(mysqli_error($this)){ throw new Error('<br><br />Problem z wykonaniem zapytania! <br />'.mysqli_error($this)); } return $wynik; } public function __destruct(){ if(!mysqli_connect_error()){ $this->close(); } } } ?>
Klasa do obsługi sesji:
<?php interface sessionHandler{ public function _open($savePath, $sessionName); public function _close(); public function _read($sessionId); public function _write($sessionId, $sessionData); public function _destroy($sessionId); public function _gc($maxLifetime = NULL); } class Session implements sessionHandler{ private $db; private $sessionId; private $sessionName; private $sessionLifetime; private $sessionTable; private $isNew = TRUE; public function __construct(&$db, $sessionTable = 'my_session_data', $sessionLifetime = 30, $sessionProbability = 5, $sessionDivisor = 100 ){ $this->db = $db; $this->sessionLifetime = $sessionLifetime; $this->sessionTable = $sessionTable; ); } public function _open($savePath, $sessionName){ //echo '<br /><font color=violet>_open()</font><br /><br />'; return(TRUE); } public function _close(){ //echo '<br /><font color=violet>_close()</font><br />'; return(TRUE); } public function _read($sessionId){ $sql = 'SELECT data FROM '.$this->sessionTable.' WHERE session_id = "'.$this->db->real_escape_string($this->sessionId).'" AND( user_agent="'.$this->db->real_escape_string(USER_AGENT).'" AND user_ip="'.$this->db->real_escape_string(USER_IP).'" )'; //echo '<br /><b>_read('.$this->sessionId.')</b><br />'.$sql; try{ $wynik = $this->db->query($sql); if($wynik->num_rows == 1){ $this->isNew = FALSE; } return(''); }catch(Error $e){ } } public function _write($sessionId, $sessionData){ if($this->isNew){ //echo '<br /><br /><b>isset( $_COOKIE[ '.$this->sessionName.' ] )</b><br />'; $this->_destroy($this->sessionId); } $sql = 'INSERT INTO '.$this->sessionTable.' (session_id, data, start, time, user_agent, user_ip) VALUES ("'.$this->db->real_escape_string($this->sessionId).'", "'.$this->db->real_escape_string($sessionData).'", "'.$this->db->real_escape_string(USER_AGENT).'", "'.$this->db->real_escape_string(USER_IP).'" )'; //echo '<br /><b>_write(INSERT)('.$this->sessionId.', '.$sessionData.')</b><br />'.$sql; try{ $this->db->query($sql); $this->isNew = FALSE; if($this->db->affected_rows > 0){ return(TRUE); } }catch(Error $e){ } }else{ $sql = 'UPDATE '.$this->sessionTable.' SET data="'.$this->db->real_escape_string($sessionData).'", user_agent="'.$this->db->real_escape_string(USER_AGENT).'", user_ip="'.$this->db->real_escape_string(USER_IP).'" WHERE session_id="'.$this->db->real_escape_string($this->sessionId).'"'; //echo '<br /><b>_write(UPDATE)('.$this->sessionId.', '.$sessionData.')</b><br />'.$sql; try{ $this->db->query($sql); if($this->db->affected_rows){ return(TRUE); } }catch(Error $e){ } } return(FALSE); } public function _destroy($sessionId){ //echo '<br /><b>_destroy('.$this->sessionId.')</b><br />'; $sql = 'DELETE FROM '.$this->sessionTable.' WHERE session_id="'.$this->db->real_escape_string($this->sessionId).'"'; try{ if($this->db->query($sql)){ return(TRUE); }else{ return(FALSE); } }catch(Error $e){ } } public function _gc($maxLifetime = NULL){ //echo '<br /><b>_gc()</b><br />'; $sql = 'DELETE FROM '.$this->sessionTable.' try{ if($this->db->query($sql)){ return(TRUE); }else{ return(FALSE); } }catch(Error $e){ } } } ?>
Struktura bazy danych:
<?php CREATE TABLE `session` ( `start` varchar(11) NOT NULL, `data` text, `user_ip` varchar(40) NOT NULL, `user_agent` varchar(150) NOT NULL, ) ENGINE=InnoDB DEFAULT CHARSET=utf8_bin; ?>
Plik testowy:
<?php $g_config['host'] = 'localhost'; $g_config['user'] = 'user'; $g_config['pass'] = 'pass'; $g_config['database'] = 'testy'; include_once('db.class.php'); include_once('session.class.php'); try{ $db = DB::instance($g_config); $s = new Session($db); }else{ } $_SESSION['a'] = 'Mietek'; }catch(Error $e){ } ?>
Na początku dodam ze chce ograniczyć do minimum ilość wykonywanych zapytań.

Aby zobaczyć cokolwiek w oknie przeglądarki trzeba odkomentować niektóre linie w kodzie.
W rozwiązaniach które przeglądałem sprawdzanie istnienia/aktywności sesji jest wykonywane zarówno w metodzie _read() jak i _write() w ten sposób "tracimy" dodatkowe zapytanie(choć ma to i plus rozwiązuje mój problem :-)) ponieważ i tak jeśli rozpoczniemy sesje wykonywane są metody _read() i _write().
Ja w metodzie _read() sprawdzam czy istnieje sesja i nie wygasła, jeśli jest to ustawiam zmienną isNew na FALSE(domyślnie TRUE) dzięki temu metoda _write() wie że ma wykonać UPDATE a nie INSERT INTO(jeśli isNew ustawiona na TRUE) i odwrotnie jeśli zmienna isNew ma wartość TRUE. W tym momencie zaczynają się schody, jeśli sesja wygasła(wygasła w bazie z powodu nieaktywności usera ale przeglądarka jest otwarta) to metoda _write() będzie próbowała dodać nowy rekord do bazy o id takim samym jak ten wygasły w bazie (to samo ciacho) i wyniknie błąd w zapytaniu ponieważ kolumna session_id ma primary_key w celu zwiększenia wydajności bazy a jak wiadomo to pole musi być unikalne. Jak widać ja to rozwiązuje tym kawałkiem kodu:
<?php $this->_destroy($this->sessionId); } ?>
Wszystko ładnie pięknie działa tylko tylko mamy już dodatkowe zapytanie

Kombinowałem z session_regenerate_id" title="Zobacz w manualu PHP" target="_manual ale najwyraźniej wewnątrz mechanizmu obsługi sesji to nie działa - i dobrze, pozostaje chyba tylko session_set_cookie_params" title="Zobacz w manualu PHP" target="_manual i nadpisywanie cookie albo dodatkowe zapytanie.
No i narzuca się pytanie czy nie lepiej wykonać jedno zapytanie DELETE, SELECT więcej czy kombinować z zmianą id?
P.S IMO jeśli sesja wygaśnie i tak powinno zostać zmienione id sessji

Pozdrawiam