编程

PHP 8.3: unserialize(): E_NOTICE 错误升级为 E_WARNING

1226 2023-07-27 11:51:00

PHP 提供了 serializeunserialize() 函数用以将 PHP 值(字符串、整型值、对象、NULL、数组、枚举等)系列化为字符串表示,及从字符串中重建 PHP 值。

$data = ['apple', 'banana', 'orange'];
$serialized = serialize($data);
// "a:3:{i:0;s:5:"apple";i:1;s:6:"banana";i:2;s:6:"orange";}"

$restoredData = unserialize($serialized);
// ['apple', 'banana', 'orange']

PHP 8.3 之前,传入无效的字符串到 unserialize() 函数在特定情况下(比如语法错误)将发出 PHP 通知(E_NOTICE)。自 PHP 8.3 起,将变成发出警告(E_WARNING)。此外,serialize() 函数的特定错误条件也改成了 发送 E_WARNING 警告。

unserialize("invalid-string");
- PHP Notice:  unserialize(): Error at offset 0 of 14 bytes
+ PHP Warning:  unserialize(): Error at offset 0 of 14 bytes

理想情况下,未能对给定字符串的反序列化应该是一个硬失败,并且应该抛出异常。然而,为了保持向后兼容性并简化升级路径,PHP 8.3中的错误级别有所增加,未来可能会升级它以抛出异常。

错误条件不一致
并非所有 unserialize() 失败都会发送 E_NOTICE 错误。比如,反系列化超过最大深度限制(PHP7.4起,通过 INI 设置 unserialize_max_depth 项进行配置)的字符串已经发送 E_WARNING警告。这种情况将不作调整并继续发出 E_WARNING 错误。

受影响的错误条件

从 PHP 8.3 起,以下三个先前发出 E_NOTICE 的错误条件已更改为发出E_WARNING

  • 传递的字符串中存在语法错误(有时是由不正确的序列化处理程序引起的)
unserialize('invalid string');
- PHP Notice:  unserialize(): Error at offset 0 of 12 bytes
+ PHP Warning:  unserialize(): Error at offset 0 of 12 bytes
  • 使用 __unserialize 魔术方法的自定义反系列化处理程序失败;例如 __unserialize() 方法没有返回任何值
class Test {
    public function __unserialize(array $data) { } // Does not return anything
}
- PHP Notice: unserialize(): Unexpected end of serialized data
+ PHP Warning: unserialize(): Unexpected end of serialized data
  • __sleep() 魔术方法返回同一变量两次,导致名称冲突
class Test {
  public $foo = 'test';
  public  function __sleep() {
    return array("foo", "foo"); // Same value returned twice
  }
}

serialize(new Test());
- PHP Notice: serialize(): "foo" is returned from __sleep() multiple times
+ PHP Warning: serialize(): "foo" is returned from __sleep() multiple times

向后兼容性影响

在 PHP 8.0 中,PHP 的默认错误报告级别更改为 E_ALL。除非在自定义 INI 文件中更改了 error_reporting值,否则除了严重性的更改之外,这不应该引入任何新的错误。

由于严重性的更改,以前忽略 E_NOTICE 错误的自定义错误处理程序可能会遇到新的 E_WARNING。强烈建议不要调整错误处理程序以忽略这些警告,而是评估警告并修正无效的错误条件。

一个值得注意的警告是,PHP 8.1 为 Enum 引入了新的序列化模式。序列化枚举采用 E:… 格式,并且在 PHP 8.1 之前的 PHP 版本中不能反序列化。