PHP进阶之路-- 之 “面向对象” 基础
- 概念
- 类的介绍和定义
- 类的属性
- 类常量
- 类的自动加载
- 构造函数和析构函数
- 访问控制
- 对象继承
- 范围解析操作符
- static静态关键字
- 抽象类
- 对象接口
- Trait
- 匿名类
- 遍历对象
- 魔术方法
- Final关键字
- 对象复制
一、概念
面向对象编程是很多编程语言的重头戏,对于php来讲,同样是支持面向对象的。在php5之后,对面向对象有了更加良好的支持。
掌握面向对象编程对于提高php编程能力和规划web开发架构具有很好的帮助。
在php中,想要掌握面向对象,需要先来弄清楚类和对象之间的关系。
二、类的介绍和定义
在php中,类是对象的基础,也可以说是对象的模型,没有类就没有对象。那么如何创建一个类呢?
创建类主要通过class关键字。每个类的定义都以关键字 class 开头,后面跟着类名,后面跟着一对花括号,里面包含有类的属性与方法的定义。 类名可以是任何非 PHP 保留字的合法标签。一个合法类名以字母或下划线开头,后面跟着若干字母,数字或下划线。以正则表达式表示为:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*。
一个类可以包含有属于自己的常量,变量(称为“属性”)以及函数(称为“方法”)。
例如下面的代码,创建一个非常简单的类:
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 17:48 7 */ 8 9 class MyClass { 10 // 类中包含有属性 和 方法 11 public $name; 12 public $age; 13 14 public function sayHello(){ 15 echo "hello,world"; 16 } 17 }
需要注意的是,在类中存在一个伪变量$this,这个伪变量是php提供给我们为了方便我们在类中调用自身属性或者方法而提供,$this是一个指向性的变量,它代指未来通过类创建出来的那个对象,简单点说就是谁变成了对象,这个$this就指向谁。
关于$this的具体用法可以看下面的例子:
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 17:48 7 */ 8 9 class MyClass { 10 // 类中包含有属性 和 方法 11 public $name; 12 public $age; 13 14 public function sayHello(){ 15 echo "hello,world"; 16 } 17 public function test(){ 18 $this->name = "张三"; // 设置属性值 19 } 20 }
三、类的属性
类的变量成员叫做“属性”,或者叫“字段”、“特征”,在本文档统一称为“属性”。属性声明是由关键字 public,protected 或者 private 开头,然后跟一个普通的变量声明来组成。属性中的变量可以初始化,但是初始化的值必须是常数,这里的常数是指 PHP 脚本在编译阶段时就可以得到其值,而不依赖于运行时的信息才能求值。
在类的成员方法里面,可以用 ->(对象运算符):$this->property(其中 property 是该属性名)这种方式来访问非静态属性。静态属性则是用 ::(双冒号):self::$property 来访问。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 17:48 7 */ 8 9 class MyClass { 10 //属性 11 public $name; 12 protected $age; 13 private $like; 14 public static $info; 15 16 }
四、类常量
可以把在类中始终保持不变的值定义为常量。在定义和使用常量的时候不需要使用 $ 符号。常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 17:48 7 */ 8 9 class MyClass { 10 11 const hello = "hello,world"; // 类常量 12 13 // 在方法中调用类常量 14 public function getCon(){ 15 echo self::hello; 16 } 17 18 }
五、类的自动加载
在编写面向对象(OOP) 程序时,很多开发者为每个类新建一个 PHP 文件。 这会带来一个烦恼:每个脚本的开头,都需要包含(include)一个长长的列表(每个类都有个文件)。
在 PHP 5 中,已经不再需要这样了。 spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。通过注册自动加载器,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 17:48 7 */ 8 9 spl_autoload_register(function($className){ 10 require_once $className . ".php";// 需要注意的是,如果想要通过这种方式加载,那么类名必须和文件名相同 11 }); 12 13 $obj = new MyClass(); 14 $obj->sayHello(); 15 16 #============================================== 17 //MyClass.php 18 class MyClass { 19 public function sayHello(){ 20 echo "hello,world"; 21 } 22 }
六、构造函数和析构函数
PHP 5 允行开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。
需要注意的是,构造函数通常情况下我们用来进行初始化工作,同时构造函数的执行时机是类实例化生成对象执行自动执行。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 18:06 7 */ 8 9 // 构造函数和析构函数 10 class MyClass { 11 12 // 假设我们的属性并没有初始化 13 public $name; 14 15 // 那么我们就可以在构造函数中进行初始化 16 public function __construct($name) 17 { 18 $this->name = $name; // 对类的属性进行初始化 19 } 20 21 public function __destruct() 22 { 23 // TODO: Implement __destruct() method. 24 echo "我是在回收销毁之前执行哦"; 25 } 26 } 27 28 // 因为我们设置了构造函数,并且让构造函数进行初始化操作,那么我们在实例化的时候,就需要传入值 29 $obj = new MyClass("张三");
我们在使用构造函数和析构函数的时候需要注意的地方就是二者的执行时机,构造函数在实例化的时候会执行,而析构函数会在回收销毁时执行。
七、访问控制
对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。被定义为公有的类成员可以在任何地方被访问。被定义为受保护的类成员则可以被其自身以及其子类和父类访问。被定义为私有的类成员则只能被其定义所在的类访问。
php作为一门服务端语言,代码的严谨性是直接与web站点的安全挂钩,所以说你写的代码是否足够强壮(安全),就决定了你的网站安全程度,而访问限制就是保证代码强壮的因素之一。
1 <?php 2 class MyClass { 3 4 // 创建属性 5 public $name; 6 protected $age; 7 private $sex; 8 9 // 创建方法 10 // 构造方法通常情况下是public权限 11 public function __construct($name,$age,$sex) // 对属性进行初始化 12 { 13 $this->name = $name; 14 $this->age = $age; 15 $this->sex = $sex; 16 } 17 // 受保护的权限,只能够在本类和子类中使用 18 protected function sayHello(){ 19 echo "hello,world"; 20 } 21 // 私有权限,只能够在本类中使用 22 private function test(){ 23 echo "this is test"; 24 } 25 }
八、对象继承
继承已为大家所熟知的一个程序设计特性,PHP 的对象模型也使用了继承。继承将会影响到类与类,对象与对象之间的关系。
比如,当扩展一个类,子类就会继承父类所有公有的和受保护的方法。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能。继承对于功能的设计和抽象是非常有用的,而且对于类似的对象增加新功能就无须重新再写这些公用的功能。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 18:16 7 */ 8 9 // 对象继承 10 class Base { 11 public $info; 12 13 public function __construct($info) 14 { 15 $this->info = $info; 16 } 17 } 18 19 // 创建子类继承 -- 子类会无条件继承父类的所有的属性和方法 20 class MyClass extends Base { 21 22 // 如果当子类中的方法与父类相同,那么子类的方法会覆盖掉父类的方法 23 public function __construct($info) // 此时子类的构造函数会覆盖掉父类的构造函数 24 { 25 // 如果想要使用父类的构造函数,需要如下 26 parent::__construct($info); 27 } 28 } 29 30 $obj = new MyClass('hello');
九、范围解析操作符
范围解析操作符(也可称作 Paamayim Nekudotayim)或者更简单地说是一对冒号,可以用于访问静态成员,类常量,还可以用于覆盖类中的属性和方法。
当在类定义之外引用到这些项目时,要使用类名。self,parent 和 static 这三个特殊的关键字是用于在类定义的内部对其属性或方法进行访问的。
例如我们上面在类的方法使用父类的方法,可以使用parent::MethodName ,例如我们想要调用静态变量,可以是self::$StaticName。
十、static静态关键字
声明类属性或方法为静态,就可以不实例化类而直接访问。静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)。
由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。
静态属性不可以由对象通过 -> 操作符来访问。
用静态方式调用一个非静态方法会导致一个 E_STRICT 级别的错误。
就像其它所有的 PHP 静态变量一样,静态属性只能被初始化为文字或常量,不能使用表达式。所以可以把静态属性初始化为整数或数组,但不能初始化为另一个变量或函数返回值,也不能指向一个对象。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 18:31 7 */ 8 9 class MyClass { 10 // 创建一个静态属性 11 public static $info = "php is the best language in the world"; 12 13 // 创建一个静态方法,并且调用这个静态属性 14 public static function test(){ 15 echo self::$info; 16 } 17 }
十一、抽象类
PHP 5 支持抽象类和抽象方法。定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。
继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。例如,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 18:31 7 */ 8 9 // 抽象类 10 abstract class AbstractClass { 11 // 抽象类中的方法会强制要求子类去实现 12 abstract protected function Run(); 13 abstract protected function Song(); 14 15 // 抽象类中同样可以存在普通的方法 16 public function hello(){ 17 echo "hello,world"; 18 } 19 } 20 21 // 定义普通类并且继承抽象类 22 class MyClass extends AbstractClass { 23 // 子类中的权限必须跟父类相同或者更加宽松 24 public function Run() 25 { 26 // TODO: Implement Run() method. 27 echo "running...."; 28 } 29 public function Song() 30 { 31 // TODO: Implement Song() method. 32 echo "唱歌ing..."; 33 } 34 } 35 36 // 实例化子类 37 $obj = new MyClass(); 38 $obj->Run(); 39 $obj->Song(); 40 $obj->hello();
十二、 对象接口
使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。
接口中定义的所有方法都必须是公有,这是接口的特性。
要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。类可以实现多个接口,用逗号来分隔多个接口的名称。
接口中也可以定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口所覆盖。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 18:40 7 */ 8 9 // 接口 10 interface Hello { // 定义一个接口,接口权限必须全部是公开的 11 public function sayHello($name); 12 } 13 14 // 通过类来实现接口 15 class MyClass implements Hello{ 16 public function sayHello($name) 17 { 18 // TODO: Implement sayHello() method. 19 echo "你好,{$name}"; 20 } 21 }
十三、Trait
自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。
Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。 无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 18:44 7 */ 8 9 // trait 实现代码复用 10 trait Hello { 11 public function HelloWorld() { 12 echo "hello,world!"; 13 } 14 public function HelloPhp(){ 15 echo "hello,php!"; 16 } 17 public function HelloHow(){ 18 echo "how are you ?"; 19 } 20 } 21 22 23 // 创建一个类并且使用hello 24 class MyClass { 25 // 我们在这个类中想要实现打招呼的功能 26 use Hello; 27 } 28 29 class MyClassTwo { 30 use Hello; // 第二次使用Hello trait 31 } 32 33 $obj = new MyClass(); 34 $obj2 = new MyClassTwo(); 35 36 $obj->HelloHow(); 37 $obj2->HelloWorld();
十四、匿名类
PHP 7 开始支持匿名类。 匿名类很有用,可以创建一次性的简单对象。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 18:50 7 */ 8 class MyClass {} 9 interface Test {} 10 trait TestTrait {} 11 12 var_dump(new class(10) extends MyClass implements Test { 13 private $name; 14 15 public function __construct($num) 16 { 17 $this->num = $num; 18 } 19 20 use TestTrait; 21 }); 22 23 24 ################################## 25 # 打印结果 26 #object(class@anonymous)#1 (2) { 27 # ["name":"class@anonymous":private]=> 28 # NULL 29 # ["num"]=> 30 # int(10) 31 # } 32 ###################################
匿名类被嵌套进普通 Class 后,不能访问这个外部类(Outer class)的 private(私有)、protected(受保护)方法或者属性。 为了访问外部类(Outer class)protected 属性或方法,匿名类可以 extend(扩展)此外部类。 为了使用外部类(Outer class)的 private 属性,必须通过构造器传进来:
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 18:56 7 */ 8 9 // 匿名类 10 class MyClass { 11 12 private $num = 10; 13 14 public function testFunc(){ 15 return new class($this->num) extends MyClass { //想要访问外部受保护的属性或者方法需要继承外部类 16 private $prop3; 17 18 public function __construct($num) // 处在类方法里面的匿名类想要调用上级属性,需要通过构造函数传值的形式传入 19 { 20 $this->prop3 = $num; 21 } 22 }; 23 } 24 25 26 27 }
十五、遍历对象
在php中,遍历对象,我们可以采用foreach方法。通过foreach方法所有的可见属性都可以被遍历出来。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 19:44 7 */ 8 9 class MyClass { 10 public $name = "Java"; 11 public $info = "java 和 php "; 12 13 public function sayBye(){ 14 echo "我爱php,再见java"; 15 } 16 } 17 18 $obj = new MyClass(); 19 20 foreach($obj as $key=>$value){ 21 echo "$key=>$value"; 22 }
十六、魔术方法
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() 和 __debugInfo() 等方法在 PHP 中被称为"魔术方法"(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能。
通常情况下,魔术方法不需要显示的调用。
具体介绍:http://php.net/manual/zh/language.oop5.magic.php
十七、Final关键字
PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承。
1 <?php 2 /** 3 * Created by PhpStorm. 4 * User: liujunhang 5 * Date: 2018/6/18 6 * Time: 19:44 7 */ 8 9 class MyClass { 10 11 public function test(){ // 类中的普通方法 12 echo "test ....."; 13 } 14 15 final public function test2(){ 16 echo "final method ..."; // final方法 17 } 18 } 19 20 // 子类继承 21 class MyTemplete extends MyClass { 22 // 覆盖父类的final方法 23 public function test2() { 24 // 执行就会报 Cannot override final method MyClass::test2() 25 } 26 } 27 28 $obj = new MyTemplete(); 29 $obj->test(); // test ...... 30 $obj->test2(); // final method ... 可以使用父类的final方法 ,但是一旦进行覆盖则会报错
十八、对象复制
对象复制可以通过 clone 关键字来完成(如果可能,这将调用对象的 __clone() 方法)。对象中的 __clone() 方法不能被直接调用。
语法: $copy_of_object = clone $object;
当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。
1 <?php 2 header("Content-type:text/html;Charset=utf-8"); 3 4 5 // 创建一个对象,并且进行复制操作 6 class MyClass { 7 public $info = <<<TEST: 8 Name: 张三 9 Phone:13889893432 10 Address:上海静安区万荣路1188号 11 TEST:; 12 13 public function Running(){ 14 echo "running....."; 15 } 16 17 } 18 // 将这个对象实例化,并且进行克隆 19 $obj = new MyClass(); 20 $test= clone $obj; 21 //var_dump($test); 22 23 $test->hello = "hello,PHP"; // 克隆对象新增加了一个属性 ,打印后发现原对象并没有增加,克隆对象增加 24 //var_dump($obj); 25 //var_dump($test); 26 27 $obj->info="info替换更改"; // 当原对象发生更改的时候,克隆对象并没有发生更改 28 var_dump($obj); 29 var_dump($test); 30 31 32 33 34 35 36 37 38 39 40 ?>