当前位置: 首页 > article >正文

[Web 安全] PHP 反序列化漏洞 —— PHP 面向对象基础知识

关注这个专栏的其他相关笔记:[Web 安全] 反序列化漏洞 - 学习笔记-CSDN博客

因为后面介绍的 PHP 反序列化漏洞其实与 PHP 对象是密不可分的,所以这边笔者就简单介绍一下 PHP 中面向对象编程的基础知识。这里笔者是假定读者已经对 PHP 有基础了解了,且本地已经安装了 PHP 环境的情况。

0x01:PHP 面向对象编程 — 基础入门

面向对象(Object-Oriented,简称 OO)是一种编程思想和方法,它将程序中的数据和操作数据的方法封装在一起,形成 ”对象“,并通过对象之间的交互和信息传递来完成程序的功能。面向对象编程强调数据的封装、继承、多态和动态绑定等特性,使得程序具有更好的扩展性、可维护性和可重用性。

以上是 ”面向对象” 的概念简介,如果读者有点懵也不要紧,在后面的实操中相信你能 Get 到那个点的,然后笔者这里只是做个简介,详细学习可以参考下面这个链接:

PHP 面向对象 | 菜鸟教程PHP 面向对象 面向对象(Object-Oriented,简称 OO)是一种编程思想和方法,它将程序中的数据和操作数据的方法封装在一起,形成“对象”,并通过对象之间的交互和消息传递来完成程序的功能。面向对象编程强调数据的封装、继承、多态和动态绑定等特性,使得程序具有更好的可扩展性、可维护性和可重用性。 在面向对象的程序设计(英语:Object-oriented programming,缩写:OOP)中,对象是一个由信息及对信息进行处..https://www.runoob.com/php/php-oop.html

0x02:PHP 面向对象编程 — 知识速览

0x0201:PHP 面向对象编程 — 类的定义

在 PHP 中定义类需要注意以下几点:

  • 类使用 class 关键字后加上类名进行定义。

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

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

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

如下就是一个 PHP 类的示例:

 // className 是类名
 class className {
     // 这个是类的成员变量
    var $var1 = "This Is a Class"; 
     
     // 这个是类的成员函数
     function exampleFunction() {
         echo "This Is ExampleFunction !!\n";
 ​
         // 可以在类的成员函数中调用类的成员变量,但是得通过 $this 进行调用
         echo $this -> var1;
     }
 }

0x0202:PHP 面向对象编程 — 实例化类

类创建好后,我们就要使用它,使用类就需要对类进行 “实例化”,“实例化” 可以通过 new 关键字来进行实现,比如下面这个例子,我们实例化了 className 类,并且调用了其内部的成员方法:

 <?php
 ​
 // className 是类名
 class className {
     // 这个是类的成员变量
    var $var1 = "This Is a Class"; 
     
     // 这个是类的成员函数
     function exampleFunction() {
         echo "This Is ExampleFunction !!\n";
 ​
         // 可以在类的成员函数中调用类的成员变量,但是得通过 $this 进行调用
         echo $this -> var1;
     }
 }
 ​
 $class = new className();   // 实例化 className 类
 $class ->exampleFunction(); // 调用 className 类中的 exampleFunction() 方法(调用成员变量方法是一样的)

0x0203:PHP 面向对象编程 — 构造函数

构造函数是一种特殊的方法。主要用来在创建对象时初始化对象,即为对象成员变量赋初始值。

PHP5 允许开发者在一个类中定义一个方法作为构造函数,语法格式如下:

 void __construct ([ mixed $args [, $...]] )

如下是一个示例,我们创建了一个 Animal 类,并利用构造函数在实例化每个对象(类)时赋予了它们自己的物种名称:

 <?php
 ​
 // 动物类
 class Animal
 {
     // 这个保存物种类别
     var $animalType;
 ​
     // 这个就是构造函数
     function __construct($animalType) {
         $this -> animalType = $animalType;
     }
 ​
     // 当用户调用此函数,会输出当前动物的类型
     function getAnimalType() {
         echo "我是一只" . $this -> animalType . "\n";
     }
 }
 ​
 $dog = new Animal("小狗");
 $dog -> getAnimalType();
 ​
 $cat = new Animal("小猫");
 $cat -> getAnimalType();

0x0204:PHP 面向对象编程 — 析构函数

析构函数(destructor)与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统就会自动执行该函数。

