sql >> Databasteknik >  >> RDS >> Mysql

Hur sparar jag PHP-sessionsdata till en databas istället för i filsystemet?

Jag har under loppet av flera timmars felsökning upptäckt att de refererade artiklarna som hittats på många Google-sökningar samt en betydande delmängd av Stack Overflow-svar som här , här och här alla tillhandahåller ogiltig eller inaktuell information.

Saker som kan orsaka [kritiska] problem med att spara sessionsdata i en databas:

  • Medan alla exempel online säger att du kan "fylla" session_set_save_handler , ingen av dem anger att du också måste ställa in register_shutdown_function('session_write_close') också (referens ).

  • Flera (äldre) guider hänvisar till en föråldrad SQL-databasstruktur och bör inte användas. Databasstrukturen som du behöver för att spara sessionsdata i databasen är:id /access /data . Det är allt. inget behov av olika extra tidsstämpelkolumner som jag har sett på några "guider" och exempel.

    • Flera av de äldre guiderna har också föråldrad MySQL-syntax som t.ex. DELETE * FROM ...
  • Klassen [gjord i min fråga] måste implementera SessionHandlerInterface . Jag har sett guider (som hänvisas till ovan) som ger implementeringen av sessionHandler vilket inte är ett lämpligt gränssnitt. Kanske hade tidigare versioner av PHP en något annorlunda metod (förmodligen <5.4).

  • Sessionsklassmetoderna måste returnera de värden som anges i PHP-manualen. Återigen, förmodligen ärvt från pre-5.4 PHP men två guider jag läste angav att class->open returnerar raden som ska läsas, medan PHP-manualen anger att den behöver returnera true eller false bara.

  • Detta är orsaken till mitt ursprungliga problem :Jag använde anpassade sessionsnamn (faktiskt id:n som sessionsnamn och sessions-id:n är samma sak! ) enligt det här mycket bra StackOverflow-inlägget och detta genererade ett sessionsnamn som var 128 tecken långt. Eftersom sessionsnamnet är den enda nyckeln som måste knäckas för att kompromissa med en session och ta över med en kapning av sessioner då är ett längre namn/id en mycket bra sak.

    • Men detta orsakade ett problem eftersom MySQL i tysthet skar upp sessions-id:t ner till bara 32 tecken istället för 128, så den kunde aldrig hitta sessionsdata i databasen. Detta var en helt tyst fråga (kanske på grund av att min databasanslutningsklass inte kastade varningar för sådana saker). Men det är den här man ska se upp med. Om du har några problem med att hämta sessioner från en databas, kontrollera först att den fullständiga sessions-id kan lagras i det angivna fältet.

Så med allt det där ur vägen finns det några extra detaljer att lägga till också:

PHP-manualsidan (länkad ovan) visar en olämplig hög med rader för ett klassobjekt:

Medan det fungerar lika bra om du lägger detta i klasskonstruktorn:

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();
    }
...
}

Detta betyder att allt du behöver för att sedan starta en session på din utdatasida är:

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

//Bang session has been setup and started and works

Som referens är den kompletta sessionskommunikationsklassen som följer, detta fungerar med PHP 5.6 (och förmodligen 7 men inte testat på 7 ännu)

<?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();
    }

}

Användning:Som visas precis ovanför klasskodtexten.




  1. Allvarligt fel:Anrop till en medlemsfunktion bind_param() på boolean

  2. Skapa en ODBC-länkad server utan att konfigurera en datakälla

  3. Det snabbaste sättet att kontrollera om några poster i en databastabell?

  4. Java SQL FEL:Relationstabellnamn finns inte