PHP中序列化数据使用的两个方法: serialize()
和 unserialize()
。
对一个自定义对象序列化时,有4个魔术方法和预定义接口Serializable
,共3个层级。
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);
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()
更灵活一点,能控制各变量的序列化和反序列化。
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
暂无评论,赶紧发表一下你的看法吧。