序列化

在我看完这篇文章后,我终于懂了一些关于序列化的知识:

serialize() 函数会检查类中是否存在魔术方法比如 __sleep()。如果存在,则该方法会优先被调用,然后才执行序列化操作。

先看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Person{

var $name;

var $sex;

var $age;

function __construct($name = "", $sex = "", $age = "") {

$this->name = $name;

$this->sex = $sex;

$this->age = $age;

} function say() {

echo "我的名字叫:" . $this->name . " 性别:" . $this->sex . " 我的年龄是:" . $this->age . "<br>";

} //指定串行化时把返回的数组中$name和$age值串行化,忽略没在数组中的属性$sex

function __sleep() {

$arr = array("name", "age"); // 此时,属性$sex将被删除!!!

return($arr);

} //重新生成对象时,并重新赋值$age为40

function __wakeup() {

$this->age = 40;

}

}$p1 = new Person("张三", "男", 20);//把一个对象串行化,返一个字符串,调用了sleep()方法,忽略没在数组中的属性

$p1_string = serialize($p1);

echo $p1_string . "<br>"; //串行化的字符串我们通常不去解析

$p2 = unserialize($p1_string); //反串行化形成对象$p2重新赋值

//此时$age为40

$p2->say();?>

1.sleep()函数会在序列化时被调用,比如我现在有一个Person类,有name,sex和age三个变量,并且类中有 __sleep()函数

1
2
3
4
5
6
7
 function __sleep() 
{
$arr = array("name", "age"); // 此时,属性$sex将被删除!!!
return($arr);
}
$p1=new Person("张三", "男", 20);
echo serialize($p1);

会输出:

1
O:6:"Person":2:{s:4:"name";s:4:"张三";s:3:"age";i:20;}

他们分别表示:
变量类型:类名长度:类名:属性数量:{属性类型:属性名长度:属性名;属性值类型:属性值长度:属性值内容}

O(object)表示对象,因为$p1是类Person的一个对象

这里我们可以看到,创建类对象时,是有“男”这一属性值的,但由于调用了 __sleep(),这一属性消失了,并且序列化后的属性数量由3变为了2,而当我最后使用:

1
$p2->say();

由于$p2是反序列化后的$p1,而反序列化时又会调用wakeup函数,它修改了age的值

故返回值为:

1
我的名字叫:张三 性别: 我的年龄是:40

可以看到,性别这一变量为空并且age为40

当私有成员序列化时: \x00 + [私有成员所在类名] + \x00 [变量名]

总结一下调用顺序:

当对象使用 serialize() 函数进行序列化时,如果该对象中定义了 __sleep() 方法,PHP 将自动调用该方法,并在序列化之前执行其中的逻辑,而对于__wakeup()函数则是在反序列化后执行的。

在序列化时使用 __sleep() 方法进行预处理,而在反序列化时使用 __wakeup() 方法进行后处理,可以实现序列化和反序列化过程的完整控制。

__sleep() ------->serialize()
unserialize() -------> __wakeup()

序列化
http://example.com/2023/04/21/序列化/
作者
想有双手
发布于
2023年4月21日
许可协议