反序列化之魔术方法的浅学习

前言

学习反序列化,那么就需要首先掌握魔术方法的使用,本篇文章将尽可能详细的讲解反序列化的魔术方法,博主只是小白,如有问题还请各位师傅多多指点!

类和对象

对对象和类进行简单讲解

面向对象(部分)

组成

类− 定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。
举个例子,全部的小狗就是一个类,代码实例如下

<?php
class phpClass { //phpClass是这个类的名字,类在定义时需要加上class
  var $var1; //成员变量,名字是var1
  var $var2 = "constant string"; //成员变量

  function myfunc ($arg1, $arg2) { //定义成员函数
     [..]//函数中的内容
  }
  [..]
}
?>
官方解析如下:
类使用 class 关键字后加上类名定义。

类名后的一对大括号({})内可以定义变量和方法。

类的变量使用 var 来声明, 变量也可以初始化值。

函数定义类似 PHP 函数的定义,但函数只能通过该类及其实例化的对象访问。

对象

对象− 是类的实例。
全部的小狗只是一个虚拟化的东西,当面前有一只狗时,我们就可以说这个狗是一个对象。
类和对象简单理解如下

$mercedes = new Car ();// Car()在这里是个类名,new +类名可以实例化一个对象,这里的$mercedes就是我们的对象名
$bmw = new Car ();
$audi = new Car ();
在这里插入图片描述

成员变量

成员变量− 定义在类内部的变量。该变量的值对外是不可见的,但是可以通过成员函数访问,在类被实例化为对象后,该变量即可称为对象的属性。

成员函数

成员函数− 定义在类的内部,可用于访问对象的数据。

完整过程

类的定义

此时大致知道了类的组成,我们来看一个实例,实例如下

<?php
class Site {
  /* 成员变量 */
  var $url;
  var $title;

  /* 成员函数 */
  function setUrl($par){ //设置url方法
     $this->url = $par;
  }

  function getUrl(){ //获取url方法
     echo $this->url . PHP_EOL;
  }

  function setTitle($par){ //设置标题方法
     $this->title = $par;
  }

  function getTitle(){ //获取标题方法
     echo $this->title . PHP_EOL;
  }
}
?>

首先解释一下变量 $this 代表自身的对象。PHP_EOL 为换行符。
然后这里这个->符号是“插入式解引用操作符”。换句话说,它用来调用由引用传递参数的子程序的方法,
可以理解为调用某种方法或变量

创建对象

前面我们定义了一个类,它的类名是Site,那这里我们想要把它变成对象,该怎么办呢,这时候就可以用new +类名来实例化对象

$php = new Site; //创建了对象php

调用成员方法

此时拥有了对象,那我们就可以使用该对象调用成员方法,该对象的成员方法只能操作该对象的成员变量

$php->setUrl("www.php.cn");//调用设置url方法并赋值
$php->setTitle('PHP中文网');//调用设置title方法并赋值

此时我们再用剩下的两个获取方法

$php->getUrl();
$php->getTitle();

此时就构造完了,我们把语句总合到一起

<?php
class Site {
  /* 成员变量 */
  var $url;
  var $title;

  /* 成员函数 */
  function setUrl($par){ //设置url方法
     $this->url = $par;
  }

  function getUrl(){ //获取url方法
     echo $this->url . PHP_EOL;
  }

  function setTitle($par){ //设置标题方法
     $this->title = $par;
  }

  function getTitle(){ //获取标题方法
     echo $this->title . PHP_EOL;
  }
}
$php = new Site; //创建了对象php
$php->setUrl("www.php.cn");//调用设置url方法并赋值
$php->setTitle('PHP中文网');//调用设置title方法并赋值
$php->getUrl();
$php->getTitle();
?>

此时输出一下
在这里插入图片描述

魔术方法

常见魔术方法有以下几种

