У меня есть строки базы данных, содержащие сериализованные объекты.
Я хочу их десериализовать, но класс изменился, некоторые свойства стали частными, поэтому десериализация перестала работать.
Есть ли способ заставить десериализацию массива или stdClass? (или что-либо, что не приведет к ошибке при десериализации)
Я хочу избежать переноса данных со сценария. Я предпочел бы иметь обратную совместимость с объектами, сериализованными в старом формате.
Не совсем, или, по крайней мере, я бы очень боялся использовать что-то подобное в производстве. Однако unserialize
будет использовать систему автозагрузки или имя функции, указанное в настройке unserialize_callback_func
ini. Поэтому с небольшим взломом вы можете сделать эту работу:
// this a serialized object with the class "SomeMissingClass" $str = 'O:16:"SomeMissingClass":1:{s:1:"a";s:1:"b";}'; ini_set('unserialize_callback_func', 'define_me'); // set your callback_function // unserialize will pass in the desired class name function define_me($classname) { // just create a class that has some nice accessors to it eval("class $classname extends ArrayObject {}"); } $object = unserialize($str); print $object['a']; // should print 'b'
Вы можете использовать что-то подобное, чтобы перенести свои данные в более удобный формат.
Я консультировался с моими репрессированными воспоминаниями об этом (однажды я столкнулся с чем-то подобным) и вспомнил другое решение:
Итак, у вас есть SomeClass
с private
собственностью с именем $a
class SomeClass { private $a; public function getA(){ return $this->a; } }
И у вас есть сериализованная версия:
$str = 'O:9:"SomeClass":1:{s:1:"a";s:1:"b";}';
Когда вы несериализуете его и сбрасываете результат, он будет выглядеть так, что нехорошо:
$a = unserialize($str); var_dump($a->getA()); // prints 'null' var_dump($a); /* prints: object(SomeClass)#1 (2) { ["a":"SomeClass":private]=> NULL ["a"]=> string(1) "b" } */
Теперь, когда объект get не является неэтериализованным, php будет называть его методом __wakeup
magic. Необходимые данные есть в объекте, но не под частной собственностью, но с аналогичным названием public. Вы не можете достичь этого с помощью $this->a
так как он будет искать неправильный, однако метод get_object_vars()
вернет эти свойства, и вы можете переназначить их внутри __wakeup()
:
class SomeClass { private $a; public function getA(){ return $this->a; } public function __wakeup(){ foreach (get_object_vars($this) as $k => $v) { $this->{$k} = $v; } } } $str = 'O:9:"SomeClass":1:{s:1:"a";s:1:"b";}'; $a = unserialize($str); print $a->getA();
Я думаю, нет необходимости говорить, что вы должны сохранить себе дальнейшую головную боль и преобразовать свои данные в какой-то специальный формат обмена данными.
Помимо ручной обработки данных в самой базе данных, которая всегда является рискованным предложением, я думаю, что ваш единственный вариант – откат кода класса до более старой версии, извлечение данных, а затем повторное сохранение его более разумным образом, можно более легко рассмотреть в будущих версиях кода.
У вас есть старые классы в SVN / GIT / CVS, правильно?