The list of backwards-incompatible changes for PHP 7.4 contain the following note:
Serialization
The o serialization format has been removed. As it is never produced by PHP, this may only break unserialization of manually crafted strings.
(Note that this is referring to a little-o
, not the big-O
format which is used for object serialization.)
It seems this was never generated by PHP’s serialize()
function, but the fact that this note exists implies that it was recognised by the unserialize()
function.
I’ve done a little test fiddle (3v4l.org) which shows this was not simply a synonym for big-O
, which would be one obvious possibility.
The fiddle exposes the changes in PHP by the differences in the error message that is output. In PHP >= 7.4 we get an error at position 0 (where the o
is encountered) whereas prior to 7.4 the error was reported at position 5 (where the data is located). This implies that o
was recognised but the data is in the wrong format, which ties in with what I’ve already deduced, above.
So, what was the o
serialization format, what did it deserialize to and why did PHP support such a feature if it didn’t actually generate it, itself?
Advertisement
Answer
Originally, PHP 3 used o:<num_fields>:{<fields>}
to serialize objects.
The following program works in PHP 4.0.0, which can be downloaded from php.net/releases/index.php (the Windows binary still works on Windows 10!):
<?php var_dump(unserialize('o:0:{}'));
Output:
X-Powered-By: PHP/4.0.0 Content-type: text/html object(stdClass)(0) { }
I was able to trace the original implementation of the object serialization format to this commit in 1999. See php3api_var_serialize.
Later that year, the object serialization format was changed to include the classname of the object being serialized in preparation for PHP 4.
This commit changed the serialization format to o:<classname_length>:"<class_name>":<num_fields>:{<fields>}
This made the output of PHP3 and PHP4 incompatible: PHP4 would not have been able to unserialize objects serialized with PHP3.
Therefore, another commit was added that changed o
to O
(lowercase o to uppercase O).
o
was still supported by unserialize()
to unserialize objects serialized with PHP3, but serialize()
did not use o
anymore.
In 2000, the serialization/unserialization code was refactored, resulting in the file we see today.
What probably happened is that the compatibility layer broke somewhere along the way, and no-one cared enough about PHP3 compatibility to fix it. The code in the beginning no longer works with any PHP version released in the last 15 years.