PHP 5 中引入了析构函数的概念,其基础语法如下:

 void __destruct (void)

如下就是一个示例,当对象调用结束后,系统会自动销毁该对象并自动执行析构函数:

 <?php
 ​
 // 动物类
 class Animal
 {
     // 这个保存物种类别
     var $animalType;
 ​
     // 这个就是构造函数
     function __construct($animalType) {
         $this -> animalType = $animalType;
         echo "你创造了一只" . $animalType . " !!!!\n";
     }
 ​
     // 当用户调用此函数,会输出当前动物的类型
     function getAnimalType() {
         echo "你好,我是一只" . $this -> animalType . "\n";
     }
 ​
     // 当实例化的对象结束其生命周期时就会执行我
     function __destruct(){
         echo "很遗憾," . $this -> animalType . "的生命周期结束了!!!"; // 有点悲惨是咋回事
     }
 }
 ​
 echo "=========== 创建一只小狗 ===========\n";
 $dog = new Animal("小狗");
 $dog -> getAnimalType();
 echo "=========== 很久很久以后 ===========\n";

0x0205:PHP 面向对象编程 — 类的继承

在 PHP 中可以通过关键字 extends 来继承一个类(但并不支持同时继承多个类),格式如下:

 class Child extends Parent {
     // 类内部的代码
 }

比如下面这个示例,我们创建了一个 Dog 类继承自 Animal 类(Dog 类可以调用 Animal 类中已有的方法),并且扩展了功能(Dog 类可以拥有自己独特的方法):

<?php

// 动物类
class Animal {
    // 这个保存物种类别
    var $animalType;

    // 这个就是构造函数
    function __construct($animalType) {
        $this -> animalType = $animalType;
        echo "你创造了一只" . $animalType . " !!!!\n";
    }

    // 当用户调用此函数,会输出当前动物的类型
    function getAnimalType() {
        echo "你好,我是一只" . $this -> animalType . "\n";
    }

    // 当实例化的对象结束其生命周期时就会执行我
    function __destruct() {
        echo "很遗憾," . $this -> animalType . "的生命周期结束了!!!"; // 有点悲惨是咋回事
    }
}

class Dog extends Animal{
    // 调用后输出狗叫声
    function shout() {
        echo "汪汪 !!!\n";
    }
}

echo "=========== 创建一只小狗 ===========\n";
$dog = new Dog("小狗");
$dog -> getAnimalType();
$dog -> shout();
echo "=========== 很久很久以后 ===========\n";

0x0206:PHP 面向对象编程 — 方法重写

如果从父类继承的方法不能满足子类的需求,子类可以对其进行改写,覆盖父类中的方法,这也被称为方法的重写。

比如下面这个例子,Animal 类中有一个 shout 方法,但是子类 Dog 觉得 Animal 中的 shout 不符合自己的意思,所以在自己的类中重写了该方法:

<?php

// 动物类
class Animal {
    function shout() {
        echo "动物会叫 !!! \n";
    }
}

class Dog extends Animal{
    // 调用后输出狗叫声
    function shout() {
        echo "汪汪 !!!\n";
    }
}

$animal = new Animal();
$animal -> shout();

$dog = new Dog();
$dog -> shout();

0x0207:PHP 面向对象编程 — 访问控制

PHP 对属性或方法的访问控制是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的:

  • public(公有): 共有的类成员可以在任何地方被访问。

  • protected(受保护): 受保护的类成员则可以被其自身以及其子类和父类访问。

  • private(私有): 私有的类成员只能被其定义所在的累访问。

类属性必须被定义为公有,受保护,私有之一。如果用 var 定义,则被视为公有。

类中的方法可以被定义为公有、私有或受保护。如果没有这些关键字,则该方法默认为公有。

下面这个示例演示的是在子类中可以访问父类中被 protected 修饰的变量,但是无法访问 private 修饰的变量(对于方法原理是一致的,笔者这里就不演示了):

<?php

// 动物类
class Animal {
    // 定义一个私有属性 animalType,该属性只能在 Animal 类中使用
    private $animalType;
    protected $animalName;

    function __construct($animalType, $animalName) {
        $this -> animalType = $animalType;
        $this -> animalName = $animalName;
    }
}

class Dog extends Animal{
    function getAnimalType() {
        echo $this -> animalType;
    }

    function getAnimalName() {
        echo $this -> animalName;
    }
}

