PHP反序列化
1.什么是php序列号和反序列化
在开发的过程中常常遇到需要把对象或者数组进行序列号存储,反序列化输出的情况。特别是当需要把数组存储到mysql数据库中时,我们时常需要将数组进行序列化操作。
php序列化(serialize):是将变量转换为可保存或传输的字符串的过程
php反序列化(unserialize):就是在适当的时候把这个字符串再转化成原来的变量使用
这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性。
常见的php序列化和反序列化方式主要有:serialize,unserialize;json_encode,json_decode。
2.php类与属性
简单写一段php代码,与Java里面的类、对象类似
运行结果:
3.魔术方法
php中有着魔术方法,以两个__下划线开头的方法称为魔术方法。在触发某个事件后,魔术方法会自动调用。
方法名 | 作用 |
__construct | 构造函数,在创建对象时候初始化对象,一般用于对变量赋初值 |
__destruct | 析构函数,和构造函数相反,在对象不再被使用时(将所有该对象的引用设为null)或者程序退出时自动调用 |
__toString | 当一个对象被当作一个字符串被调用,把类当作字符串使用时触发,返回值需要为字符串,例如echo打印出对象就会调用此方法 |
__wakeup() | 使用unserialize时触发,反序列化恢复对象之前调用该方法 |
__sleep() | 使用serialize时触发 ,在对象被序列化前自动调用,该函数需要返回以类成员变量名作为元素的数组(该数组里的元素会影响类成员变量是否被序列化。只有出现在该数组元素里的类成员变量才会被序列化) |
__destruct() | 对象被销毁时触发 |
__call() | 在对象中调用不可访问的方法时触发,即当调用对象中不存在的方法会自动调用该方法 |
__callStatic() | 在静态上下文中调用不可访问的方法时触发 |
__get() | 读取不可访问的属性的值时会被调用(不可访问包括私有属性,或者没有初始化的属性) |
__set() | 在给不可访问属性赋值时,即在调用私有属性的时候会自动执行 |
__isset() | 当对不可访问属性调用isset()或empty()时触发 |
__unset() | 当对不可访问属性调用unset()时触发 |
__invoke() | 当脚本尝试将对象调用为函数时触发 |
通过触发事件而调用不通的魔术方法
<?php class animal { private $name = 'Nolan'; public function sleep(){ echo "<hr>"; echo $this->name . " is sleeping...\n"; } public function __wakeup(){ echo "<hr>"; echo "调用了__wakeup()方法\n"; } public function __construct(){ echo "<hr>"; echo "调用了__construct()方法\n"; } public function __destruct(){ echo "<hr>"; echo "调用了__destruct()方法\n"; } public function __toString(){ echo "<hr>"; echo "调用了__toString()方法\n"; } public function __set($key, $value){ echo "<hr>"; echo "调用了__set()方法\n"; } public function __get($key) { echo "<hr>"; echo "调用了__get()方法\n"; } } $ji = new animal();
//初始化对象,调用__construct $ji->name = 1;
//对不可访问的属性赋值,调用__set echo $ji->name;
//调用不可访问的属性,调用__get $ji->sleep();
//这里只是为了方便看出调用了sleep,使用serialize序列化之前会自动调用__sleep $ser_ji = serialize($ji); print_r($ser_ji); print_r(unserialize($ser_ji))
//在使用unserialize反序列化时调用__wakeup方法
?>
就会出现如下结果,调用两次__destruct方法是因为在animal类的一个对象被序列号后的字符串又被反序列化时调用
在程序结束时又会调用。
序列化例子
<?php class object{ public $team = 'Nolan123'; private $team_name = 'power'; protected $team_group = 'biubiu'; function test(){ $this->$team_members = 'hard work!'; } } $object = new object(); echo serialize($object); ?>
运行结果
对象类型:对象长度:“对象名称”:类里面的变量个数:{变量类型:长度:“名称”;类型:长度:值;.......}
O是指一个对象,6是object的长度,3是有三个属性,{}里面是属性的内容; ss是team的类型,4是team长度,以此类推。
注意类里面的方法不会参加序列化。
需要注意的是变量受到不同修饰符(public,private,protected)修饰进行序列化时,序列化后变量的长度和名称会发生变化。
- 使用public修饰进行序列化后,变量$team的长度为4,正常输出。
- 使用private修饰进行序列化后,会在变量$team_name前面加上类的名称,在这里是object,并且长度会比正常大小多2个字节,也就是9+6+2=17。
- 使用protected修饰进行序列化后,会在变量$team_group前面加上*,并且长度会比正常大小多3个字节,也就是10+3=13。
通过对比发现,在受保护的成员前都多了两个字节,受保护的成员在序列化时规则:
1. 受Private修饰的私有成员,序列化时: \x00 + [私有成员所在类名] + \x00 [变量名]
2. 受Protected修饰的成员,序列化时:\x00 + * + \x00 + [变量名]
其中,"\x00"代表ASCII为0的值,即空字节," * " 必不可少。
序列化格式中字母的意思:
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
反序列化
使用序列化后的字符串,用反序列化函数unserialize进行反序列化
<?php class object{ public $team = 'Nolan123'; private $team_name = 'power'; protected $team_group = 'biubiu'; function test(){ echo $this->team." hard work!<br>"; } } $object = new object(); $ser = serialize($object); echo "$ser<br>"; $ser = unserialize($ser); $ser->test(); var_dump($ser); ?>
调用test方法,用var_dump方法打印所有的对象,可以查看对象内部的数据结构