sql >> Base de Datos >  >> RDS >> Mysql

¿Cómo guardo los datos de la sesión de PHP en una base de datos en lugar de en el sistema de archivos?

En el transcurso de varias horas de depuración, descubrí que los artículos a los que se hace referencia se encuentran en numerosas búsquedas de Google, así como en un subconjunto significativo de respuestas de Stack Overflow, como aquí , aquí y aquí todos proporcionan información no válida o desactualizada.

Cosas que pueden causar problemas [críticos] al guardar datos de sesión en una base de datos:

  • Si bien todos los ejemplos en línea indican que puede "llenar" el session_set_save_handler , ninguno de ellos indica que también debe configurar register_shutdown_function('session_write_close') también (referencia ).

  • Varias guías (más antiguas) hacen referencia a una estructura de base de datos SQL obsoleta y no ser usado. La estructura de la base de datos que necesita para guardar los datos de la sesión en la base de datos es:id /access /data . Eso es todo. no hay necesidad de varias columnas de marca de tiempo adicionales como he visto en algunas "guías" y ejemplos.

    • Varias de las guías más antiguas también tienen una sintaxis de MySQL obsoleta, como DELETE * FROM ...
  • La clase [realizada en mi pregunta] debe implementar la SessionHandlerInterface . He visto guías (mencionadas anteriormente) que brindan la implementación de sessionHandler que no es una interfaz adecuada. Quizás las versiones anteriores de PHP tenían un método ligeramente diferente (probablemente <5.4).

  • Los métodos de clase de sesión deben devuelve los valores establecidos por el manual de PHP. Nuevamente, probablemente heredado de PHP anterior a 5.4, pero dos guías que leí decían que class->open devuelve la fila que se va a leer, mientras que los estados manuales de PHP que necesita devolver true o false solo.

  • Esta es la causa de mi problema original :Estaba usando nombres de sesión personalizados (en realidad, los ID como nombres de sesión y los ID de sesión son lo mismo! ) según esta muy buena publicación de StackOverflow y esto generaba un nombre de sesión de 128 caracteres. Como el nombre de la sesión es la única clave que se necesita descifrar para comprometer una sesión y tomar el control con un secuestro de sesión entonces un nombre/identificación más largo es algo muy bueno.

    • Pero esto causó un problema porque MySQL estaba cortando silenciosamente la identificación de la sesión hasta solo 32 caracteres en lugar de 128, por lo que nunca pudo encontrar los datos de la sesión en la base de datos. Este fue un problema completamente silencioso (tal vez debido a que mi clase de conexión de base de datos no arroja advertencias de tales cosas). Pero este es el que hay que tener cuidado. Si tiene algún problema con la recuperación de sesiones de una base de datos, primero verifique que el completo la identificación de la sesión se puede almacenar en el campo provisto.

Entonces, con todo eso fuera del camino, también hay algunos detalles adicionales para agregar:

La página del manual de PHP (vinculada arriba) muestra una pila de líneas inadecuadas para un objeto de clase:

Mientras que funciona igual de bien si pones esto en el constructor de clases:

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

Esto significa que para iniciar una sesión en su página de salida todo lo que necesita es:

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

//Bang session has been setup and started and works

Como referencia, la clase de comunicación de sesión completa es la siguiente, esto funciona con PHP 5.6 (y probablemente 7, pero aún no se ha probado en 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();
    }

}

Uso:como se muestra justo encima del texto del código de clase.