Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Wie speichere ich PHP-Sitzungsdaten in einer Datenbank statt im Dateisystem?

Ich habe im Laufe von mehreren Stunden Debugging festgestellt, dass die Artikel, auf die verwiesen wird, bei zahlreichen Google-Suchen gefunden wurden, sowie eine erhebliche Teilmenge von Stack Overflow-Antworten wie hier , hier und hier alle liefern ungültige oder veraltete Informationen.

Dinge, die [kritische] Probleme beim Speichern von Sitzungsdaten in einer Datenbank verursachen können:

  • Während alle Online-Beispiele angeben, dass Sie den session_set_save_handler "füllen" können , keiner von ihnen besagt, dass Sie auch die register_shutdown_function('session_write_close') setzen müssen auch (Referenz ). ).

  • Mehrere (ältere) Anleitungen beziehen sich auf eine veraltete SQL-Datenbankstruktur und sollten nicht verwendet werden. Die Datenbankstruktur, die Sie zum Speichern von Sitzungsdaten in der Datenbank benötigen, lautet:id /access /data . Das ist es. keine Notwendigkeit für verschiedene zusätzliche Zeitstempelspalten, wie ich in einigen "Leitfäden" und Beispielen gesehen habe.

    • Einige der älteren Anleitungen haben auch eine veraltete MySQL-Syntax wie DELETE * FROM ...
  • Die Klasse [in meiner Frage erstellt] muss implementieren das SessionHandlerInterface . Ich habe Anleitungen (siehe oben) gesehen, die die Implementierung von sessionHandler beschreiben das ist keine geeignete Schnittstelle. Vielleicht hatten frühere Versionen von PHP eine etwas andere Methode (wahrscheinlich <5.4).

  • Die Methoden der Session-Klasse müssen gibt die im PHP-Handbuch festgelegten Werte zurück. Wieder wahrscheinlich von PHP vor 5.4 geerbt, aber zwei Leitfäden, die ich gelesen habe, gaben an, dass class->open gibt die zu lesende Zeile zurück, wohingegen das PHP-Handbuch sagt dass es true zurückgeben muss oder false nur.

  • Dies ist die Ursache meines ursprünglichen Problems :Ich habe benutzerdefinierte Sitzungsnamen verwendet (eigentlich sind IDs als Sitzungsnamen und Sitzungs-IDs dasselbe! ) gemäß diesem sehr guten StackOverflow-Beitrag und dies generierte einen Sitzungsnamen, der 128 Zeichen lang war. Da der Sitzungsname der einzige Schlüssel ist, der geknackt werden muss, um eine Sitzung zu kompromittieren und mit einem Session-Hijacking dann ist ein längerer name/id eine sehr gute sache.

    • Dies verursachte jedoch ein Problem, da MySQL die Sitzungs-ID stillschweigend abschnitt auf nur 32 statt 128 Zeichen reduziert, sodass die Sitzungsdaten nie in der Datenbank gefunden werden konnten. Dies war ein völlig stilles Problem (möglicherweise aufgrund meiner Datenbankverbindungsklasse, die keine Warnungen vor solchen Dingen ausgibt). Aber das ist derjenige, auf den man achten sollte. Wenn Sie Probleme beim Abrufen von Sitzungen aus einer Datenbank haben, überprüfen Sie zunächst, ob die Datei vollständig ist Session-ID kann im dafür vorgesehenen Feld hinterlegt werden.

Nachdem all das aus dem Weg geräumt ist, gibt es noch einige zusätzliche Details hinzuzufügen:

Die PHP-Handbuchseite (oben verlinkt) zeigt einen ungeeigneten Zeilenhaufen für ein Klassenobjekt:

Wobei es genauso gut funktioniert, wenn Sie dies in den Klassenkonstruktor einfügen:

class MySessionHandler implements SessionHandlerInterface {

    private $database = null;

public function __construct(){

    $this->database = new Database(whatever);

    // Set handler to overide SESSION
    session_set_save_handler(
        array($this, "open"),
        array($this, "close"),
        array($this, "read"),
        array($this, "write"),
        array($this, "destroy"),
        array($this, "gc")
        );
    register_shutdown_function('session_write_close');
    session_start();
    }
...
}

Das bedeutet, dass Sie zum Starten einer Sitzung auf Ihrer Ausgabeseite nur Folgendes benötigen:

<?php
require "path/to/sessionhandler.class.php"; 
new MySessionHandler();

//Bang session has been setup and started and works

Als Referenz ist die vollständige Session-Kommunikationsklasse wie folgt, dies funktioniert mit PHP 5.6 (und wahrscheinlich 7, aber noch nicht auf 7 getestet)

<?php
/***
 * Created by PhpStorm.
 ***/
class MySessionHandler implements SessionHandlerInterface {
    private $database = null;

    public function __construct($sessionDBconnectionUrl){
        /***
         * Just setting up my own database connection. Use yours as you need.
         ***/ 

            require_once "class.database.include.php";
            $this->database = new DatabaseObject($sessionDBconnectionUrl);

        // Set handler to overide SESSION
        session_set_save_handler(
            array($this, "open"),
            array($this, "close"),
            array($this, "read"),
            array($this, "write"),
            array($this, "destroy"),
            array($this, "gc")
        );
        register_shutdown_function('session_write_close');
        session_start();
    }

    /**
     * Open
     */
    public function open($savepath, $id){
        // If successful
        $this->database->getSelect("SELECT `data` FROM sessions WHERE id = ? LIMIT 1",$id,TRUE);
        if($this->database->selectRowsFoundCounter() == 1){
            // Return True
            return true;
        }
        // Return False
        return false;
    }
    /**
     * Read
     */
    public function read($id)
    {
        // Set query
        $readRow = $this->database->getSelect('SELECT `data` FROM sessions WHERE id = ? LIMIT 1', $id,TRUE);
        if ($this->database->selectRowsFoundCounter() > 0) {
            return $readRow['data'];
        } else {
            return '';
        }
    }

    /**
     * Write
     */
    public function write($id, $data)
    {
        // Create time stamp
        $access = time();

        // Set query
        $dataReplace[0] = $id;
        $dataReplace[1] = $access;
        $dataReplace[2] = $data;
        if ($this->database->noReturnQuery('REPLACE INTO sessions(id,access,`data`) VALUES (?, ?, ?)', $dataReplace)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Destroy
     */
    public function destroy($id)
    {
        // Set query
        if ($this->database->noReturnQuery('DELETE FROM sessions WHERE id = ? LIMIT 1', $id)) {
            return true;
        } else {

            return false;
        }
    }
    /**
     * Close
     */
    public function close(){
        // Close the database connection
        if($this->database->dbiLink->close){
            // Return True
            return true;
        }
        // Return False
        return false;
    }

    /**
     * Garbage Collection
     */
    public function gc($max)
    {
        // Calculate what is to be deemed old
        $old = time() - $max;

        if ($this->database->noReturnQuery('DELETE FROM sessions WHERE access < ?', $old)) {
            return true;
        } else {
            return false;
        }
    }

    public function __destruct()
    {
        $this->close();
    }

}

Verwendung:Wie direkt über dem Text des Klassencodes gezeigt.