PHP反序列化基础(魔术方法)
本文最后更新于115 天前,其中的信息可能已经过时。

PHP反序列化基础

概念

序列化:使用serialize()可以将对象转换为字符串的形式

O:4:”Test”:3:{s:4:”flag”;b:1;s:4:”name”;s:8:”xiaoming”;s:3:”age”;i:20;}

反序列化:使用unserialize()函数将上述的序列化的字符串转回对象

代码案例

<?php
class Test {
    public $flag = "flag";
    public $name = "lang";
    public $age = 10;
}

$test1 = new Test();
$test1->flag = true;
$test1->name = "xiaoming";
$test1->age = 20;

echo serialize($test1);

?>

执行后获得序列化的数据

O:4:”Test”:3:{s:4:”flag”;b:1;s:4:”name”;s:8:”xiaoming”;s:3:”age”;i:20;}

  • O:表示对象类型,Object
  • 4:类名的长度(“Test” 是 4 个字符)
  • "Test":类名
  • 3:对象属性数量flag、name、age
  • 每个属性由s(字符串String)、b(布尔BOOL)或i(整数int)开头,后跟属性名和值
  • N:代表空值
  • a:数组 a:大小:{键序列段;值序列段;重复}

访问控制

public:公有类型、全局,类的内部和外部都可以访问

protected:私有类 只有当前类的内部可以访问

private:受保护的类 只有当前类或者父类可以访问

常见的魔术方法

__construct(): //当对象new的时候会自动调用

__destruct()://当对象被销毁时会被自动调用

__sleep(): //serialize()执行时被自动调用

__wakeup(): //unserialize()时会被自动调用

__invoke(): //当尝试以调用函数的方法调用一个对象时会被自动调用

__toString(): //把类当作字符串使用时触发

call(): //调用某个方法,若方法存在,则调用;若不存在,则会去调用call函数。

__callStatic(): //在静态上下文中调用不可访问的方法时触发

get(): //读取对象属性时,若存在,则返回属性值;若不存在,则会调用get函数

set(): //设置对象的属性时,若属性存在,则赋值;若不存在,则调用set函数。

__isset(): //在不可访问的属性上调用isset()或empty()触发

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

__set_state(),调用var_export()导出类时,此静态方法会被调用

__clone(),当对象复制完成时调用

__autoload(),尝试加载未定义的类

__debugInfo(),打印所需调试信息

常见反序列化样式

常见开始

__wakeup()一定会被调用,因为wakeup()是在使用unserialize()的时候被调用,作为初始化操作

__destruct()一定会被调用,因为destruct()是用于销毁状态的时候会自动调用,作为销毁操作

__toString()当对象被反序列化后又当作字符串使用的时候

常见中间部分

__toString()当对象被反序列化后又当作字符串使用的时候

__get()读取不可访问或者不存在属性的时候会被调用

__set()当不可访问或者不存在的属性赋值的时候会被调用

__isset()对于不可访问或者不存在的属性嗲用isset()或者empty()的时候会被调用

常见结尾

__call()调用了不可访问或者不存在的方法的时候被调用

常见魔术方法案例(参考小迪学习)

construct()、destruct()案例

<?php
class Test{
    public $name;
    public $age;
    public $string;
    //__construct:实例化对象时被调用.其作用是拿来初始化一些值。
    public function __construct($name, $age, $string){
        // 给类的属性赋值
        $this->name = $name;
        $this->age = $age;
        $this->string = $string;
        echo "__construct 初始化\n";
    }
    // __destruct:当删除一个对象或对象操作终止时被调用。其最主要的作用是拿来做垃圾回收机制。
    /*
     * 当对象销毁时会调用此方法
     * 一是用户主动销毁对象,二是当程序结束时由引擎自动销毁
     */
    function __destruct(){
       echo "__destruct 类执行完毕\n";
    }
}

$a = new Test('miren', '20', 'hello');

unset($a);

