Mysql
 sql >> база данни >  >> RDS >> Mysql

Как да запиша данни за сесията на PHP в база данни вместо във файловата система?

В хода на няколко часа отстраняване на грешки открих, че посочените статии са намерени в многобройни търсения в Google, както и значителна подмножество от отговори на Stack Overflow, като тук , тук и тук всички предоставят невалидна или остаряла информация.

Неща, които могат да причинят [критични] проблеми със запазването на данни за сесията в база данни:

  • Докато всички примери онлайн посочват, че можете да "попълните" session_set_save_handler , нито един от тях не посочва, че трябва да зададете и register_shutdown_function('session_write_close') също (справка ).

  • Няколко (по-стари) ръководства се отнасят до остаряла структура на SQL база данни и трябва да не използван. Структурата на базата данни, която ви е необходима за запазване на данни за сесията в базата данни е:id /access /data . Това е. няма нужда от различни допълнителни колони за времеви отпечатъци, както видях в няколко „ръководства“ и примери.

    • Няколко от по-старите ръководства също имат остарял синтаксис на MySQL, като DELETE * FROM ...
  • Класът [направен в моя въпрос] трябва внедрява SessionHandlerInterface . Виждал съм ръководства (посочени по-горе), които дават реализацията на sessionHandler което не е подходящ интерфейс. Може би предишните версии на PHP са имали малко по-различен метод (вероятно <5.4).

  • Методите на класа на сесията трябва върнете стойностите, посочени в ръководството за PHP. Отново, вероятно наследено от PHP преди 5.4, но две ръководства, които прочетох, гласят, че class->open връща реда за четене, докато PHP ръчното състояние че трябва да върне true или false само.

  • Това е причината за първоначалния ми проблем :Използвах персонализирани имена на сесии (всъщност идентификаторите като имена на сесии и идентификаторите на сесии са едно и също нещо! ) според тази много добра публикация в StackOverflow и това генерираше име на сесия, което беше дълго 128 знака. Тъй като името на сесията е единственият ключ, който трябва да бъде разбит, за да се компрометира сесия и да се поеме с отвличане на сесия тогава по-дълго име/идентификатор е много добро нещо.

    • Но това предизвика проблем, защото MySQL безшумно изрязваше идентификатора на сесията до само 32 знака вместо 128, така че никога не успя да намери данните за сесията в базата данни. Това беше напълно тих проблем (може би поради моят клас за връзка с база данни, който не изпраща предупреждения за такива неща). Но това е този, за който трябва да внимавате. Ако имате проблеми с извличането на сесиите от база данни, първата проверка е дали пълната идентификаторът на сесията може да бъде съхранен в предоставеното поле.

Така че като изключим всичко това, има и някои допълнителни подробности за добавяне:

Страницата с ръководство на PHP (свързана по-горе) показва неподходяща купчина редове за обект на клас:

Докато работи също толкова добре, ако поставите това в конструктора на класа:

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

Това означава, че за да започнете сесия на вашата изходна страница, всичко, от което се нуждаете, е:

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

//Bang session has been setup and started and works

За справка пълният комуникационен клас Session е както следва, това работи с PHP 5.6 (и вероятно 7, но все още не е тестван на 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();
    }

}

Употреба:Както е показано точно над текста на кода на класа.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. mysql:групирайте по идентификатор, получете най-висок приоритет за всеки идентификатор

  2. Индексиране на булеви полета

  3. Някой откривал ли е, че REGEX \b не работи в MYSQL?

  4. Как мога да избера само първото отделно съвпадение от поле в MySQL?

  5. Инсталирайте уеб сървър в Windows XP с Apache2, PHP5 и MySQL4 – част 4