3

I have database rows containing serialized objects.

I want to deserialize these, but the class has changed, some properties went private so the deserialization no longer works.

Is there a way to force deserialization to an array or a stdClass? (or anything that won't cause an error upon deserialization)

I want to avoid migrating data with a script. I'd rather have backward compatibility with the objects serialized in the old format.

3
  • 3
    Not to be sarcastic or anything, but: That's why you don't store code in a database... :) Commented Jun 27, 2013 at 8:21
  • @deceze Amen to that. NEVER AGAIN! -_- Commented Jun 27, 2013 at 8:30
  • This is why you don't store objects as serialized strings to a database. Commented Jun 27, 2013 at 8:33

2 Answers 2

12

Not really, or at least i would be pretty afraid to use something like this in production. However, unserialize will use the autoload system or the function name specified in the unserialize_callback_func ini setting. So with a little hacking you can make this work:

// 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'

You can use something like this to migrate your data to a little more handy format.

UPDATE:

I've consulted with my repressed memories on this (i've faced something like this once) and remembered an other solution:

So you have your SomeClass with a private property named $a

class SomeClass {
    private $a;
    public function getA(){
        return $this->a;
    }
}

And you have the serialized version of it:

$str = 'O:9:"SomeClass":1:{s:1:"a";s:1:"b";}';

When you unserialize it, and dump the result it will look like this, which is no good:

$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"
}
*/

Now, when an object get's unserialized, php will call it's __wakeup magic method. The data you need is there in the object, but not under the private property but a similarly named public one. You can't reach that with the $this->a since it will look for the wrong one, however the method get_object_vars() will return these properties and you can reassign them inside a __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();

I think it's needless to say that you should save yourself the further headache and convert your data to some dedicated data exchange format.

Sign up to request clarification or add additional context in comments.

2 Comments

Wow, that's clever and horrifying at the same time!
Thanks for the crazy ideas! This is awesome, I will use it to migrate softly to a more sensible data format than serialized objects :)
-1

Aside from manually munging the data in the database itself, which is always a risky proposition, I think your only option is to roll back the class code to an older version, extract the data, then re-store it in a more sensible way that can be more easily dealt with in future code revisions.

You do have the old classes in your SVN/GIT/CVS, right?

1 Comment

Yes yes I do, like I said I'd like to avoid a migration step if possible.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.