PHP枚举序列化/扩展性

2024-01-03 41

PHP 中的枚举类型和对象在序列化方面存在一些不同。当将枚举类型或枚举条目序列化时,会使用新的序列化代码 “E” 来指示枚举条目的名称。因为枚举条目是单例对象,反序列化操作可以将变量设置为现有的单例值。此外,枚举类型是 final 的,不能被扩展,这也是与普通对象的另一个不同之处。

一、序列化

枚举的序列化不同于对象。 尤其是它们有新的序列化代码: “E”,指示了 enum 条目名称。 然后反序列化动作能够设置变量为现有的单例值。 确保那样:

<?php
Suit::Hearts === unserialize(serialize(Suit::Hearts));

print serialize(Suit::Hearts);
// E:11:"Suit:Hearts";
?>

如果枚举和它的条目在反序列化时,无法匹配序列化的值, 会导致 warning 警告,并返回 false。

把纯粹枚举序列化为 JSON 将会导致错误。 把回退枚举序列化为 JSON 时,仅会用标量值的形式,以合适的类型表达。 可通过实现 JsonSerializable 来重载序列化行为。

对于 print_r(),输出的枚举条目略微不同于对象, 能减少迷惑。

<?php
enum Foo {
    case Bar;
}

enum Baz: int {
    case Beep = 5;
}

print_r(Foo::Bar);
print_r(Baz::Beep);

/* 产生

Foo Enum (
    [name] => Bar
)
Baz Enum:int {
    [name] => Beep
    [value] => 5
}
*/
?>

二、枚举扩展

PHP枚举类型是 final 的,不能被扩展。类在方法有契约:

<?php

class A {}
class B extends A {}

function foo(A $a) {}

function bar(B $b) {
    foo($b);
}
?>

这段代码类型安全,因为 B 遵循 A 的契约,并通过协变/逆变的逻辑,将会保留任何对方法的期望,除了异常。

枚举在其选项上有契约,而不是方法:

<?php
enum ErrorCode {
    case SOMETHING_BROKE;
}

function quux(ErrorCode $errorCode)
{
    // When written, this code appears to cover all cases
    match ($errorCode) {
        ErrorCode::SOMETHING_BROKE => true,
    }
}

?>

在函数 quux 中,match 语句可以进行静态分析,以涵盖 ErrorCode 中的所有情况。

但是想一下,如果允许扩展枚举:

<?php
// Thought experiment code where enums are not final.
// Note, this won't actually work in PHP.
enum MoreErrorCode extends ErrorCode {
    case PEBKAC;
}

function fot(MoreErrorCode $errorCode) {
    quux($errorCode);
}

fot(MoreErrorCode::PEBKAC);

?>

根据正常的继承规则,继承另一个类的类将通过类型检查。

问题在于 quux() 中的 match 语句不再涵盖所有情况。因为它不知道 MoreErrorCode::PEBKAC,所以匹配语句会抛出异常。

  • 广告合作

  • QQ群号:707632017

温馨提示:
1、本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。邮箱:2942802716#qq.com(#改为@)。 2、本站原创内容未经允许不得转裁,转载请注明出处“站长百科”和原文地址。
PHP枚举序列化/扩展性
下一篇: PHP错误处理