PHP反序列化
作者:互联网
一、前置知识
1、基本概念
类定义是以关键字 class 开头,后跟类的名称。类的主体是包含在一对花括号中,里面有类的属性与方法的定义。
类属性存在于数据段,类方法存在于代码段,对于一个类来说,类的方法不占用类的空间,占空间的只有类的属性。
2、语法
要创建一个类的实例,必须使用 new 关键字。当创建新对象时该对象总是被赋值,除非该对象定义了构造函数并且在出错时抛出了一个异常。类应在被实例化之前定义(某些情况下则必须这样)。
如果在 new 之后跟着的是一个包含有类名的字符串,则该类的一个实例被创建。如果该类属于一个名字空间,则必须使用其完整名称。
//创建一个实例
<?php
$instance = new SimpleClass();
// 也可以这样做:
$className = 'Kin';
$instance = new $className(); // Kin()
?>
二、序列化
如果一个脚本中想要调用之前一个脚本的变量,但是前一个脚本已经执行完毕,所有的变量和内容释放掉了,我们要如何操作呢?
serialize和unserialize就是解决这一问题的存在,serialize可以将变量转换为字符串,并且在转换中可以保存当前变量的值;而unserialize则可以将serialize生成的字符串变换回变量。
1、serialize()
所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。
可以这么理解:
在程序执行结束时,内存数据便会立即销毁,变量所储存的数据便是内存数据,而文件、数据库是“持久数据”,因此 PHP序列化就是将内存的变量数据“保存”到文件中的持久数据的过程。
举个栗子:
<?php
class User
{
public $age = 0;
public $name = '';
public function PrintData()
{
echo 'User '.$this->name.' is'.$this->age.' years old. <br />';
}
}
//创建一个对象
$user = new User();
// 设置数据
$user->age = 17;
$user->name = 'kinyoobi';
//输出数据
$user->PrintData();
//输出序列化之后的数据
echo serialize($user);
?>
结果:
O 表示对象类型,4 表示对象名长度为4,User 为类名,2 表示有两个参数;
{} 里面是参数的key和value:s 表示是string类型,3 表示长度,age 是key;i 表示是integer类型,17 是value。
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 |
2、unserialize()
反序列化就是将序列化格式化存储好的的字符还原成对象的过程。
注意:在解序列化一个对象前,这个对象的类必须在解序列化之前定义。否则会报错
举个栗子:
<?php
class User
{
public $age = 0;
public $name = '';
public function PrintData()
{
echo 'User '.$this->name.' is '.$this->age.' years old. <br />';
}
}
//重建对象
$user = unserialize('O:4:"User":2:{s:3:"age";i:17;s:4:"name";s:8:"kinyoobi";}');
$user->PrintData();
?>
结果:
3、魔术方法
PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法,在特定的情况下会被调用。
__construct | 构造函数 |
__destruct | 对象被销毁时触发,析构函数 |
__toString | 把类当作字符串使用时触发 |
__wakeup() | unserialize前调用,用于预先准备对象资源 |
__sleep() | serialize前调用 |
__call() | 在对象上下文中调用不可访问的方法时触发 |
__callStatic() | 在静态上下文中调用不可访问的方法时触发 |
__get() | 试图读取一个并不存在的属性的时候被调用 |
__set() | 用于将数据写入不可访问的属性 |
__isset() | 在不可访问的属性上调用isset()或empty()触发 |
__unset() | 在不可访问的属性上使用unset()时触发 |
__invoke() | 脚本尝试将对象调用为函数时触发 |
4、与序列化相关的两个魔术方法
1)__sleep()
serialize()函数会检查类中是否存在一个魔术方法 __sleep()。如果存在__sleep(),该魔术方法会先被调用,然后才执行序列化操作。
此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。
2)__wakeup()
unserialize()函数会检查是否存在一个 __wakeup()方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。
举个栗子:
<?php
class test{
public $varr1="abc";
public $varr2="123";
public function echoP(){
echo $this->varr1."<br>";
}
public function __construct(){
echo "__construct<br>";
}
public function __destruct(){
echo "__destruct<br>";
}
public function __toString(){
return "__toString<br>";
}
public function __sleep(){
echo "__sleep<br>";
//return array('varr1','varr2');
return array('varr1');
}
public function __wakeup(){
echo "__wakeup<br>";
}
}
$obj = new test(); //实例化对象,调用__construct()方法,输出__construct
$obj->echoP(); //调用echoP()方法,输出"abc"
echo $obj; //obj对象被当做字符串输出,调用__toString()方法,输出__toString
$s =serialize($obj); //obj对象被序列化,调用__sleep()方法,输出__sleep
var_dump($s);
echo "<br>";
$u = unserialize($s); //$s首先会被反序列化,会调用__wake()方法
var_dump($u);
echo "<br>";
// 脚本结束又会调用__destruct()方法,输出__destruct
?>
结果:
其中在序列化时,__sleep返回了varr1,达到了清理数据的目的;而在反序列化时,即类恢复成对象的时候,类本身的类属性是不能被销毁的,所以最后会打印出来varr1和varr2。
参考:
https://www.cnblogs.com/ichunqiu/p/10484832.html
https://www.php.cn/php-weizijiaocheng-454909.html
标签:__,PHP,对象,serialize,调用,sleep,序列化 来源: https://www.cnblogs.com/kinyoobi/p/15366958.html