记录php反序列化学习(ctrl+s后再run)
一.面向对象的基本知识
程序设计开发有两种:面向对象和面向过程
面向过程:以整体事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步步的具体顺序中在一步步调用函数。(简单来说就是将复杂问题拆分成块,一块一块解决)
面向对象:以对象为中心的编程思想,把问题要解决的问题分解成各个对象,对象是一个由信息对信息出来的描述所组成的整体,对现实世界的抽象。(确实挺抽象的,我的理解是“对象”就是封装好具有许多属性的“个体”)
类是对象的抽象,而对象是类的具体实例。
类是想法,把类实例化(new),调用具体值后就变成对象。
1.类的内部构成:成员变量(属性)+成员函数(方法)
(1)成员变量(属性):定义在类的内部的变量,该变量值对外是不可见的,但可以通过成员函数访问,在类被实例化为对象后,该变量即可成为对象的属性。
(2)成员函数(方法):定义在类的内部可用于访问对象数据。
2.继承(继承性是子类自动共享父类数据结构的方法的机制,是类之间的一种关系)
(1)在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把一个已经存在的类所定义的内容作为自己的内容,并加入若干新内容。
(2)
- 父类:一个类被其他类继承,可将该类成为父类,或基类,超类
- 子类:一个类继承其他类称为子类,也可称为派生类
3.基本格式
class Class_Name{
成员变量声明
成员函数声明
}
定义了一个hero类,属性name赋值为'naruto',而sex没有赋值,定义了一个方法personality
可以传参$var1,方法内部调用该类中的name属性(注意:在类里面去调用类的成员属性的时候,要使用$this-> 的方法去调用,直接写$name会报错,而成员函数传参$var1是可以直接调用的)
然后将hero这个类进行实例化(new将这个类实例化成对象,然后赋值给一个变量方便调用,没有实例化直接运行什么都没有,不会输出东西)
调用对象$mingren中的name和sex,并将sex赋值为men
打印最后的对象$mingren
对象是不能直接用echo或print输出的,要么用var_dump要么用print_r
就算不去赋值也会调用这个对象的成员属性name和sex,而方法(函数)是不会显示出来的
四.类的常用访问权限修饰符介绍
- public:公共的,在类的内部、子类中或者类的外部都可以使用,不受限制。
- protected:受保护的,在类的内部、子类中可以使用,但不能在类的外部使用;
- private:私有的,只能在类的内部使用,在类的外部或者子类中都无法使用。
默认public
类的内部 | 类的子部 | 类的外部 | |
public | √ | √ | √ |
protected | √ | √ | × |
private | √ | × | × |
可以看到只能出现public的值
二.序列化和反序列化基础知识
(1)序列化的作用:是将对象的状态信息(属性)转换为可以存储或传输形式的过程。
对象-------序列化---------》字符串,在php中使用函数serialize()来将对象或者数组进行序列化,并返回一个包含字节流的字符串来表示。
注意:识别大小写;
这里虽然是双引号闭合,但可以清晰的知道字符串长度为6,所以哪怕在其中添加"也不会被截断,而是当做字符,这点很重要,字符串的长度是严格界定的。
a:3(有三个属性) i:number(索引) s:number(字符串长度)
注意:空格也算一个字符。
这里明明是7个,为什么序列化出来是9个。私有属性序列化时,在变量名前加了“%00类名%00” (实际上是空字符为了方便看我们将空字符写成%00的形式)
protected受保护属性序列化时,在变量名前加“%00*%00”
对象$a在实例化类'test2'时调用另一个类'test'实例化后的对象
注意:function后面至少要有一个空格再接魔术方法,并且是双下划线,魔术方法后续会介绍。
(2)反序列化:将序列化后的参数还原成实例化的对象
序列化将对象变成字符串,反序列化则是将字符串变成对象
反序列化生成的对象里的值,由反序列里的值(字符串$a)提供;与原有类预定义的值无关。
反序列化不改变类的成员方法;需要调用方法后才能触发。
对象本身是没有方法的,对象只是可以调用方法,就像人不是伞,但有伞,只要想用就可以拿起来用。
(3)反序列化漏洞
成因:反序列化过程中,unserialize()接受的值(字符串)可控。通过更改这个值(字符串),得到所需要的代码,即生成的对象的属性值。
三.魔术方法
一个预定义好的,在特定情况下自动触发的行为方法。
-
__construct 构造函数,在实例化一个对象的时候,首先会去自动执行的一个方法。(序列化和反序列化过程中不会触发)
功能:提前清理不必要的内容
参数:非必要
返回值:无
-
__destruct 析构函数,在对象的所有引用被删除或者当对象被显式销毁时执行的魔术方法。实例化对象结束后,代码运行完全会销毁,触发析构函数__destruct().
-
__sleep() 序列化serialize()函数会检查类中是否存在一个魔术方法__sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。
触发时机:序列化serialize()之前
功能:对象被序列化之前触发,返回需要被序列化储存的成员属性,删除不必要的成员属性。
参数:成员属性
返回值:需要被序列化储存的成员属性。
-
__wakeup() userialize()会检查是否存在一个__wakeup方法。如果存在,则会先调__wakeup()方法,预先准备对象需要的资源。预先准备对象资源,返回void,常用于反序列化操作中重新建立数据库连接或执行其他初始化操作。
__wakeup()在反序列化unserialize()之前
__destruct()在反序列化unserialize()之后
-
__tostring() 把对象被当成字符串调用时触发
-
__invoke() 把对象当成函数调用时触发
-
__call() 调用一个不存在的方法或不可访问的方法时触发
(如果是本类中,那就只能是不存在才不可访问,如果是在本类外不可访问还可能是没有访问权限)
参数:2个参数传参$arg1,$arg2($arg1是调用的不存在的方法的名称,$arg2是调用的不存在方法的参数)
返回值:调用的不存在的方法的名称和参数
-
__callstatic() 静态调用($test :: callxxx('a');)或调用成员常量时使用的方法不存在或不可访问
参数:2个参数传参$arg1,$arg2
返回值:调用的不存在的方法的名称和参数
-
__get() 调用成员属性不存在时或不可访问时读取数据时调用
参数:传参$arg1
返回值:不存在的成员属性的名称
举个例子-- 类里面只有一个属性var1 $test->var2;(调用了不存在的成员属性),就会把不存在的属性名称var2赋值给传参点$arg1.
-
__set() 给不存在的成员属性或不可访问的属性时赋值(写入数据)时触发
参数:传参$arg1,$arg2
返回值:不存在的成员属性的名称和赋的值
-
__isset() 对不可访问属性(private)或不存在使用isset()或empty()时,isset()会被调用
参数:传参$arg1
返回值:不存在的成员属性名称
-
__unset() 对不可访问属性使用unset()时
参数:传参$arg1
返回值:不存在的成员属性名称
-
__clone() 当使用clone关键字拷贝完成一个对象后,新对象会自动调用定义的魔术方法__clone.