Mysql
 sql >> Base de données >  >> RDS >> Mysql

Comment enregistrer les données de session PHP dans une base de données plutôt que dans le système de fichiers ?

J'ai trouvé au cours de plusieurs heures de débogage que les articles référencés trouvés sur de nombreuses recherches Google ainsi qu'un sous-ensemble important de réponses Stack Overflow telles que ici , ici et ici fournissent tous des informations invalides ou obsolètes.

Éléments pouvant causer des problèmes [critiques] lors de l'enregistrement des données de session dans une base de données :

  • Alors que tous les exemples en ligne indiquent que vous pouvez "remplir" le session_set_save_handler , aucun d'entre eux n'indique que vous devez également définir la register_shutdown_function('session_write_close') aussi (référence ).

  • Plusieurs guides (plus anciens) font référence à une structure de base de données SQL obsolète et ne devraient pas être utilisé. La structure de base de données dont vous avez besoin pour enregistrer les données de session dans la base de données est :id /access /data . C'est ça. pas besoin de diverses colonnes d'horodatage supplémentaires comme je l'ai vu sur quelques "guides" et exemples.

    • Plusieurs des anciens guides ont également une syntaxe MySQL obsolète, telle que DELETE * FROM ...
  • La classe [faite dans ma question] doit implémenter l'SessionHandlerInterface . J'ai vu des guides (référencés ci-dessus) qui donnent l'implémentation de sessionHandler qui n'est pas une interface appropriée. Peut-être que les versions précédentes de PHP avaient une méthode légèrement différente (probablement <5.4).

  • Les méthodes de classe de session doivent renvoie les valeurs définies par le manuel PHP. Encore une fois, probablement hérité de PHP pré-5.4 mais deux guides que j'ai lus indiquaient que class->open renvoie la ligne à lire, alors que le PHP manual indique qu'il doit retourner true ou false uniquement.

  • C'est la cause de mon problème d'origine  :J'utilisais des noms de session personnalisés (en fait, les identifiants en tant que noms de session et les identifiants de session sont la même chose ! ) selon ce très bon article StackOverflow et cela générait un nom de session de 128 caractères. Comme le nom de session est la seule clé qui doit être crackée pour compromettre une session et prendre le relais avec un piratage de session alors un nom/id plus long est une très bonne chose.

    • Mais cela a causé un problème car MySQL découpait silencieusement l'identifiant de session jusqu'à 32 caractères au lieu de 128, il n'a donc jamais été en mesure de trouver les données de session dans la base de données. C'était un problème complètement silencieux (peut-être parce que ma classe de connexion à la base de données ne lançait pas d'avertissements de telles choses). Mais c'est celui-là qu'il faut surveiller. Si vous rencontrez des problèmes pour récupérer des sessions à partir d'une base de données, vérifiez d'abord que le complet l'identifiant de session peut être stocké dans le champ fourni.

Donc, avec tout cela, il y a aussi quelques détails supplémentaires à ajouter :

La page de manuel PHP (liée ci-dessus) montre une pile de lignes inappropriée pour un objet de classe :

Alors que cela fonctionne aussi bien si vous mettez ceci dans le constructeur de la classe :

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

Ceci signifie que pour ensuite démarrer une session sur votre page de sortie, tout ce dont vous avez besoin est :

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

//Bang session has been setup and started and works

Pour référence, la classe de communication Session complète est la suivante, cela fonctionne avec PHP 5.6 (et probablement 7 mais pas encore testé sur 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();
    }

}

Utilisation :comme indiqué juste au-dessus du texte du code de classe.