проблема с обработчиком сеанса базы данных

Привет, я пытаюсь понять, как работают сеансы и как работает session_set_handler(), поэтому я начал создавать класс для обработки сеансов, но столкнулся с некоторыми проблемами с функцией write(). Не могли бы вы помочь мне решить проблему?

<?php
class Session extends Model
{
    public
        $connectionName = 'sessions',
        $sessionsId,
        $sessionsData;

    public function __construct()
    {
        parent::__construct();
        session_set_save_handler(
            array($this, 'Open'),
            array($this, 'Close'),
            array($this, 'Read'),
            array($this, 'Write'),
            array($this, 'Destroy'),
            array($this, 'Gc'));
        session_start();
    }

    public function __destruct()
    {
        session_write_close();
    }

    public function Open()
    {
        return true;
    }

    public function Close()
    {
        return true;
    }

    public function Read($sessionsId)
    {
        $this->stmt = $this->prepare("SELECT * FROM `sessions` WHERE `id` = :sessionId");
        $this->stmt->bindParam(":sessionId", $sessionsId);
        $this->stmt->execute();
        return $this->stmt->fetchAll();
    }

    public function Write($sessionsId, $sessionsData)
    {
        $this->stmt = $this->prepare("REPLACE INTO `sessions`.`sessions` 
        (`id`, `access`, `data`) 
        VALUES 
        (:sessionId, NOW(), :sessionValue)");
        $this->stmt->bindParam(":sessionId", $sessionsId);
        $this->stmt->bindParam(":sessionValue", $sessionsData);
        $this->stmt->execute();
    }

    public function Destroy($sessionsId)
    {
        $this->stmt = $this->prepare("DELETE FROM `sessions`.`sessions` WHERE `id` = :sessionId");
        $this->stmt->bindParam(":sessionId", $sessionsId);
        $this->stmt->execute();
    }

    public function Gc()
    {
        $this->stmt = $this->prepare("DELETE FROM `sessions`.`sessions` WHERE `access` < NOW() - INTERVAL 1 HOUR");
        $this->stmt->execute();
    }
}

теперь приведенный выше код работает нормально, единственная проблема заключается в том, что когда я пишу сеанс в БД, я получаю следующую вставку:

дамп из mysql

CREATE TABLE IF NOT EXISTS `sessions` (
  `id` varchar(32) NOT NULL,
  `access` datetime NOT NULL,
  `data` text NOT NULL,
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `sessions` (`id`, `access`, `data`) VALUES
('lhig71coed8ur81n85iiovje37', '2011-07-25 15:37:57', '');

Как вы можете видеть, значение сеанса не вставлено, я получаю только идентификатор сеанса. на var_dump($sessionsData) я получаю:

"name|s:6:"Bogdan";"

и для var_dump($sessionsId) я получаю:

lhig71coed8ur81n85iiovje37

Не могли бы вы помочь? я не получаю никаких ошибок проверенные журналы настройки xampp отображают ошибки при сообщении об ошибках все, что я знал, даже в php.ini все еще нет ошибок, что-либо использовалось, даже попробуйте блоки catch. большое спасибо, что прочитали это и помогли мне.


person Bogdan    schedule 25.07.2011    source источник
comment
Можете ли вы подтвердить, что $sessionsData имеет значения? Попробуйте повторить его перед процессами.   -  person ariefbayu    schedule 25.07.2011
comment
Этот var_dump выглядит подозрительно. Это недопустимый вывод serialize() - есть ли какой-либо HTML/xml, хранящийся в массиве сеансов, который может быть скрыт представлением браузера? Что вы видите в исходном коде браузера?   -  person Marc B    schedule 25.07.2011
comment
@Marc B: var_dump выглядит очень хорошо для данных сеанса. serialize() не работает должным образом с данными сеанса, я думаю, вы немного смешиваете это, потому что он близок, но данные сеанса список имен переменных с их сериализованными значениями примерно так ({varname}|{serialized($varname)})*.   -  person hakre    schedule 25.07.2011
comment
serialize() не заботятся о данных сеанса. Это просто еще одна структура данных, в основном serialize($_SESSION). Действительный сериализованный массив сеансов будет как минимум начинаться с a:.... | не является допустимым разделителем.   -  person Marc B    schedule 25.07.2011
comment
Afaik, также suhosin будет выполнять некоторое шифрование, поэтому, если у вас это работает, вы даже получите там бессмысленную строку из-за этого шифрования. Конечно же, он не должен быть пустым.   -  person Wrikken    schedule 25.07.2011
comment
Вы уверены, что можете сериализовать сеансы, хотя для этого были предусмотрены session_decode() и session_decode, если моя память меня не обманывает.   -  person Bogdan    schedule 25.07.2011
comment
@Hakre: где вызывается session_encode?   -  person Marc B    schedule 25.07.2011


Ответы (1)


Я обнаружил недостаток в вашей реализации, который приводит к удалению любых данных сеанса:

return $this->stmt->fetchAll();

in

public function Read($sessionsId)

Эта функция должна возвращать строку, в вашем случае данные из поля data.

Но вы возвращаете массив (как это выглядит - не могу конкретно сказать, потому что фактическая база данных скрыта, PDO/MySQLi?).

Однако весьма вероятно, что десериализация данных сеанса (см. session_decode()) завершится ошибкой, поэтому данные сеанса станут пустыми, а затем снова сохранятся (как пустая строка).

Это должно быть причиной вашей проблемы. Исправьте это, вернув строковые данные из базы данных.


Кроме того, я подозреваю, что это какая-то проблема с кодировкой, но я не совсем уверен, что это ее вызывает. Но просто учтите следующее:

Данные сеанса представляют собой двоичные данные, а не текстовые. Вы можете изменить определение таблицы и доступ соответственно. В MySQL для этого есть BLOB Type и bindParam() может помочь спецификатор типа данных PDO::PARAM_LOB.

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

person hakre    schedule 25.07.2011
comment
@ hackre: изменил режим на тип BLBO, я пробовал, ваш совет не сработал, я получаю [BLOB - 0octețs] в этом поле. - person Bogdan; 25.07.2011
comment
@ молчание для эха: Идентификатор сеанса: lhig71coed8ur81n85iiovje37 Значение идентификатора сеанса: name|s:6:Bogdan; на var_dump string(18) name|s:6:Bogdan; string(26) lhig71coed8ur81n85iiovje37 на имя print_r|s:6:Богдан; lhig71coed8ur81n85iiovje37 все три являются функцией записи функции, поэтому я точно получаю данные до этого шага. в первом посте я съел несколько букв там извиняюсь был немного голоден но сейчас что-то съел. что касается исходного кода, я не вижу ничего плохого @ hakre, спасибо за этот документ, добавленный в закладки и все еще читаемый. - person Bogdan; 25.07.2011
comment
@Bogdan: Понятно, но я думаю, что вам все равно следует придерживаться BLOB вместо текста, чтобы хранить данные (когда это работает) как двоичные, а не текстовые. - person hakre; 25.07.2011
comment
@hrake прислушаюсь к твоему совету и воспользуюсь им :) - person Bogdan; 25.07.2011
comment
@Bogdan: Я, наверное, нашел виновника, см. отредактированный ответ. - person hakre; 25.07.2011
comment
@ hakre внес необходимые изменения и вставил новый источник: pastebin.com/v0Nt9sdq. вы были правы, теперь он работает нормально. я не знал, что после вызова $_SESSION['name'] = 'Bogdan'; будет писать и читать после этого, а затем писать и анализировать нулевые данные, чтобы записывать и не получать данные в mysql. Теперь я получаю [BLOB - 18 октетов] и на каждой странице, которую я проверяю, я получаю сеанс: D. Ответ был таким очевидным, и он был у меня перед глазами, я провел два дня, это был третий день, когда я пытался найти проблему и исправить ее несколько часов назад, я даже пытался с каплей, как ваш совет, и не работал. :) - person Bogdan; 25.07.2011
comment
Хорошо, тогда я перемещаю это редактирование над частью BLOB-объекта. Пожалуйста ;) - person hakre; 25.07.2011
comment
Я надеюсь, что кто-то еще, кто попытается изучить и понять поток данных для сеансов, найдет эту тему полезной для меня. Спасибо всем за вашу помощь. D Кстати, забыл ответить на ваш вопрос, который я использую, и PDO. Если кто-то, кто читает это или уже читал, получил какие-либо советы, я с радостью им воспользуюсь. - person Bogdan; 25.07.2011
comment
@Bogdan: Просто примите этот вопрос как ответ (см. здесь: meta.stackexchange.com/questions/5234/), чтобы другие могли видеть, что на ваш вопрос ответили. - person hakre; 25.07.2011