PHP 的 "o" 序列化格式有什么用?

What was PHP's "o" serialization format for?

PHP 7.4 的向后不兼容更改列表包含 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.

(请注意,这里指的是 little-o,而不是用于对象序列化的 big-O 格式。)

这似乎从未由 PHP 的 serialize() 函数生成,但该注释存在的事实表明它 识别unserialize() 函数。

我做了 little test fiddle (3v4l.org) 这表明这不仅仅是 big-O 的同义词,它将是一种明显的可能性。

fiddle 通过输出的错误消息的差异公开了 PHP 中的更改。在 PHP >= 7.4 中,我们在位置 0(遇到 o 的地方)出现错误,而在 7.4 之前,错误报告在位置 5(数据所在的位置)。这意味着 o 已被识别,但数据格式错误,这与我在上面已经推断出的内容有关。

那么,什么是 o 序列化格式,它反序列化成什么,如果 PHP 本身并没有实际生成它,为什么要支持这样的功能?

最初,PHP3使用o:<num_fields>:{<fields>}序列化对象。

以下程序适用于 PHP 4.0.0,可从 php.net/releases/index.php 下载(Windows 二进制文件仍适用于 Windows 10!):

<?php

var_dump(unserialize('o:0:{}'));

输出:

X-Powered-By: PHP/4.0.0
Content-type: text/html

object(stdClass)(0) {
}

我能够追溯到 1999 年 this commit 对象序列化格式的原始实现。 参见 php3api_var_serialize

那年晚些时候,对象序列化格式为 changed 以包含正在序列化的对象的类名,为 PHP 4 做准备。 此提交将序列化格式更改为 o:<classname_length>:"<class_name>":<num_fields>:{<fields>}

这使得 PHP3 和 PHP4 的输出不兼容:PHP4 将无法反序列化使用 PHP3 序列化的对象。 因此,another commit was addedo 更改为 O(小写 o 变为大写 O)。 o 仍受 unserialize() 支持以反序列化使用 PHP3 序列化的对象,但 serialize() 不再使用 o

2000 年,serialization/unserialization 代码 was refactored, resulting in the file we see today

可能发生的事情是兼容性层在某个地方坏了,没有人足够关心 PHP3 兼容性来修复它。 开头的代码不再适用于过去 15 年发布的任何 PHP 版本。