$animal = new Dog("🐕", "旺财\n");
$animal -> getAnimalName();
$animal -> getAnimalType();

0x0208:PHP 面向对象编程 — 接口(interface)

使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。

接口是通过 interface 关键字定义的,就像定义一个标准类一样,但其中定义的所有方法都是空的。

接口中定义的所有方法都必须是公有的,这是接口的特性。

要实现一个接口,需要使用 implements 操作符。类中必须实现接口中定义的方法,否则就会被报错。一个类中可以实现多个接口,用逗号来分隔多个接口的名称:

<?php

// 声明一个 Animal 接口
interface Animal {
    public function setType($type); // 设置动物类型
    public function setAge($age);   // 设置动物年龄
}

// 实现动物接口
class Dog implements Animal {
    var $type; // 动物类型
    var $age;   // 动物年龄

    function __construct($type, $age){
        $this -> setType($type);    // 调用内部函数设置动物类型
        $this -> setAge($age);      // 调用内部函数设置动物年龄
   }

    // 实现接口方法,这个必须得有,不然报错
    public function setType($type) {
        $this -> type = $type;
    }

    // 实现接口方法,这个必须也得有,不然也给你报错
    public function setAge($age) {
        $this -> age = $age;
    }
}

$dog = new Dog("狗", 18);
echo $dog -> type . " " . $dog -> age; // 输出: 狗 18

0x0209:PHP 面向对象编程 — 抽象类(Abstract)

任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。

抽象类是无法被实例化的。

被定义为抽象的方法只是声明了其调用方式(参数),不能定义具体的功能实现。

继承一个抽象类的时,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中的一样(或者更为宽松)。例如,某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。

如下是一个例子,我们抽象了一个 Animal 类,我们假设每个动物都会叫,但叫声具体是啥我们不知道,所以我们抽象了一个 shout 方法。而继承自 Animal 类的 Dog 类和 Cat 类都要实现这个方法:

<?php

abstract class Animal {
    abstract public function shout(); // 动物的叫声

    // 抽象类中的普通方法
    public function whoami() {
        echo "我是一个动物!!\n";
    }
}

class Dog extends Animal {
    // Dog 类需要实现抽象类中的 shout 方法
    public function shout() {
        echo "旺旺 !! ";
    }
}

class Cat extends Animal {
    // Cat 类也需要实现抽象类中的 shout 方法
    public function shout() {
        echo "喵喵 !! ";
    }
}

$dog = new Dog();
$dog -> shout();
$dog -> whoami(); // 调用 Dog 父类的方法

$cat = new Cat();
$cat -> shout();
$cat -> whoami(); // 调用 Cat 父类的方法

此外,子类方法可以包含父类方法中不存在的可选参数(例子笔者就不写了,可以去开头介绍的那个文章那里学习)。

0x0210:PHP 面向对象编程 — 调用父类构造方法

当子类中有自己的构造方法时,PHP 不会自动调用父类的构造方法,要想执行父类的构造方法,就需要在子类的构造方法中添加下面这样语句(笔者懒,就不演示了):

parent::__construct()

http://www.kler.cn/a/563476.html

相关文章:

  • 【监督学习】ARIMA预测模型步骤及matlab实现
  • 12、k8s安全机制
  • 【网络编程】几个常用命令:ping / netstat / xargs / pidof / watch
  • Android 12系统源码_多屏幕(四)自由窗口模式
  • OA办公系统自动渗透测试过程
  • AI快速变现之路,AI家装与婚礼设计
  • 机器学习02
  • 计算机视觉|Mask2Former:开启实例分割新范式
  • 10.前端部分|首页index.html|题库all_questions.html|指定题目one_question.html(html)
  • 设计模式|结构型模式总结
  • Linux 命令大全完整版(09)
  • 华为昇腾910b服务器部署DeepSeek翻车现场
  • RFID:汽车智能化产线工艺加工的指挥棒
  • 在Ubuntu系统上部署Dify(开源大语言模型应用开发平台)
  • 前端px转为rem的自适应解决方案
  • [VMware]卸载VMware虚拟机和Linux系统ubuntu(自记录版)
  • 论文解读<CRAW4LLM: Efficient Web Crawling for LLM Pretraining>
  • 记一次pytorch训练loss异常的问题
  • 驱动开发系列39 - Linux Graphics 3D 绘制流程(二)- 设置渲染管线
  • 系统架构设计:软件测试需要掌握的常用方法