Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Jak zapisać dane sesji PHP w bazie danych zamiast w systemie plików?

W ciągu kilku godzin debugowania odkryłem, że wymienione artykuły znalezione w wielu wyszukiwaniach Google, a także znaczny podzbiór odpowiedzi Stack Overflow, takich jak tutaj , tutaj i tutaj wszystkie podają nieprawidłowe lub nieaktualne informacje.

Rzeczy, które mogą powodować [krytyczne] problemy z zapisywaniem danych sesji w bazie danych:

  • Podczas gdy wszystkie przykłady online stwierdzają, że możesz „wypełnić” session_set_save_handler , żaden z nich nie stwierdza, że ​​należy również ustawić register_shutdown_function('session_write_close') też (odniesienie ).

  • Kilka (starszych) przewodników odnosi się do przestarzałej struktury bazy danych SQL i nie być użytym. Struktura bazy danych potrzebna do zapisania danych sesji w bazie danych to:id /access /data . Otóż ​​to. nie ma potrzeby stosowania różnych dodatkowych kolumn sygnatur czasowych, jak widziałem w kilku „przewodnikach” i przykładach.

    • Kilka starszych przewodników ma również przestarzałą składnię MySQL, taką jak DELETE * FROM ...
  • Klasa [wykonana w moim pytaniu] musi zaimplementować SessionHandlerInterface . Widziałem przewodniki (wspomniane powyżej), które podają implementację sessionHandler co nie jest odpowiednim interfejsem. Być może poprzednie wersje PHP miały nieco inną metodę (prawdopodobnie <5.4).

  • Metody klasy sesji muszą zwrócić wartości określone w podręczniku PHP. Ponownie, prawdopodobnie odziedziczone z PHP sprzed wersji 5.4, ale dwa przewodniki, które przeczytałem, stwierdzały, że class->open zwraca wiersz do odczytania, podczas gdy stany podręcznika PHP że musi zwrócić true lub false tylko.

  • To jest przyczyna mojego oryginalnego wydania :używałem niestandardowych nazw sesji (w rzeczywistości id jako nazwy sesji i identyfikator sesji to to samo! ) zgodnie z tym bardzo dobrym postem na StackOverflow a to generowało nazwę sesji o długości 128 znaków. Ponieważ nazwa sesji jest jedynym kluczem, który należy złamać, aby złamać sesję i przejąć kontrolę za pomocą przejmowanie sesji wtedy dłuższa nazwa/identyfikator to bardzo dobra rzecz.

    • Wywołało to jednak problem, ponieważ MySQL po cichu dzielił identyfikator sesji do zaledwie 32 znaków zamiast 128, więc nigdy nie był w stanie znaleźć danych sesji w bazie danych. To był całkowicie cichy problem (może z powodu mojej klasy połączenia z bazą danych, która nie wyświetla ostrzeżeń o takich rzeczach). Ale na to należy uważać. Jeśli masz jakiekolwiek problemy z pobieraniem sesji z bazy danych, najpierw sprawdź, czy pełny identyfikator sesji może być przechowywany w podanym polu.

Pomijając to wszystko, należy dodać również kilka dodatkowych szczegółów:

Strona podręcznika PHP (link powyżej) pokazuje nieodpowiedni stos wierszy dla obiektu klasy:

Podczas gdy działa to równie dobrze, jeśli umieścisz to w konstruktorze klasy:

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

To oznacza, że ​​aby następnie rozpocząć sesję na stronie wyjściowej, wystarczy:

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

//Bang session has been setup and started and works

Dla porównania, kompletna klasa komunikacji sesji jest następująca, działa to z PHP 5.6 (i prawdopodobnie 7, ale jeszcze nie testowane na 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();
    }

}

Sposób użycia:jak pokazano tuż nad tekstem kodu klasy.




  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 LOAD DATA INFILE:działa, ale nieprzewidywalny terminator linii

  2. jak dodać datę (DateTime.now) do bazy mySql w C#

  3. Format daty MySQL

  4. Procedura składowana MySQL a funkcja, której powinienem użyć, kiedy?

  5. Nakładające się zapytanie o rezerwację