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方法打印所有的对象,可以查看对象内部的数据结构

 

posted @ 2021-07-09 10:45  Erichas  阅读(1877)  评论(0编辑  收藏  举报