深入浅出PHP中的魔术方法

0x01 引子

PHP的魔术方法在反序列化漏洞利用中是十分常见的,因此对于深入理解反序列化漏洞来说,理解PHP中常见的魔术方法也是至关重要的,所以在学习php.net文档中的魔术方法后,写篇文章来总结一下。

0x02 __wakeup()__sleep()

在PHP的序列化与反序列化中wakeup()和sleep()这两个魔术方法是必须到知道的。sleep函数是在序列化的时候会被自动调用,而wakeup函数则实在反序列化的时候被调用,对应的序列化的函数是serialize(),而反序列化函数是unserialize()。

那么如何理解自动调用呢?举个例子,在学习其他面向对象语言,例如java,c++的时候,一定听说过construct函数,一般叫做构造函数,构造函数就是当类被实例化的时候会首先执行构造函数,因此sleep和wakeup两个函数也是类似的,当php进行序列化或者反序列化的时候就会执行sleep或者wakeup函数了。

说完自动调用再来细说一下这两个魔术方法吧。
首先是__sleep()函数,
sleep函数的原型如下

意味着在写该函数的时候要注意需要有一个返回值,并且该返回值的类型是一个数组,而数组的内容是需要被序列化的变量名称,但是如果你没有这个返回值,PHP会报一个N_NOTICE的错误(就是一个警告),并把NULL给序列化了,举个例子看下图:

无返回值

结果

我们可以看到爆了一个警告,并且在第二行有个N;,这个就是就是NULL被序列化的结果。

有返回值
![]/images/深入浅出PHP中的魔术方法4.png)
结果

然后就是__wakeup()函数了
wakeup函数原型如下

好吧,没什么好说的,就是当执行unserialize()的时候会执行该魔术方法啦。

0x03 __toString()

__toString()魔术方法看名字也能懂,就是当对象被当作字符串来使用的时候会调用该魔术方法。什么叫做把对象当作字符串呢?就比如说echo $obj;这样的,我把一个对象echo出来,那么就会调用这个魔术方法啦。

在PHP5.2.0版本之前呢,只有直接使用echo和print才会生效,但是现在什么年代了,5.2.0几乎没有了,所以我们一般只要记得在任何字符串环境都可以的。

__toString()的原型如下:

这个也是有返回值的,不过返回值的类型是String类型的,因此我们在定义该函数的时候一定要记得返回一个字符串。

然后有一点需要记得那就是不能在toString方法中抛出错误,否则会报error的。
那么有的同学一定会问什么是抛出错误了,回答一下就是throw一个error了,博主亲自做了实验,还真的会报错。

0x04 __invoke()

invoke也是一个比较常用的函数,当对象是用调用函数的方式来使用的时候就会调用invoke魔术方法。那么什么是用调用函数的方式来使用呢?举个例子,一般我们写一个函数,比如function hello($string){….}这么写,然后想要调用这个函数的话就会hello(“hello”)这么来调用,因此呢,把对象使用函数的方式调用就是$obj($string)这样,括号里面是参数,当然也可以没有。obj是一个new好的对象,这样子就会自动调用invoke魔术方法啦。(PS.只有在PHP5.3.0版本以上才有的,当然现在基本都是这个版本以上了吧)

0x05 PHP“重载”系列

接下来要总结的是PHP的“重载”系列的魔术方法了。那么细心的小伙伴一定发现我在重载两个字上打了双引号。这是因为在PHP中,重载跟我们以前在java或者c++中见到的重载不太一样。在c++中,重载是指函数名字一样,但是参数和类型是不一样的,但是在PHP中,重载的意思是动态地“创建”类属性和方法。机智的小伙伴发现这里又有双引号了,好吧,这里的创建的意思包括读取和设置两种意思。

那么在PHP中什么时候使用重载呢?答案就是当调用当前环境下未定义或不可见的属性和方法的时候,会调用重载方法。那么现在一定有小伙伴会问,未定义我们可以理解,那什么是不可见的属性和方法呀,这里解释一下,不可见的属性和方法就比如一个变量是private的,那么我在别的类中就无法访问这个变量了,对于别的类中,这个private变量就是不可见吧,那么我访问了这个private变量,就会调用重载方法啦。

现在该正式开始介绍“重载”系列的魔术方法啦。
__get()__set()__isset()__unset()

当我们想要读取未定义或不可见属性的时候会自动调用get
当我们想要设置未定义或不可见属性的时候会自动调用
set
当我们对未定义或不可见属性使用isset()或者empty()时会自动调用isset
当我们对未定义或不可见属性使用unset()时会自动调用
unset

__call__callstatic

当我们调用一个不可访问的方法的时候会自动调用call
当我们调用一个不可访问的静态方法的时候会自动调用
callstatic
(注意访问静态方法的时候使用“类名::方法名”的方式哦~)

0x06 __clone()

当我们复制一个对象的时候,会用到clone这个关键字,当复制完成后,会自动调用clone魔术方法。

0x07 参考

http://php.net/

本文标题:深入浅出PHP中的魔术方法

文章作者:Pino-HD

发布时间:2018年06月10日 - 16:06

最后更新:2018年06月10日 - 17:06

原始链接:https://pino-hd.github.io/2018/06/10/深入浅出PHP中的魔术方法/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!