2016-04-20 2 views
5

У меня есть два веб-сайта, Один TLS и один не так, как это для одного клиента, но мне нужны сайты, чтобы поделиться друг с другом (и только друг с другом) общие данные для пользователей, заказы, счета и т. Д.Как сохранить данные сеанса в базе данных, а не в файловой системе?

Это обычно делается с данными $_SESSION, но я, очевидно, не могу работать на других сайтах, и я обнаружил, что я могу хранить данные сеанса в базе данных (MySQL), а не в файловой системе.

Я выкопал и нашел This useful guide так же, как и этот старый, но useful guide. Я также нашел this guide, который имеет немного более современный MySQL.

Я написал класс интерфейса, но он работает только частично, он хранит данные сеанса в базе данных, но он не извлекает его. Я также использовал suggested method from the PHP manual.

Моя MySQL (копируемый из первых двух ссылок выше):

CREATE TABLE `sessions` (
    `id` varchar(32) COLLATE utf8_unicode_ci NOT NULL, 
    `access` int(10) NOT NULL, 
    `data` text COLLATE utf8_unicode_ci NOT NULL, 
    UNIQUE KEY `id` (`id`) 
) ENGINE=InnoDb DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

Пожалуйста, обратите внимание: Прежде чем я покажу вам мой интерфейс класса, пожалуйста, знать, что connetion Db использует мой собственный пользовательский интерфейс и отлично работает сам по себе.

$sessionDBconnectionUrl содержит сведения о соединении с базой данных сеанса, поскольку я занимаюсь сессиями отдельной базы данных с основного содержимого веб-сайта.

Мой интерфейс класса (как на основе всех приведенных выше ссылок)

<?php 
/*** 
* Created by PhpStorm. 
***/ 
class HafSessionHandler implements SessionHandler { 
    private $database = null; 

    public function __construct($sessionDBconnectionUrl){ 

     if(!empty($sessionDBconnectionUrl) && file_exists($_SERVER['DOCUMENT_ROOT'].$sessionDBconnectionUrl)) { 
      require_once "class.dataBase.php"; 
      // Instantiate new Database object 
      $this->database = new Database($sessionDBconnectionUrl); 
     } 
     else { 
      error_log("Session could not initialise class."); 
     } 

    } 

    /** 
    * Open 
    */ 
    public function open($savepath, $id){ 
     $openRow = $this->database->getSelect("SELECT `data` FROM sessions WHERE id = ? LIMIT 1",$id); 
    if($this->database->selectRowsFoundCounter() == 1){ 
     // Return True 
     return $openRow['data']; 
     } 
    else { 
     // Return False 
     return ' '; 
    } 
    /** 
    * 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 { 
      error_log("could not read session id ".$id); 
      return ''; 
     } 
    } 

    /** 
    * Write 
    */ 
    public function write($id, $data) 
    { 
     $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 = ? ', $id)) { 
      return TRUE; 
     } else { 

      return FALSE; 
     } 
    } 
    /** 
    * Close 
    */ 
    public function close(){ 
     // Close the database connection 
     // If successful 
     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; 

     // Set query 
     if ($this->database->noReturnQuery('DELETE * FROM sessions WHERE access < ?', $old)) { 
      return TRUE; 
     } else { 
      return FALSE; 
     } 
    } 

    public function __destruct() 
    { 
     $this->close(); 
    } 

} 

Моя тестовая страница (написана с нуля!)

<?php 
require "class.sessionHandler.inc.php"; 
$HSH = new HafSessionHandler("connection.session.dbxlink.php"); 
session_set_save_handler($HSH, TRUE); 
session_start(); 

print "<p>Hello this is an index page</p>"; 
$_SESSION['horses'] = "treesx3"; 
$_SESSION['tiespan'] = (int)$_SESSION['tiespan']+7; 

print "<p>There should be some session data in the database now. <a href='index3.php'>link</a></p>"; 
var_dump($_SESSION); 


exit; 

Выпуск:

Проверочные страницы, которые я запускаю, сохраняют данные в базе данных в порядке, но они, похоже, не извлекают данные,

