Неожиданное исключение 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
это продолжается SPLObjectstorage
session_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).
Так как дальше?
Пришло время начать изолировать свою проблему и создать из нее самостоятельный, воспроизводимый пример. Это необходимо для дальнейшего изучения вопроса, как вы видите его. Это важно по двум причинам:
- Если это ошибка в PHP, о ней следует сообщить, что регрессионный тест написан и добавлен в PHP Q&A, а затем исправлена ошибка (если еще не исправлена).
- Если вы ищете обходной путь, воспроизведение исходной проблемы необходимо для быстрого и простого создания обходного пути.
Я выполнил несколько тестов для обхода, но пока я не могу воспроизвести вашу проблему, поэтому я не могу предложить, как обойти эту проблему, поскольку у меня ее здесь нет.
Объекты с именем 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, чтобы увидеть, если это все еще происходит.