PHP序列化和反序列化详解

Author Avatar
Mr-houzi 3月 26, 2019
  • 在其它设备中阅读本文章

最近,在看PHP官方文档时,发现官方文档中关于序列化的内容并不是非常详细,下面我就详细介绍一下PHP序列化和反序列化

serialize()和unserialize()函数

用法

  • serialize()函数可以把数字、字符串、数组以及对象进行序列化,并返回序列化后的字符串。
  • unserialize()函数可以把序列化的字符串你,还原原来的结构

实例

整型

序列化和反序列化整型变量

1
2
3
4
5
6
7
8
<?php
$data = 56;
$serialized_data = serialize($data);
echo "序列化:".$serialized_data . PHP_EOL;
$unserialized_data = unserialize($serialized_data);
echo "反序列化:".$unserialized_data . PHP_EOL;
?>

输出:

1
2
序列化:i:56;
反序列化:56

浮点型

序列化和反序列化浮点型变量

输入:56.012

1
2
3
4
5
6
7
8
<?php
$data = 56.012;
$serialized_data = serialize($data);
echo "序列化:".$serialized_data . PHP_EOL;
$unserialized_data = unserialize($serialized_data);
echo "反序列化:".$unserialized_data . PHP_EOL;
?>

输出:

1
2
序列化:d:56.012;
反序列化:56.012

输入:56.452

1
2
3
4
5
6
7
8
<?php
$data = 56.452;
$serialized_data = serialize($data);
echo "序列化:".$serialized_data . PHP_EOL;
$unserialized_data = unserialize($serialized_data);
echo "反序列化:".$unserialized_data . PHP_EOL;
?>

输出:

1
2
序列化:d:56.451999999999998;
反序列化:56.452

字符串

序列化和反序列化字符串变量

1
2
3
4
5
6
7
8
<?php
$data = "acdef";
$serialized_data = serialize($data);
echo "序列化:".$serialized_data . PHP_EOL;
$unserialized_data = unserialize($serialized_data);
echo "反序列化:".$unserialized_data . PHP_EOL;
?>

输出:

1
2
序列化:s:5:"acdef";
反序列化:acdef

数组

序列化和反序列化数组

1
2
3
4
5
6
7
8
9
<?php
$data = array("Thor","Iornman","Cap");
$serialized_data = serialize($data);
echo "序列化:".$serialized_data . PHP_EOL;
$unserialized_data = unserialize($serialized_data);
echo "反序列化:". PHP_EOL;
var_dump($unserialized_data);
?>

输出:

1
2
3
4
5
6
反序列化:
array (size=3)
0 => string 'Thor' (length=4)
1 => string 'Iornman' (length=7)
2 => string 'Cap' (length=3)
序列化:a:3:{i:0;s:4:"Thor";i:1;s:7:"Iornman";i:2;s:3:"Cap";}

对象

序列化和反序列化对象,serialize只会把对象成员变量序列化。

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
<?php
class Book{
private $num;
private $title;
private $writer;
function __construct($num,$title,$writer){
$this->num = $num;
$this->title = $title;
$this->writer = $writer;
}
private function test(){
echo "test";
}
}
$book = new Book(001,"算法","作者");
$serialized_data = serialize($book);
echo "序列化:".$serialized_data . PHP_EOL;
$unserialized_data = unserialize($serialized_data);
echo "反序列化:". PHP_EOL;
var_dump($unserialized_data);
?>

输出:

1
2
3
4
5
6
反序列化:
object(Book)[2]
private 'num' => int 1
private 'title' => string '算法' (length=6)
private 'writer' => string '作者' (length=6)
序列化:O:4:"Book":3:{s:9:"Booknum";i:1;s:11:"Booktitle";s:6:"算法";s:12:"Bookwriter";s:6:"作者";}

另:序列化对象时,不会保存常量的值。对于父类中的变量,则会保留。

当反序列化对象之前,未定义类时会正常输入,但反序列化得到的对象是__PHP_Incomplete_Class,并指定了未定义类的类名。

1
2
$serialized_data = 'O:4:"User":2:{s:8:"username";s:7:"Iornman";s:8:"nickname";s:6:"尼尼";}';
var_dump(unserialize($serialized_data));

输出:

1
2
3
4
object(__PHP_Incomplete_Class)[1]
public '__PHP_Incomplete_Class_Name' => string 'User' (length=4)
public 'username' => string 'Iornman' (length=7)
public 'nickname' => string '尼尼' (length=6)

其他

布尔类型和NUll序列化后的字符串格式如下:

  • Boolean : b:value;(1或0)
  • Null : N;

sleep()和wakeup()魔术方法

有时,我们并不想将对象中的所有成员变量都进行序列化,而是有选择的进行序列化,这时就要用到__sleep()魔术方法。

__sleep()用法

当时使用serialize()进行序列化对象时,如果存在__sleep()魔术方法,则会先调用此方法,然后才执行序列化。

__wakeup()用法

当时使用unserialize()进行反序列化对象时,如果存在__wakeup()魔术方法,则会先调用此方法,然后才执行反序列化。

实例

利用__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
<?php
class User{
public $username;
public $nickname;
private $password;
public function __construct($username, $nickname, $password)
{
$this->username = $username;
$this->nickname = $nickname;
$this->password = $password;
}
// 重载序列化调用的方法
public function __sleep()
{
// 返回需要序列化的变量名,过滤掉password变量
return array('username', 'nickname');
}
public function __wakeup(){
echo "执行__wakeup";
}
}
$user = new User('Iornman', '尼尼', '123456');
$serialized_data = serialize($user);
var_dump($serialized_data);
$unserialized_data = unserialize($serialized_data);
var_dump($unserialized_data);
?>

输出:

1
2
3
4
5
6
string 'O:4:"User":2:{s:8:"username";s:7:"Iornman";s:8:"nickname";s:6:"尼尼";}' (length=72)
执行__wakeup
object(User)[2]
public 'username' => string 'Iornman' (length=7)
public 'nickname' => string '尼尼' (length=6)
private 'password' => null

序列化接口Serializable

当使用__sleep()时,存在一个问题,__sleep()无法将父类的成员变量序列化,我们可以使用Serializable序列化接口来解决这个问题。

实例

让父类继承Serializable接口,并在父类中重写Serializable提供的serialize()unserialize()方法。当父类继承Serializable接口时,子类中的__sleep()__wakeup()将不再被调用。

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
48
49
50
<?php
class Common implements Serializable{
public $publc_val;
private $private_val = '123';
//重写Serializable接口提供的方法
public function serialize()
{
echo __METHOD__ . "\n";
return serialize($this->private_val);
}
//重写Serializable接口提供的方法
public function unserialize($serialized)
{
echo __METHOD__ . "\n";
}
}
class User extends Common{
public $username;
public $nickname;
private $password;
public function __construct($username, $nickname, $password)
{
$this->username = $username;
$this->nickname = $nickname;
$this->password = $password;
}
// 重载序列化调用的方法
public function __sleep()
{
// 返回需要序列化的变量名,过滤掉password变量
echo __METHOD__;
}
public function __wakeup(){
echo __METHOD__;
}
}
$user = new User('Iornman', '尼尼', '123456');
$serialized_data = serialize($user);
var_dump($serialized_data);
$unserialized_data = unserialize($serialized_data);
var_dump($unserialized_data);
?>

输出:

1
2
3
4
5
6
7
8
9
Common::serialize
object(User)[2]
public 'username' => null
public 'nickname' => null
private 'password' => null
public 'publc_val' => null
private 'private_val' (Common) => null
Common::unserialize
string 'C:4:"User":10:{s:3:"123";}' (length=26)