?>

当unset销毁$a的时候输出

construct 初始化 destruct 类执行完毕

代表在这个Test类里面construct()、destruct()都会执行一个进行初始化一个销毁后执行

当注释掉unset($a);后一样会输出

construct 初始化 destruct 类执行完毕

是因为无论如何都会注销

sleep()、wakeup()、__toString:案例:

<?php
class MyClass {  
    public $sex;
    public $name;
    public $age;

    public function __construct($name, $age, $sex) {
        $this->name = $name;
        $this->age = $age;
        $this->sex = $sex;
        echo "__construct被调用<br>";
    }

        public function __sleep() {
        echo "__sleep()被执行,序列化时调用<br>";
        return ['name', 'age', 'sex']; 
    }

    public function __wakeup() {  
        echo "__wakeup()被执行,反序列化时调用<br>";
    }

    public function __toString() {
        return "姓名:{$this->name},年龄:{$this->age},性别:{$this->sex}";
    }
}  

// 实例化对象
$a = new MyClass('miren', 20, '男');
// 序列化对象
$b = serialize($a);
echo "序列化后的字符串:" . $b . "<br><br>";
// 反序列化对象
$c = unserialize($b);
// 使用__toString方法直接echo对象
echo "反序列化后的对象信息:" . $c . "<br>";
?>

当serialize的时候自动执行了sleep() 方法、当serialize的时候自动执行了wakeup()方法

执行输出:

construct被调用
__sleep()被执行,序列化时调用
序列化后的字符串:O:7:"MyClass":3:{s:4:"name";s:5:"miren";s:3:"age";i:20;s:3:"sex";s:3:"男";}

__wakeup()被执行,反序列化时调用
反序列化后的对象信息:姓名:miren,年龄:20,性别:男

invoke()函数案例:

<?php

class Invokable {

 public function __invoke($x, $y) {

  echo "__invoke 被触发<br>";

  return $x + $y;

 }

}

$obj = new Invokable();

$result = $obj(3, 4);  //此处触发 __invoke

echo "结果=$result\n";

对象当函数去调用的时候会触发invoke函数执行

执行输出:

__invoke 被触发
结果=7

toString()函数案例:

<?php

class StrObj {

 public function __toString(): string {

  echo "__toString 被触发<br>";

  return "I am a string";

 }

}

$o = new StrObj();

echo $o;// 触发 __toString

当StrObj类被当作字符串进行使用的时候会触发

执行输出:

__toString 被触发
I am a string

call()函数案例:

<?php

class Demo {

 public function __call($name, $args) {

  echo "__call 被触发:method=$name args=" . json_encode($args, JSON_UNESCAPED_UNICODE) . "\n";

  return "from __call";

 }

}

$d = new Demo();

echo $d->noSuchMethod(1, "a") . "\n";  // 触发 __call

此处使用$d去实例化Demo类,然后让$d去调用了一个不存在的方法noSuchMethod触发了call函数

执行输出:

__call 被触发:method=noSuchMethod args=[1,"a"] from __call

callStatic()函数案例:

<?php
class Demo {
  public static function __callStatic($name, $args) {
    echo "__callStatic 被触发:method=$name args=" . json_encode($args, JSON_UNESCAPED_UNICODE) . "\n";
    return "from __callStatic";
  }
}

echo Demo::noSuchStatic("x") . "\n";    // 触发 __callStatic

同样的当调用不可访问或者不存在的静态方法的时候会直接触发__callStatic执行

执行输出:

__callStatic 被触发:method=noSuchStatic args=["x"] from __callStatic

get()函数案例:

<?php
class User {
  private $data = array(
    "name" => "张三",
  );
  

  public function __get($key) {
    echo "触发 __get:你正在读取属性 {$key}<br>";
    return $this->data[$key];
  }
}

$u = new User();

echo $u->name;  //触发 __get

当读一个不存在或不可访问的属性时候会自动触发get()因为此处的name是private类型的

