PHP对象序列化

PHP luoluolzb 浏览1298次

PHP中序列化数据使用的两个方法: serialize()unserialize()

对一个自定义对象序列化时,有4个魔术方法和预定义接口Serializable,共3个层级。

魔术方法 _sleep() 和 _wakeup()

public function sleep(): array;
public function wakeup();

serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。

__sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。

与之相反,unserialize() 会检查是否存在一个 wakeup() 方法。如果存在,则会先调用 wakeup 方法,预先准备对象需要的资源。

<?php

class Test implements Serializable
{
    public int $a;
    public int $b;
    public int $c;

    public function __construct(int $a, int $b, int $c)
    {
        echo "call ", __method__, '()', PHP_EOL;
        $this->a = $a;
        $this->b = $b;
        $this->c = $c;
    }

    public function __destruct()
    {
        echo "call ", __method__, '()', PHP_EOL;
    }

    // __sleep() 在 serialize() 之前执行
    // 返回一个需要被序列化的变量名称的数组
    public function __sleep()
    {
        echo "call ", __method__, '()', PHP_EOL;
        return ['a', 'b', 'c'];
    }

    // __wakeup() 在 unserialize() 之后执行
    public function __wakeup()
    {
        echo "call ", __method__, '()', PHP_EOL;
        var_dump($this);
    }
}

$t = new Test(11, 22, 33);

$str = serialize($t);
var_dump($str);

$t2 = unserialize($str);
var_dump($t2);

魔术方法 _serialize() 和 _unserialize()

PHP >= 7.4 可用

public function __serialize(): array;
public function __unserialize(array $data): void;

serialize() 函数会检查类中是否存在一个魔术方法 __serialize()。如果存在,该方法将在任何序列化之前优先执行。它必须以一个代表对象序列化形式的 键/值 成对的关联数组形式来返回,如果没有返回数组,将会抛出一个 TypeError 错误。

相反,unserialize()会检查是否存在具有unserialize()。如果存在,则此函数将传递从 serialize() 返回的存储数组。然后,它可以适当地从该数组恢复对象的属性。

<?php

class Test implements Serializable
{
    public int $a;
    public int $b;
    public int $c;

    public function __construct(int $a, int $b, int $c)
    {
        echo "call ", __method__, '()', PHP_EOL;
        $this->a = $a;
        $this->b = $b;
        $this->c = $c;
    }

    public function __destruct()
    {
        echo "call ", __method__, '()', PHP_EOL;
    }

    // 此函数存在时 __sleep(),serialize() 无效
    // 返回需要序列化的关联数组
    public function __serialize(): array
    {
        echo "call ", __method__, '()', PHP_EOL;
        return [
            'a' => $this->a,
            'b' => $this->b,
            'c' => $this->c,
        ];
    }

    // 此函数存在时 __wakeup(),unserialize() 无效
    // 接受一个关联数组
    public function __unserialize(array $data): void
    {
        echo "call ", __method__, '()', PHP_EOL;
        [
            'a' => $this->a,
            'b' => $this->b,
            'c' => $this->c,
        ] = $data;
        // var_dump($data);
    }
}

$t = new Test(11, 22, 33);

$str = serialize($t);
var_dump($str);

$t2 = unserialize($str);
var_dump($t2);

这种方法比使用 __sleep()__wakeup() 更灵活一点,能控制各变量的序列化和反序列化。

Serializable 接口

interface Serializable {
    public function serialize(): string;
    public function unserialize($serialized);
}

// 实现此接口的类将不再支持 __sleep() 和 __wakeup()。
// 不论何时,只要有实例需要被序列化,serialize 方法都将被调用。它将不会调用 __destruct() 或有其他影响,除非程序化地调用此方法。
// 当数据被反序列化时,类将被感知并且调用合适的 unserialize() 方法而不是调用 __construct()。如果需要执行标准的构造器,你应该在这个方法中进行处理。
<?php

class Test implements Serializable
{
    public int $a;
    public int $b;
    public int $c;

    public function __construct(int $a, int $b, int $c)
    {
        echo "call ", __method__, '()', PHP_EOL;
        $this->a = $a;
        $this->b = $b;
        $this->c = $c;
    }

    public function __destruct()
    {
        echo "call ", __method__, '()', PHP_EOL;
    }

    // 自定义序列化
    // 返回对象序列化之后的字符串
    // 它将不会调用 __destruct()
    public function serialize(): string
    {
        echo "call ", __method__, '()', PHP_EOL;
        // 也可以使用 serialize() 方法序列化数据
        return json_encode([$this->a, $this->b, $this->c]);
    }

    // 自定义反序列化
    // 接受序列化字符串
    // 不调用 __construct()
    public function unserialize($serialized)
    {
        echo "call ", __method__, '()', PHP_EOL;
        var_dump($serialized);
        [$this->a, $this->b, $this->c] = json_decode($serialized, true);
    }
}

$t = new Test(11, 22, 33);

$str = serialize($t);
var_dump($str);

$t2 = unserialize($str);
var_dump($t2);

使用这种方法最灵活,能够更底层的控制序列化,反序列化的细节。

总结

__sleep()__wakeup() 应该是PHP老版本设计的,虽然使用简单,但不够灵活,不能控制序列化细节。

__serialize()__unserialize() 更灵活一点,能控制各变量的序列化和反序列化。

Serializable 接口方式最灵活,能控制到序列化存储层次。

本文标签: PHP

版权声明:本文为作者原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://luoluolzb.cn/articles/104/php-object-serialization
您需要登录后才发布评论。 点此登录
用户评论 (0条)

暂无评论,赶紧发表一下你的看法吧。