Modernizing Serialized PHP Objects with class_alias()
Several weeks ago, a correspondent presented a legacy situation that I've never had to deal with. He was working his way through Modernizing Legacy Applications in PHP, and realized the codebase was storing serialized PHP objects in a database. He couldn't refactor the class names without seriously breaking the application.
I was carefully moving my classes to a PSR-0/4 structure when I found this. The application saves the output of
serialize($shoppingCart)
in a BLOB column, and unserializes it later to recreate the ShoppingCart object. The serialized object string looks like this:O:12:"ShoppingCart":17:{s:13:"\00Basket\00items";a:25:{...}; ...}
See how the class name is embedded in the serialized string? If I rename the ShopppingCart class to PSR-0/4 namespace, then the old class won't be found when the application tries to
unserialize()
the serialized representation. How can I begin refactoring this without breaking the whole application?
Before I was able to reply, my correspondent ended up changing the serialization strategy to use JSON, which was a rather large change. It ended up well, but it turns out there is a less intrusive solution: class_alias()
.
If you're in a serialized situation, and you need to change a class name, you can use a class_alias()
to point the old class name to the new one. (Call class_alias()
early in execution, probably before you register your autoloader.) You can then rename and move the class according to PSR-0/4 rules, and PHP will handle the rest for you.
For example, if you renamed ShoppingCart to Domain\Orders\Cart, you might do this:
class_alias('Domain\Orders\Cart', 'ShoppingCart');
Now when you call unserialize($shoppingCart)
to create an object, PHP will create it as an instance of Domain\Orders\Cart instead of ShoppingCart. Re-serialized representations of the recreated object will retain the new class name, not the old one: O:18:"Domain\Orders\Cart":...
.
As soon as there are no more serialized representations using the old class name, you can remove the class_alias()
call entirely.
Read the Reddit discussion about this post here.