У меня включено протоколирование ошибок и сообщения об ошибках PHP отсутствуют. Никаких критических ошибок MySQL не сообщается.

Почему это не работает?

+0

Я думаю, ваши худшие проблемы - это совместное использование файлов cookie между доменами (да, на стороне клиента) ... – SparK

+0

Почему? можете ли вы объяснить немного больше? – Martin

+0

На двух сайтах будет использоваться страница передачи, поэтому все посещения торгового сайта с основного сайта будут осуществляться через страницу передачи, которая устанавливает передачу данных сеанса, прежде чем передавать пользователя на свою целевую страницу. – Martin

ответ

12

я нашел в течение нескольких часов отладки, что упомянутые предметы, найденные на многочисленном Google ищут, а также значительное подмножество переполнению стека ответов, такие как here, here и here все предоставляют неверную или устаревшую информацию.

вещи, которые могут вызвать [критические] проблемы с сохранением данных сессии в базе данных:

  • Хотя все примеры в Интернете утверждают, что вы можете «заполнить» session_set_save_handler, ни один из них утверждают, что вы также должны установите register_shutdown_function('session_write_close') тоже (reference).

  • Несколько (более старых) руководств относятся к устаревшей структуре базы данных SQL и должны использоваться не. Структура базы данных, которая необходима для сохранения данных сеанса в базе данных: id/access/data. Вот и все. нет необходимости в дополнительных столбцах временной отметки, как я видел на нескольких «руководствах» и примерах.

    • Некоторые из старых руководств также устарели синтаксис MySQL, такие как DELETE * FROM ...
  • Класс [сделано в моем вопросе] должен implementSessionHandlerInterface. Я видел руководства (упомянутые выше), которые дают реализацию sessionHandler, который не является подходящим интерфейсом. Возможно, предыдущие версии PHP имели несколько иной метод (возможно, < 5.4).

  • Методы класса сеанса должен вернуть значения, указанные в руководстве по PHP. Опять же, вероятно, унаследованный от pre-5.4 PHP, но два руководства, которые я читал, утверждали, что class->open возвращает строку, которую следует читать, а PHP manual states - то, что ей нужно вернуть только true10 или false.

  • Это является причиной моего оригинала выпуска: Я использовал пользовательские имена сессии (! Собственно идентификаторов в качестве имен сеанса и идентификаторов сессии одно и то же) согласно this very good StackOverflow post и это производило имя сеанса, было 128 символов. Поскольку имя сеанса является единственным ключом, который необходимо взломать, чтобы скомпрометировать сеанс и взять на себя ответственность с session hijacking, тогда более длинное имя/идентификатор - очень хорошая вещь.

    • Но это вызвало проблему, потому что MySQL молча нарезку идентификатора сессии вниз только 32 символов вместо 128, поэтому он никогда не был в состоянии найти данные сессии в базе данных. Это была совершенно бесшумная проблема (возможно, из-за того, что мой класс подключения к базе данных не выдавал предупреждений о таких вещах). Но это тот, на который нужно следить. Если у вас возникли какие-либо проблемы с получением сеансов из базы данных, первая проверка заключается в том, что полный идентификатор сеанса можно сохранить в указанном поле.

Так со всем, что из пути есть некоторые дополнительные детали, чтобы добавить, а также:

Страница руководства PHP (сопряженная выше) показывает неподходящую кучу линий для объекта класса:

$handler = new MySessionHandler(); 
session_set_save_handler($handler, true); 
session_start(); 

в то время как она работает так же хорошо, если вы поместите это в конструктор класса:

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 

Для справки полный класс связи сеанса следующим образом, это работает с 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(); 
    } 

} 

Использование: Как показано чуть выше класса c ode текст.

+0

FYI: open() не использует параметр $ savepath – paul

+0

Когда первый сеанс добавлен в базу данных? Ваша функция 'open' должна действительно проверять только наличие правильного подключения к базе данных. Прямо сейчас, похоже, он проверяет, есть ли уже действующий сеанс в базе данных - если это был новый созданный сеанс, я сомневаюсь, что он когда-либо вернет «истину». – duckboy81

Смежные вопросы