执行输出:

触发 __get:你正在读取属性 name
张三

set()函数案例:

<?php

class Demo {

 private $data = [];

 public $normal = "init";

 public function __set($name, $value) {

  echo "__set 被触发:$name = " . var_export($value, true) . "<br>";

  $this->data[$name] = $value;

 }

}

$d = new Demo();

$d->token = "abc";

$d->normal = "cba";

echo "正常属性 normal 的值:" . $d->normal . "<br>";

当给私有的token赋值之后自动调用了set()函数但是当给公开的normal赋值的时候就正常输出

执行输出:

__set 被触发:token = 'abc'
正常属性 normal 的值:cba

isset()函数案例:

<?php

class Demo {

 private $data = ["token" => "abc"];

 public function __isset($name): bool {

  echo "__isset 被触发:name=$name";

  return $this->data[$name];

 }

}

$d = new Demo();

var_dump(isset($d->token)); // 触发 __isset

echo "<br>";

var_dump(empty($d->token)); // 也会触发

当对于不可访问的私有属性进行isset和empty的时候会触发isset函数

执行输出:

__isset 被触发:name=tokenbool(true)
__isset 被触发:name=tokenbool(true)

unset()函数案例:

<?php

class Demo {

 private $data = ["token" => "abc"];

 public function __unset($name) {

  echo "__unset 被触发:name=$name\n";

  unset($this->data[$name]);

 }

}

$d = new Demo();

unset($d->token);   // 触发 __unset

与上同理

执行输出:

__unset 被触发:name=token

set_state函数案例:

<?php

class Demo {

 public $a;

 public function __construct($a) {

  $this->a = $a;

 }

 public static function __set_state($props) {

  echo "__set_state 被触发<br>";

  $obj = new self(isset($props['a']) ? $props['a'] : 0);

  return $obj;

 }

}

$obj = new Demo(7);

$code = var_export($obj, true);

echo "var_export 输出:<pre>{$code}</pre>";

//还原

$restored = eval('return ' . $code . ';'); // 触发 __set_state

echo "还原后的对象:<pre>";

var_dump($restored);

echo "</pre>";

当执行var_export该静态方法set_state()会被调用,此处的var_export()是把对象导出成一段可执行的 PHP 代码,然后用eval去执行了code会还原对象

执行输出:

var_export 输出:

Demo::__set_state(array(
   'a' => 7,
))

__set_state 被触发
还原后的对象:

object(Demo)#2 (1) {
  ["a"]=>
  int(7)
}

clone()函数案例:

<?php
class Demo {
  public int $x = 1;

  public function __clone() {
    echo "__clone 被触发\n";
    $this->x++;
  }
}

$a = new Demo();
$b = clone $a;          // 触发 __clone
echo "a->x={$a->x}, b->x={$b->x}\n";

此处的clone函数主要用于PHP代码的对象复制,当使用clone去复制的时候会自动调用clone()函数

执行输出:

__clone 被触发 a->x=1, b->x=2

__autoload()函数案例:

<?php

error_reporting(0);

function __autoload($class) {

 echo "__autoload 被触发:class=$class\n";

}

new NotExistsClass(); // 触发 __autoload

当加载未定义的类的时候会自动调用

执行输出:

autoload 被触发:class=NotExistsClass

debugInfo()函数案例:

<?php

class Demo {

 public $secret = "top_secret";

 public function __debugInfo(): ?array {

  echo "__debugInfo 被触发<br>";

  return ["secret" => "abc", "note" => "cba"];

 }

}

$d = new Demo();

var_dump($d); // 触发 __debugInfo

当使用var_dump函数的时候会自动调用debugInfo()魔术方法

执行输出:

__debugInfo 被触发
object(Demo)#1 (2) { ["secret"]=> string(3) "abc" ["note"]=> string(3) "cba" }
文末附加内容
暂无评论

发送评论 编辑评论


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