__construct()   当一个对象创建时被调用,
__destruct()   当一个对象销毁时被调用,
__toString()   当一个对象被当作一个字符串被调用。
__wakeup()   使用unserialize时触发
__sleep()    使用serialize时触发
__destruct()    对象被销毁时触发
__call()    在对象上下文中调用不可访问的方法时触发
__callStatic()    在静态上下文中调用不可访问的方法时触发
__get()    用于从不可访问的属性读取数据
__set()    用于将数据写入不可访问的属性
__isset()    在不可访问的属性上调用isset()或empty()触发
__unset()     在不可访问的属性上使用unset()时触发
__toString()    把类当作字符串使用时触发,返回值需要为字符串
__invoke()   当脚本尝试将对象调用为函数时触发

这是整体的,但这样看似乎显得过于抽象,因此我们将其进行分类,依次进行举例讲解

__construct()与__destruct()

__construct : 在创建对象时候初始化对象,一般用于对变量赋初值。
__destruct : 和构造函数相反,当对象所在函数调用完毕后执行。
<?php
class bai{
    public $name;
    public $age;
    public function __construct()
    {
        echo "__construct()初始化<br>";
        $this->name;
        $this->age;
    }
    public function __destruct()
    {
        echo "__destruct()执行结束";
    }        
 }
 $a=new bai(); //创建一个对象命名为a
 /*
 赋值
 $a->name='quan9i'; //给对象a里的name赋值为quan9i
 $a->age=19; //给对象a里的age赋值为19
 echo(serialize($a)); //输出序列化后的$a
 */

在这里插入图片描述
但这是一种,__destruct还有一种利用方式,就是__destruct() 对象被销毁时触发,它的栗子如下

<?php
class bai{
    public $name;
    public $age;
    public function __construct($name,$age)
    {
        echo "__construct()初始化<br>";
        $this->name=$name;//将传入的第一个参数赋值给name变量
        $this->age=$age;
    }
    public function __destruct()
    {
        echo "__destruct()执行结束<br>";
    }        
 }
 //主动销毁
 $a=new bai('quan9i',19);
 unset ($a);//主动销毁对象,此时先触发destruct魔法函数再echo
 echo"777<br>";
 echo "------------分隔符----------------<br>";
 //自动销毁
 $b=new bai('quan9i',19);
 echo "123<br>";
 //此时先echo再触发destruct函数
?>

执行结果
在这里插入图片描述

__sleep()

__sleep()    serialize 之前被调用,可以指定要序列化的对象属性。
<?php
class bai{
    public $name;
    public $age;
    public function __construct($name,$age)
    {
        echo "__construct()初始化<br>";
        $this->name=$name;
        $this->age=$age;
    }
    public function __sleep()
    {
        echo "当使用serialize时触发此方法<br>";
        return array('name','age');
    }   
}
$a=new bai('quan9i',19);
echo serialize($a);
?>
在这里插入图片描述

__wakeup()

__wakeup()   反序列化恢复对象之前调用该方法

实例如下

<?php
class bai{
    public $name;
    public $age;
    public function __construct($name,$age)
    {
        echo "__construct()初始化<br>";
        $this->name=$name;
        $this->age=$age;
    }
    public function __wakeup()
    {
        echo "当使用unserialize时触发此方法<br>";
        $this->age=1000;//更改$age的值为1000
    }
}
$a=new bai('quan9i',19);
$b= serialize($a);
var_dump(unserialize($b));
?>
在这里插入图片描述

__toString()

__toString() :在对象当做字符串的时候会被调用。

实例如下

<?php

class Test
{
    public $a = 'This is a string';

    public function good(){
        echo $this->a.'<br />';
    }

    // 在对象当做字符串的时候会被调用
    public function __toString()
    {
        return '__toString方法被调用 <br>';
    }
}

$a = new Test();
$a->good();
echo $a;
?>
在这里插入图片描述

__invoke()

__invoke() :将对象当作函数来使用时执行此方法。

示例如下

<?php

class Test{
    public $data ="调用正常方法<br>";

    public function __invoke()
    {
        echo"调用__invoke()方法";
    }
}
$a= new test();
echo $a();
?>
在这里插入图片描述

__get

