PHP 序列化学习笔记

什么是序列化

所有 php 里面的值都可以使用函数 serialize() 来返回一个包含字节流的字符串来表示。unserialize() 函数能够重新把字符串变回 php 原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字

也就是说,序列化serialize()会把一个对象转换成字符串,反序列化unserialize()会把字符串还原为一个对象

基础补充

类和对象

和其他语言类似,举个栗子

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
<?php
class demo
{
/*
* 对象被当成一个字符串的时候,__toString()会被调用
*/
function __toString()
{
return "__toString <br/>";
}
/*
* 当一个对象创建时被调用
*/
function __construct()
{
echo '__construct <br/>';
}
function test()
{
echo "function test <br/>";
}
}
$tmp = new demo;
$tmp->test();
echo $tmp;
?>
---
/*
__construct
function test
__toString
*/

上面的代码是正常情况下的调用。但是 php 中存在一些特殊的类成员在某些特定情况下会自动调用,称之为 magic 函数,magic 函数命名是以符号 __ 开头的。

魔术方法

几个常用魔术方法及触发条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
__wakeup() //使用 unserialize 时触发
__sleep() //使用 serialize 时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用 isset() 或 empty() 触发
__unset() //在不可访问的属性上使用 unset() 时触发
__toString() //把类当作字符串使用时触发,返回值需要为字符串
__invoke() //当脚本尝试将对象调用为函数时触发

序列化的格式

boolean

1
2
3
4
5
b:;
b:1; // True
b:0; // False

integer

1
2
3
4
5
i:;
i:1; // 1
i:-3; // -3

double

1
2
3
d:;
d:1.2345600000000001; // 1.23456(php弱类型所造成的四舍五入现象)

NULL

1
N; //NULL

string

1
2
3
s::"";
s"INSOMNIA"; // "INSOMNIA"

array

1
2
3
a::{key, value pairs};
a{s"key1";s"value1";s"value2";} // array("key1" => "value1", "key2" => "value2")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
a - array
b - boolean
d - double
i - integer
o - common object
r - reference
s - string
C - custom object
O - class
N - null
R - pointer reference
U - unicode string

PHP序列化

php 允许保存一个对象方便以后重用,这个过程被称为序列化。为什么要有序列化这种机制呢?在传递变量的过程中,有可能遇到变量值要跨脚本文件传递的过程。试想,如果为一个脚本中想要调用之前一个脚本的变量,但是前一个脚本已经执行完毕,所有的变量和内容释放掉了,我们要如何操作呢?难道要前一个脚本不断的循环,等待后面脚本调用?这肯定是不现实的。因为这样的操作,在小项目还好,在大项目里是极其浪费资源的。但是如果你将一个对象序列化,那么它就会变成一个字符串,等你需要的时候再通过反序列化转换回变了变量,在进行调用就好了,在这样就剩了资源的使用。

举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class demo
{
public $var;
function test()
{
echo $this->var . "<br/>";
}
}
$tmp = new demo;
$tmp->var = 'test';
echo serialize($tmp);
?>

结果

1
2
O:4:"demo":1:{s:3:"var";s:4:"test";}
# 类型:长度:"名字":类中变量的个数:{类型:长度:"名字";类型:长度:"值";......}

反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class demo
{
public $var;
function test()
{
echo $this->var . "<br/>";
}
}
$unser = "O:4:\"demo\":1:{s:3:\"var\";s:4:\"test\";}";
$tmp = unserialize($unser);
$tmp->test();
?>

反序列化返回的为序列化之前成功对变量赋值的对象,再用该对象可以直接调用类中的方法。

总结

大佬给的解释

序列化实质就是记录一个对象的类名,变量名,变量值这三个内容,反序列化会根据类名来实例化一个对象,并将对应的变量赋值。其中都不涉及类的函数。

参考文章

php 反序列漏洞初识

Powered by Hexo and Hexo-theme-hiker

Copyright © 2017 - 2018 Damit5's Blog All Rights Reserved.

UV : | PV :