sql >> Database >  >> RDS >> Mysql

Hoe sla ik PHP-sessiegegevens op in een database in plaats van in het bestandssysteem?

Ik heb in de loop van enkele uren debuggen ontdekt dat de artikelen waarnaar wordt verwezen in talloze Google-zoekopdrachten, evenals een aanzienlijke subset van Stack Overflow-antwoorden, zoals hier , hier en hier ze bieden allemaal ongeldige of verouderde informatie.

Dingen die [kritieke] problemen kunnen veroorzaken bij het opslaan van sessiegegevens in een database:

  • Hoewel alle online voorbeelden aangeven dat je de session_set_save_handler kunt "vullen" , geen van hen geeft aan dat u ook de register_shutdown_function('session_write_close') moet instellen ook (referentie ).

  • Verschillende (oudere) handleidingen verwijzen naar een verouderde SQL Database-structuur en mogen niet worden gebruikt. De databasestructuur die u nodig heeft om sessiegegevens in de database op te slaan is:id /access /data . Dat is het. geen behoefte aan verschillende extra tijdstempelkolommen zoals ik heb gezien bij een paar "gidsen" en voorbeelden.

    • Verscheidene van de oudere handleidingen hebben ook een verouderde MySQL-syntaxis, zoals DELETE * FROM ...
  • De klasse [gemaakt in mijn vraag] moet implementeren de SessionHandlerInterface . Ik heb handleidingen gezien (hierboven vermeld) die de implementatie van sessionHandler . geven wat geen geschikte interface is. Misschien hadden eerdere versies van PHP een iets andere methode (waarschijnlijk <5.4).

  • De sessieklasse-methoden moeten retourneer de waarden die zijn uiteengezet in de PHP-handleiding. Nogmaals, waarschijnlijk overgenomen van PHP van vóór 5.4, maar twee handleidingen die ik las, verklaarden dat class->open geeft de te lezen rij terug, terwijl de PHP-handleiding aangeeft dat het true moet retourneren of false alleen.

  • Dit is de oorzaak van mijn oorspronkelijke probleem :Ik gebruikte aangepaste sessienamen (eigenlijk zijn id's als sessienamen en sessie-id's zijn hetzelfde! ) volgens dit zeer goede StackOverflow-bericht en dit genereerde een sessienaam die 128 tekens lang was. Aangezien de sessienaam de enige sleutel is die moet worden gekraakt om een ​​sessie te compromitteren en over te nemen met een sessie kaping dan is een langere naam/id een hele goede zaak.

    • Maar dit veroorzaakte een probleem omdat MySQL stilletjes de sessie-ID aan het snijden was tot slechts 32 tekens in plaats van 128, dus het was nooit in staat om de sessiegegevens in de database te vinden. Dit was een volledig stil probleem (misschien omdat mijn databaseverbindingsklasse geen waarschuwingen voor dergelijke dingen gaf). Maar dit is degene om op te letten. Als je problemen hebt met het ophalen van sessies uit een database, controleer dan eerst of de volledige sessie-ID kan worden opgeslagen in het daarvoor bestemde veld.

Dus met dat alles uit de weg, zijn er ook wat extra details om toe te voegen:

De PHP-handleiding (hierboven gelinkt) toont een ongeschikte stapel regels voor een klasseobject:

Terwijl het net zo goed werkt als je dit in de klassenconstructor plaatst:

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

Dit betekent dat u alleen het volgende nodig heeft om een ​​sessie op uw uitvoerpagina te starten:

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

//Bang session has been setup and started and works

Ter referentie:de volledige sessie-communicatieklasse is als volgt, dit werkt met PHP 5.6 (en waarschijnlijk 7 maar nog niet getest op 7)

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

}

Gebruik:zoals weergegeven net boven de tekst van de klassecode.




  1. Unieke identifier (guid) als primaire sleutel in databaseontwerp

  2. Hoe voeg je meerdere rijen tegelijk in een mysql-database in met voorbereide instructies?

  3. kan geen verbinding maken met mysql docker vanuit lokaal

  4. Oracle ODP.NET versie agnostisch alternatief