PHP面向对象编程
1.类的定义:
- 定义了一件事物的抽象特点
- 类的定义包含了数据形式以及对数据的操作
1.2.类的定义与调用方法:
类的定义:
1 2 3 4 5 6 7 8
| class 类名{ public $成员属性1=值; public $成员属性2=值2; public $成员属性3=值3; public fuction 成员方法(){ echo "hello world!" } }
|
举例:
1 2 3 4 5 6 7
| class Animal { public $name="cat"; public $name2="dog"; public function eat(){ echo "在吃饭"; } }
|
通过new关键字实例化,可以理解对象是一个灵魂,new实例化是给灵魂塑造肉身:
1 2 3 4 5 6 7 8
| class Animal { public $name="cat"; public $name2="dog"; public function eat(){ echo "在吃饭"; } } $animal=new Animal;
|
接下来我们要输出Animal中的第一个成员属性,即name1,可以使用 $实例化的变量->成员属性名来直接调用,不需要额外添加”$”符号
1 2 3 4 5 6 7 8 9
| class Animal { public $name="cat"; public $name2="dog"; public function eat(){ echo "在吃饭"; } } $animal=new Animal; echo $animal->name;
|
调用成员方法也是一样的道理:
1 2 3 4 5 6 7 8 9
| class Animal { public $name="cat"; public $name2="dog"; public function eat(){ echo "在吃饭"; } } $animal=new Animal; echo $animal->eat();
|
稍微复杂一点的拼接:
1 2 3 4 5 6 7 8 9
| class Animal { public $name="cat"; public $name2="dog"; public function eat($name){ echo $name."在吃饭"; } } $animal=new Animal; echo $animal->eat("小狗");
|
1.3.$this关键字的介绍
this关键字用于指向类自己本身的属性或者方法
比如:
1 2 3 4 5 6 7 8 9
| class Animal { public $name="cat"; public $name2="dog"; public function eat(){ echo $this->name."在吃饭"; } } $animal=new Animal; echo $animal->eat();
|
又:
1 2 3 4 5 6 7 8 9 10 11
| class Animal { public $name="cat"; public $name2="dog"; public function eat(){ echo $this->name."在吃饭"; } } $animal=new Animal; echo $animal->eat(); $animal->name="pig"; echo $animal->eat();
|
2.类的访问权限控制
1.public (共有) 公有的类成员可以在任何地方被访问
2.protected(受保护的)受保护的类成员则可以被其自身以及其子类和父类访问(咱家的)
1 2 3 4 5 6 7 8 9 10 11 12
| <?php class car { protected $car1 = "benchi"; public $car2 = "qirui"; } $car = new car(); echo $car->car1; ?> PHP Fatal error: Uncaught Error: Cannot access protected property car::$car1 in /tmp/sandbox.s0-s0;c473,c741/home/.code.tio:7 Stack trace:
thrown in /tmp/sandbox.s0-s0;c473,c741/home/.code.tio on line 7
|
可以看到调用触发保护机制,受保护的类无法被外部调用
3.private(私有) 私有的类成员则只能被其定义所在的类访问(自己的)
私有的
1 2 3 4 5 6 7 8
| <?php class car { private $car1 = "benchi"; public $car2 = "qirui"; } $car = new car(); echo $car->car1; ?>
|
private和protected如果想在外部调用私有类,可通过创建public 关键字声明的函数使用:
1 2 3 4 5 6 7 8 9 10 11 12
| <?php class car { private $car1 = "benchi"; public $car2 = "qirui"; public function name(){ return $this->car1; } } $car = new car(); echo $car->name(); ?>
|
3.常用的魔术方法
3.1 构造函数(__construct )
介绍:
- 构造函数是一种特殊的方法,在创建一个新对象的时候它会被自动调用
- 可用来初始化对象的属性或执行其它必要操作,没有返回值
例子:
1 2 3 4 5 6 7 8 9 10 11
| <?php class fruit{ public $f1="apple"; public $f2="orange"; public function __construct(){ echo "I LIKE". " ".$this->f1; } } $a=new fruit; ?>
|
例2:
1 2 3 4 5 6 7 8 9 10
| <?php class fruit{ public $name; public function __construct($name){ echo "I LIKE ".$name; } } $a=new fruit("apple"); ?>
|
可通过创建对象的时候赋值name,这种写法在CTF反序列化题目中非常常见
3.2析构函数(__destruct)
- 析构函数是一种特殊的方法,他在对象被销毁时自动调用;
- 它可以用来执行一些清理操作,例如释放资源或者关闭数据库连接
- 当对象不再被引用或者脚本执行结束的时候,析构函数会被自动调用
例1:
1 2 3 4 5 6 7 8 9
| <?php class obj { public function __destruct(){ echo "im deleted!"; } } $obj=new obj; unset($obj); ?>
|
使用unset触发__destruct ,输出”im deleted!”
例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php class obj { public function __destruct(){ echo "im deleted!"; } } $obj=new obj; unset($obj); if ($obj){ echo "im not delete"; } else{ echo "im deleted!"; } ?>
|
检索对象是否存在,如果存在返回 im not delete 否则输出两遍im deleted! 并报错对象不存在
4.php的static静态变量和self关键字
静态变量的特点是无需实例化即可调用,通过static对其进行声明,可以通过类::变量的方式调用,在类的内部无法使用this调用,需要使用self对其代替
例1:外部调用静态变量
1 2 3 4 5 6 7
| <?php class Animal{ public $a1="cat"; public static $a2="dog"; } echo Animal::$a2; ?>
|
通过对象::变量来调用,输出dog,通过代码可以观察到省略了实例化的那一步
例2:内部调用静态变量:
1 2 3 4 5 6 7 8 9 10 11
| <?php class Animal{ public $a1="cat"; public static $a2="dog"; public function say(){ echo self::$a2; } } $a=new Animal; $a->say(); ?>
|
输出dog
在外部也可以对静态变量进行修改:
1 2 3 4 5 6 7 8
| <?php class Animal{ public $a1="cat"; public static $a2="dog"; } Animal::$a2="cow"; echo Animal::$a2; ?>
|
对象::静态变量名=”值”;
4.1类常量
这个知识点和静态变量非常类似,区别就是类常量不能随意更改,静态变量可以更改,类常量使用const进行声明,类中self进行调用
例1:
1 2 3 4 5 6 7 8 9 10 11
| <?php class Animal{ public $a1="cat"; const a2="dog"; public function say(){ echo self::a2; } } $a=new Animal; $a->say(); ?>
|
常量不能加public等这些关键字,不加$符号
例2:外部调用
1 2 3 4 5 6 7 8 9
| <?php class Animal{ public $a1="cat"; const a2="dog"; } $obj=new Animal; echo $obj::a2;
?>
|
4.2静态方法
有了静态变量就有对应的静态方法,同样通过static关键字声明
1 2 3 4 5 6 7 8 9 10
| <?php class cat { public static $c1="狸猫"; public $c2="花猫"; public static function show(){ echo self::$c1; } } cat::show(); ?>
|
输出狸猫
4.3在类中调用非静态变量
1 2 3 4 5 6 7 8 9 10
| <?php class cat { public static $c1="狸猫"; public $c2="花猫"; public static function show(){ echo (new self)->c2; } } cat::show(); ?>
|
在类中通过 (new self) 将类本身实例化 再通过箭头指向非静态变量c2
5.类的继承
通过exends关键字继承父类的成员属性和方法,并且可以增删查改新的类
例1:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php class Animal { public $a1="dog"; private $a2="cat"; protected $a3="monkey"; }
class Animal2 extends Animal{}; $a1=new Animal; $a2=new Animal2; var_dump($a1); var_dump($a2); ?>
|
在这个例子中我们通过extends创建了一个类:Animal2 , 该类继承了父类Animal的所有属性,但是继承不一定可访问
上面的结果输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| object(Animal) ["a1"]=> string(3) "dog" ["a2":"Animal":private]=> string(3) "cat" ["a3":protected]=> string(6) "monkey" } object(Animal2) ["a1"]=> string(3) "dog" ["a2":"Animal":private]=> string(3) "cat" ["a3":protected]=> string(6) "monkey" }
|
我们可以观察到Animal2 该类继承了父类Animal的所有属性
访问:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php class Animal { public $a1="dog"; private $a2="cat"; protected $a3="monkey"; }
class Animal2 extends Animal{}; $an=new Animal; $an2=new Animal2; echo $an2->a1; echo $an2->a2; echo $an2->a3; ?>
|
输出dog,然后报错
因为a1的变量是public关键字声明的,可进行公共调用访问,a2是private私有的,只能在类中被调用,而protected只能在被继承的类中调用,不允许在外部被调用
那么,我们怎么能访问到protected 受保护的成员属性呢?
例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php class Animal { public $a1="dog"; private $a2="cat"; protected $a3="monkey"; }
class Animal2 extends Animal{ public function say () { echo $this->a3; } }; $an=new Animal; $an2=new Animal2; echo $an2->say(); ?>
|
在继承的类中,使用this指向a3即可调用
5.1魔术方法也可被继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php class Animal { protected $name; public function __construct($name){ $this->name=$name; } public function eat(){ echo $this->name."在吃饭"; } } class Animal2 extends Animal{ public function say() { echo "hello,".$this->name; } } $a=new Animal2("小猫"); $a->say(); echo " "; $a->eat();
|
先通过继承构造函数赋值,在将赋值后的变量带到方法中,在这个例子中我们知道了魔术方法也可以被继承
fina
6.类方法属性重写和final关键字
如果父类继承的方法或者属性不能满足子类的需求,可以对其进行改写。
final关键字:
先看一个类方法重写的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php class Animal { public function makeSound() { echo "Animal sound"; } }
class Dog extends Animal { public function makeSound() { echo "Bark"; } }
$dog = new Dog(); $dog->makeSound(); $animal=new Animal(); $animal->makeSound(); ?>
|
父类Animal中,存在方法makeSound ,子类Dog继承了Aniaml 的方法,在子类中可重新声明父类中的方法并进行改写,而父类的方法不受任何影响:上面的代码运行结果为:BarkAnimal sound
在开发中,有时父类方法是强制性的,不能被改写,需要在publIc声明中添加关键字final关键字来告诉 方法不能被改写
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php class Animal { final public function makeSound() { echo "Animal sound"; } }
class Dog extends Animal { public function makeSound() { echo "Bark"; } }
$dog = new Dog(); $dog->makeSound(); $animal=new Animal(); $animal->makeSound(); ?>
|
在代码中我们在父类的方法中添加了final关键字,这时子类继承父类方法时,会报错:
1
| PHP Fatal error: Cannot override final method Animal::makeSound() in /tmp/sandbox.s0-s0;c344,c899/home/.code.tio on line 13
|
注:final关键字不能加在成员属性前,只能用于成员方法
7.使用parent调用父类方法和构造函数
我们通过parent关键字调用父类的方法,但是前提是需要将parent::method 放在子类的方法中,具体示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php class Animal { public function method1 () { echo "hello world!"; } } class Dog extends Animal { public function mothod2(){ parent::method1(); } } $a=new Animal; $b=new Dog; $b->method1(); ?>
|
如上,我们在子类Dog嵌套了一个方法:method2() 我们要想使用父类的method1 需要在子类的方法中调用
7.1 使用parent 调用构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php class Animal { public $name; public function __construct($name){ $this->name=$name; echo "hello".$name; } } class cat extends Animal { public function __construct ($name) { parent::__construct($name); echo "你好".$name; } }
$cat = new cat("世界"); ?>
|
在这个例子中,我们依旧定义了两个类Animal和cat 父类使用了construct方法,在new 的时候对name进行了赋值,并输出hello 拼接name
子类中 也使用了construct方法,并继承了父类的成员属性$name ,接下来在子类构造方法的函数下面又调用了父类的构造函数,并输出你好拼接变量name
最终实例化子类的对象,赋值参数字符串”世界” 输出hello 世界你好世界
这块的逻辑可能不是很好理解,需多加练习
7.2静态延迟绑定:static
先看一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php class Animal { protected static $name = "动物"; public static function who() { echo self::$name; } } class Cat extends Animal { protected static $name = "猫"; } Cat::who(); ?>
|
输出动物而不是猫,因为self 永远指向类本身,在例子中代码编译时就把name给绑死了,无法动态绑定,为了解决这种问题我们需要加上static关键字 将其动态绑定
加了static之后:
1 2 3 4 5 6 7 8 9 10 11
| class Animal { protected static $name = "动物"; public static function who() { echo static::$name; } } class Cat extends Animal { protected static $name = "猫"; } Cat::who();
|
加了之后变量被覆写时也不会强制绑定原来的那个类
8.类的多态以及方法重载
什么是类的多态?
多态(Polymorphism)面向对象编程核心特性之一,指同一个接口(方法名)在不同类中具有不同实现。运行时根据对象实际类型调用对应方法 例如之前的例子:子类覆写父类的成员方法 但是多态不仅仅只有这些 ,在这里我们仅做一个简单的概念
重载的介绍
PHP中的重载是通过魔术方法动态处理未定义或不可访问的属性和方法,而非传统意义上的同名不同参数方法重载。
重载函数:
1.func_get_args();
2.func_num_args();
func_get_args() 用于获取参数,而func_num_args(); 则用于获取参数的数量
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php class Animal{ public function say(){ $a=func_get_args(); $b=func_num_args(); var_dump($a); var_dump($b); } } $n=new Animal; $n->say();
|
执行结果:
因为我们没有向say()传任何参数
如果是:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php class Animal{ public function say(){ $a=func_get_args(); $b=func_num_args(); var_dump($a); var_dump($b); } } $n=new Animal; $n->say(1,2,3);
|
则返回:
1 2 3 4 5 6 7 8 9
| array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) } int(3)
|
可以看到把每个参数以及参数的数量都给回显了
案例:根据不同的参数数量执行不同的事件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <?php class Animal{ public function say(){ $a=func_get_args(); $b=func_num_args(); var_dump($a); var_dump($b); if ($b==1){ echo "触发第一次事件";
} elseif ($b==2) { echo "触发第二次事件"; } elseif($b==3) { echo "触发第三次事件"; } else { echo "触发默认事件"; }
} } $n=new Animal; $n->say(1); ?>
|
9.interface接口规范
interface接口指的是一组方法的集合,不是类,不能被实例化
指定某些类必须实现某些方法,但是具体内容没有规定
只能使用public 声明
用于定义一些规范,使代码不容易出错
语法:
1 2 3 4 5
| interface 接口名称 { public func1(); public func2(); .... }
|
配合class类进行调用接口规范:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php interface A { public function func1(); public function func2(); public function func3(); }
class AImplementation implements A { public function func1() { echo 1; } public function func2() { echo 2; } public function func3() { echo 3; } } $a=new AImplementation(); $a->func1(); $a->func2(); $a->func3(); ?>
|
AImplementation 类的成员方法名称必须要完全符合interface 内定义的接口规范名称
interface内还可以用于定义静态方法,以及自定义函数中的参数名称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php interface A { public static function func1($name); public function func2(); public function func3(); }
class AImplementation implements A { public static function func1($name) { echo $name; } public function func2() { echo 2; } public function func3() { echo 3; } } $a=new AImplementation(); $a::func1("hello world"); $a->func2(); $a->func3(); ?>
|
也可以同时实现两个接口 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php interface A { public static function func1($name); } interface B { public function func2(); public function func3(); }
class AImplementation implements A,B { public static function func1($name) { echo $name; } public function func2() { echo 2; } public function func3() { echo 3; } } $a=new AImplementation(); $a::func1("hello world"); $a->func2(); $a->func3(); ?>
|
中间使用,将其隔开 通过implements 实现两个接口 这两个接口里面的规范都必须被符合
10.抽象类和抽象方法
- 抽象类是一种特殊的类,不能被实例化只能被继承
- 抽象类用于定义一组相关的方法,但是这些方法的具体实现由继承它的子类完成
- 子类继承抽象类后,必须实现抽象类的所有抽象方法
- 抽象类可以包含抽象方法和普通方法
抽象类和Interface有共同之处,也是定义了一组规范 使用abstract关键字做声明
抽象方法只存在于抽象类中 可以使用protected 但是不能使用private
语法:
abstract class Animal {
abstract public 函数($变量);
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php
abstract class Animal { abstract public function eat($food); protected $a1="cat"; } class Animal1 extends Animal { public $b1="cat"; public $a2="dog"; public function eat ($food) { echo "eat"." "."$food"; echo "\n"; echo $this->a1; } } $a=new Animal1; $a->eat("patato");
|
输出:
抽象类可以理解成一个上层语法概念,使用abstract public function eat($food); 约束子类的方法规则 和interface一样可以不用实现具体方法代码 在抽象类定义的变量也可以被子类继承
例2:抽象类也可写入__contruct 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <?php
abstract class Animal { abstract public function eat($food); protected $a1 = "cat"; public $time;
public function sleep() { echo "睡了".$this->time."秒"; } public function __construct($time) { $this->time = $time; }
}
class Animal1 extends Animal { public $b1 = "cat"; public $a2 = "dog";
public function eat($food) { echo "eat "."$food"; echo "\n"; echo $this->a1; }
}
$a = new Animal1("3"); $a->sleep(); $a->eat("patato"); ?>
|
与interface的区别:
interface是一种规范 而abstract 是一个类 相比interface更加灵活
11.trait关键字的复用
- 解决类的单一继承问题
- 可以使用多个trait 进行复用 中间使用逗号隔开
- 把常用的,通用的代码写成trait
- trait 可以使用抽象方法
- trait可以使用其他trait
- trait中可以使用parent
语法:
1 2 3 4 5 6 7 8 9
| trait A { } trait B { } class C { use A,B.... }
|
trait和类的继承非常的像 但是trait中不能有类常量,且trait不能被实例化
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php trait Animal { public $a1 = "dog"; abstract function say(); } class Animal2 { use Animal; public function say(){ echo 1; } } $a=new Animal2(); var_dump($a); ?>
|
运行结果:
1 2 3 4
| object(Animal2) ["a1"]=> string(3) "dog" }
|
可以看到成功继承了 父类Animal的内容
例2:代码复用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php trait A { public function test() { echo 1; } } trait B { public function test() { echo 2; } } class Test { use A; } $T = new Test(); $T->test(); ?>
|
在这个例子中 我们将代码放入两个类中 引用的时候用use 来代码复用
输出1
使用trait 调用其他trait:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php trait A { public function test() { echo 1; } } trait B { use A; public function test2() { echo 2; } } class Test { use B; } $T = new Test(); $T->test(); ?>
|
在这个例子中 我们在B中引用了A 的test函数 所以实例化之后的Test实际上调用的是A的test函数
11.1在trait 中调用关键字parent::指向父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php class ParentClass { public function foo() { echo "Parent\n"; } }
trait MyTrait { public function bar() { parent::foo(); echo "Trait\n"; } }
class Child extends ParentClass { use MyTrait; }
$obj = new Child(); $obj->bar(); ?>
|
class引用了 Mytrait 里面的属性 也把parent同样给带下来了
最终变成了Child 继承ParentClass 的方法