__get() 访问不存在的成员变量时调用的

实例如下

<?php

class Test {
    public $n=123;
    public function __get($name){
        echo '__get方法被调用,其中不存在变量'.$name.'<br>';
    }
}
$a = new Test();
echo $a->quan9i;//调用对象a中的变量quan9i,由于不存在quan9i这个变量,这时候就会调用__get魔术方法
?>
在这里插入图片描述

set

__set() :设置不存在的成员变量时调用的;
<?php

class xg{
    public $data = 100;
    protected $test=0;

    public function __set($name,$value){
        echo '__set 不存在成员变量 '.$name.'<br>';
        $this->test=$value;
    }

    public function show(){
        echo $this->test;
    }
}

$a = new xg();
$a->show(); //调用Get方法,这里就是输出test的值
echo '<br>';
$a->test= 777; // 给私有变量test赋值为777,但此时它是私有变量,就会调用__set,此时就会更改
$a->show();
echo '<br>';
$a->quan9i = 566;// 设置对象不存在的属性
$a->show();// 经过__set方法的设置值为566
?>
在这里插入图片描述
在这里插入图片描述

不理解的话可以进行调试,可以看见这里在调用私有变量test后走向了__set魔术方法,然后\$name就是我们调用的变量,\$value就是我们设置的值777
再看这个本来不存在的变量,它也是会调用这个__set魔术方法的,你可以在这里打断点,调试一下会发现此时这里设置了\$name=quan9i,$value=566
在这里插入图片描述

__call()

__call :当调用对象中不存在的方法会自动调用该方法

示例如下

<?php

class Test{
    public $data ="调用正常方法<br>";
    public function __call($name,$value){
        echo "__call被调用,Test类中不存在方法".$name.'<br>';
        var_dump($value);
    }
    public function show(){
        echo $this->data;
    }
}
$a= new test();
$a->show(); //调用正常方法
echo"--------------------分隔符----------------<br>";
$a->quan9i('quan9i',123); //调用一个不存在的方法,此时就调用了__call魔术方法

?>
在这里插入图片描述

isset()

__isset() : 检测对象的某个属性是否存在时执行此函数。

实例如下

<?php

class test{
    public $name;
    private $age;

    public function __construct($name, $age){
        $this->name = $name;
        $this->age = $age;
    }

    // __isset():当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
    public function __isset($content){
        echo "当使用isset()函数,自动调用<br>";
        return isset($this->$content);
    }
}

$a = new test("quan9i", 19);
// public 成员
echo ($a->name),"<br>";
// private 成员
echo isset($a->name);
echo "-----------分隔符--------------<br>";
echo isset($a->age);
?>

可以发现私有属性时会调用issset魔术方法(调用protect的属性也会调用)
在这里插入图片描述

__unset()

__unset() :在不可访问的属性上使用 unset () 时触发

实例如下

<?php

class test{
    public $name;
    private $age;

    public function __construct($name, $age){
        $this->name = $name;
        $this->age = $age;
    }


    public function __unset($content){
        echo "当使用unset()函数,自动调用<br>";
        echo "quan9i will be the best";
    }
}

$a = new test("quan9i", 19);
// public 成员
unset($a->name);
echo "<br>";
// private 成员
echo "-----------分隔符--------------<br>";
unset($a->age);
echo "<br>";
?>
在这里插入图片描述

可以发现当对一个公有属性进行unset的时候,会删除它,而私有变量则会调用unset函数

常用魔术函数汇总例子

<?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');
 }
 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
echo $s.'<br>';//sleep返回的信息此时被输出
echo unserialize($s);  //$s首先会被反序列化,会调用__wake()方法,被反序列化出来的对象又被当做字符串,就会调用_toString()方法。后面这个是一个对象,因此会执行一次__destruct方法
// 结束又会调用__destruct()方法,输出__destruct
?>

在这里插入图片描述
参考文章
原理加实践掌握反序列化
https://spaceman-911.gitee.io/2021/06/30/PHP反序列化/

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇
 
...