Неожиданное исключение ValueException в session_start() php, не выполняющее сериализацию SPLObjectStorage

Зачем UnexpectedValueException брошен в session_start()?

У меня есть объект, который имеет свойство SPLObjectstorage, Этот объект назначен для сессии, как

$_SESSION['foo'] = $barObject;

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

Пример данных сеанса

self|O:4:"User":8:{s:5:"�*�id";N;s:7:"�*�nick";N;s:13:"�*�reputation";i:1;s:11:"�*�password";N;s:8:"�*�email";N;s:7:"�*�crud";O:10:"CRUDobject":2:{s:13:"�*�fieldCache";a:0:{}s:13:"�*�dependency";r:1;}s:7:"�*�auth";N;s:11:"�*�roleList";C:11:"RoleStorage":23:{x:i:1;N;,r:13;;m:a:0:{}}}

Rolestorage это продолжается SPLObjectstoragesession_decode() на приведенной выше строке также возвращает false есть идеи?

удаление roleList Атрибут заставляет его сериализовать правильно.

Если я отдельно делаю

$sr = serialize($roles); // $roles is RoleStorage object
var_dump($sr);
var_dump(unserialize($sr));

Это печатает string 'C:11:"RoleStorage":22:{x:i:1;N;,r:3;;m:a:0:{}}' (length=46) и затем терпит неудачу с тем же сообщением во время десериализации. Я понятия не имею, почему это происходит.

Примечание: при прикреплении объекта к RoleStorage Я использовал сам объект в качестве данных. Означает, что хранится как ссылка. Я не знаю как (если) serialize() внутренне обрабатывает это.

3 ответа

Решение

Понятия не имею, почему это происходит

В вашей версии PHP и с вашим конкретным сценарием невозможно сериализовать объект на основе SPLObjectStorage если вы сами не позаботитесь о сериализации. Если вы видите эту часть вашей сериализованной строки:

C:11:"RoleStorage":23:{x:i:1;N;,r:13;;m:a:0:{}}

Это представляет RoleStorage объект. Большой C в начале означает:

C - Объект, реализующий сериализуемый интерфейс

Таким образом, сам объект отвечает здесь за сериализацию и десериализацию. Обычно вы можете ожидать, что это работает, однако не все программное обеспечение без ошибок.

В вашем случае похоже, что PHP допустил ошибку. Внутренний формат здесь:

x:i:1;N;,r:13;;m:a:0:{} 
      ^^^^^^^

И проблема в выделенной позиции, для этого требуется сериализованный объект, а не NULL. И это не запятая со ссылкой (r:13 здесь), но с нулевым значением (N) работать.

Таким образом, выглядит как всплеск, инициируемый ссылками на какой-то более ранний объект (позаботьтесь о том, чтобы эти ссылки не совпадали с ссылками / псевдонимами переменных в пользовательском PHP).

Так как дальше?

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

  1. Если это ошибка в PHP, о ней следует сообщить, что регрессионный тест написан и добавлен в PHP Q&A, а затем исправлена ​​ошибка (если еще не исправлена).
  2. Если вы ищете обходной путь, воспроизведение исходной проблемы необходимо для быстрого и простого создания обходного пути.

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

Объекты с именем RoleStorage поднимает пару флагов для меня. Часто этот объект содержит своего рода ресурс или ссылку на встроенный объект PHP. Ресурсы не могут быть сериализованы, и некоторые встроенные типы PHP не могут быть сериализованы. Рассмотрим реализацию магии __sleep а также __wakeup методы в этих случаях.
Скажи, что у тебя есть PDO ссылка где-то в RoleStorage объект, тогда эти магические свойства могут выглядеть примерно так:

public function __sleep()
{
    $this->pdo->commit();//commit && close
    $this->pdo = array($dsn, $user, $pwd, array(
                                              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
                                          )
    );
    return serialize($this);
}
public function __wakeup()
{
    $this->pdo = new PDO($this->pdo[0], $this->pdo[1], $this->pdo[2], $this->pdo[3]);
}

Но так как вы говорите RoleStorage объекты дитя SPLObjectStorageвам лучше переопределить SPLObjectStorageРеализация Serializable интерфейс:

__Sleep() не может возвращать имена частных свойств в родительских классах. Это приведет к ошибке уровня E_NOTICE. Вместо этого вы можете использовать интерфейс Serializable.

Я бы предложил объявить итерацию по всем свойствам в дочернем классе serialize метод и сохранить эти данные в массиве. Возвратите этот сериализованный массив и отмените сериализацию этой строки обратно в unserialize метод, переназначая каждое свойство в цикле.
Если SPLObjectStorage имеет частные свойства, вы можете получить к ним так:

class RoleStorage extends SPLObjectStorage
      implements Serializable
{
    public function serialize()
    {
        return serialize((array) $this);
    }
    public function unserialize($string)
    {
        $array = unserialize($string);
        foreach($array as $property => $value)
        {
            $property = explode("\0", $property);//private & protected properties
            $this->{end($property)} = $value;
        }
    }
}

Подробнее о explode("\0",$property);, обратитесь к руководству или проверьте этот вопрос

Недавно была закрыта ошибка, связанная с проблемой, подобной этой. В зависимости от версии php, на которой вы работаете, это может повлиять на вас. Уязвимая версия - 5.3.15.

Выдержка:

[2012-07-27 16:08 UTC] j точка henge-ernst в interexa точка de

Проблема в том, что десериализация ArrayIterator (а также, возможно, ArrayObject или других классов SPL) не может разыменовать ссылки на объекты.

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

Другие вопросы по тегам