Problem mit Session Handler Klasse

xtramen01

Erfahrenes Mitglied
Hallo Leute,

ich habe eine Klasse für meine Sessions.
Funktioniert eigentlich super. Allerdings wird die Session nach der vorgegebenen Zeit nicht aus der DB gelöscht.
Mit Ausnahme dann, wenn man den Browser komplett schließt und dann wieder öffnet. Dann wird die Session nach vorgegebener Zeit auch aus der DB gelöscht.
Eigentlich sollte es aber doch auch funktionieren, wenn man den Browser lediglich aktualisiert.

ALso ich stelle die Lebensdauer der Session z.b. auf 5 Minuten.
Wenn ich nun nach 7 Minuten den Browser wieder aktualisiere, dann sollte die Session doch gelöscht werden.

Vielleicht kann mal jemand über diese Klasse schauen und den Fehler bzw. die Ursache dafür finden.

Danke und Gruß

PHP:
    Namespace _Core\Models;

    use _Core\Controllers\DatabaseController as SQL;

    class SessionHandler{

        public function __construct(){

        session_set_save_handler(array(&$this, '_open'), array(&$this, '_close'), array(&$this, '_read'), array(&$this, '_write'), array(&$this, '_destroy'), array(&$this, '_gc'));

        session_start();

        register_shutdown_function('session_write_close');

        }

        public function _open($save_path, $session_name){
            return true;
        }

        public function _close(){
            return true;
        }




        public function _read($id){

            $query = SQL::Query("SELECT value FROM sessions WHERE id = '" . SQL::SecureInput($id) . "' LIMIT 1");

            if(SQL::NumRows($query) > 0){
                $erg = SQL::FetchArray($query);
                return $erg['value'];
            }

            return '';
        }




        public function _write($id, $sess_data){

            $check = SQL::Query("SELECT id FROM sessions WHERE id = '" . $id . "'");

            if(SQL::FetchArray($check) > 0){
                $query = SQL::Query("UPDATE sessions SET last_updated ='" . SQL::SecureInput(time()) . "', value = '" . SQL::SecureInput($sess_data) . "' WHERE id = '" . SQL::SecureInput($id) . "'");
            }else{
                $query = SQL::Query("INSERT INTO sessions (id, last_updated, start, value) VALUES ('" . SQL::SecureInput($id) . "', '" . SQL::SecureInput(time()) . "', '" . SQL::SecureInput(time()) . "', '" . SQL::SecureInput($sess_data) . "')");
            }

            return mysql_affected_rows();
        }




        public function _destroy($id){

            $query = SQL::Query("DELETE FROM sessions WHERE id = '" . SQL::SecureInput($id) . "'");

            $_SESSION = array();

            return mysql_affected_rows();
        }




        public function _gc($maxlifetime) {

            $maxlifetime = strtotime("-20 minutes");

            $query = SQL::Query("DELETE FROM sessions WHERE last_updated < '" . SQL::SecureInput($maxlifetime) . "'");

            return mysql_affected_rows();
        }




        public static function Register($session, $var) {
            $_SESSION[$session] = $var;
        }




        public static function Output($session) {
            return $_SESSION[$session];
        }




        public static function Check($variable){
            return isset($_SESSION) && array_key_exists($variable, $_SESSION);
        }



        public static function Recreate(){
            return session_regenerate_id();
        }



        public static function Destroy(){
            return session_destroy();
        }



    }
 
Drei Anmerkungen:

- Erweitere die Standard-SessionHandler-Klasse \SessionHandler oder
- benutze das Interface \SessionHandlerInterface und
- warum haben deine Methoden ein _ vornan stehen?

EDIT: Das mit den _ voran habe ich übersehen, du hast ja session_set_save_handler() verwendet - aber unnötigerweise. Wenn du das Interface implementierst, kannst du einfach die Klasse übergeben. Allerdings erst ab PHP 5.4.
 
Zuletzt bearbeitet:
Danke für die Tipps.
Aber kannst Du dir erklären warum die Session nach der angegebenen Zeit nicht gelöscht wird?
Muss ich die Methode evtl. irgendwo extra aufrufen? Aber eigentlich sollte das doch der session_set_save_handler erledigen.

Gruß
 
Ich werde das heute abend mal ausprobieren :)

Kannst ja mal zwischenzeitlich kontrollieren (mit Firefox+Firebug+Cookie-Culler), ob, wie und auf was das Session-Cookie in deinem Browser gesetzt wird.
 
Es kann ja nur an der GarbageCollection liegen, oder? (Habe mich damit noch nicht befasst, dass Session system zu verändern...) Also _gc() sollte schonmal true oder false zurückgeben, aber daran wird es nicht liegen... hast du was an diesen werten: "session.gc_probability and session.gc_divisor" verstellt?
 
Ich habe es nun so gelöst, das ich in der _read Methode, kontrolliere ob die Session noch gültig ist.
Ich hab aber irgendwie das Gefühl, das dies "gepfuscht" ist und es dafür eine probatere Lösung gibt.

An der php.ini habe ich nichts verändert.
Es ist eine CentOS + Plesk Standardinstallation.

Ich kann zwar mit der Lösung Leben. Wäre aber trotzdem dankbar über einen Vorschlag, wie man sowas in der Regel löst.

Gruß und Danke!
 
Das mit read() zu lösen ist mehr als konsequent. read() ist der kleinste Eingriffspunkt, der im Zweifelsfall immer greift.

Denn HTTP ist kein statusbehaftetes Protokoll. Der Browser bei einem Request baut immer eine neue Verbindung zum Server auf. Bei jedem neuen Request muss die Session aufgebaut, die Session-Daten de-serialisiert und in den Session-Scope gelegt werden. Beim Verbindungsabbau, also dann, wenn der Client alle Daten erhalten hat, wird die Session schlafen gelegt, sprich mittels write() bspw. in die Datenbank geschrieben.

Wenn ein Client jetzt eine Seite aufgerufen hat, und damit die Session in der Datenbank liegt, der Client nach einer gewissen Zeit wieder kommt, wird die Session wieder ausgelesen. Das ist der früheste Zeitpunkt, an dem du eine Logik einbauen kannst, die prüft, ob die Session in der Datenbank noch aktuell ist. Daher hast du das IMHO absolut korrekt implementiert.

Zur Methode gc(): Diese Methode kommt zur Anwendung, wenn ein Client B die Session von Client A aufräumen soll. Eine Garbage-Collection kann für nur einen Benutzer nicht zum Tragen kommen. Denn wenn man das obige im Kopf durch spielt, läuft es nämlich so ab:

- Client kommt, Session wird neu erstellt (über session_start())
- Client bekommt seine Daten passend zum Request, Session wird serialisiert (in die DB geschrieben)
- Zeit vergeht (> max life time)
- Client kommt wieder, Session wird de-serialisiert (über session_start()), gleichzeitig findet (evtl. => siehe Manual) GC statt - ist aber nicht garantiert
- Client bekommt seine Daten passend zum Request, Session wird serialisiert (widerum in die DB geschrieben)
- .....

Dadurch das die GC nicht garantiert statt findet, kann sich das Spielchen rein theoretisch immer wieder so abspielen. Als Anmerkung hier Auszug aus dem Manual:

http://php.net/manual/en/sessionhandler.gc.php hat gesagt.:
Called randomly by PHP internally when a session starts or when session_start() is invoked.

Das bedeutet, das die Garbage-Collection, wenn sie ausgeführt wird, nur auf Sessions von anderen Benutzern auswirken kann. Somit wird der Client B (aus dem obigen Beispiel) zum Aufräumer für Client A.
 
Zurück