PHP面向对象程序设计重载(overloading)操作详解

本文实例讲述了PHP面向对象程序设计重载(overloading)操作,分享给大家供大家参考,具体如下:

重载

PHP中的”重载”与其它绝大多数面向对象语言不同,只是他们都是用的相同的名词而已。传统的”重载”是用于提供多个同名的 类方法,但各方法的参数类型和个数不同。 PHP所提供的”重载”(overloading)是指动态地”创建”类属性和方法。当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用。是通过魔术方法(magic methods)来实现的。

一般来说,把类中的成员属性都定义为private的,这更符合现实的逻辑,能够更好的对类中成员起到保护作用。但是,对成员属性的读取和赋值操作是非常频繁的,而如果在类中为每个私有属性都定义可以在对象的外部获取和赋值的公有方法,又是非常非常烦恼的。因此在PHP5.1.0以后的版本中,预定义了两个方法“__get()”和“__set()”,用来完成对所用私有属性都能获取和赋值操作,以及用来检查私有属性是否存在的方法“__isset()”和用来删除对象中私有属性方法“__unset()”。

通俗一点来说,重载在php中的含义是指,当一个对象或类使用其未定义或不可见的属性和方法时,其中的一些“处理机制”。

属性重载

对一个对象不存在的属性进行使用时,这个类中预先设定好的应对办法(处理机制)。

属性,本质就是变量,其只有4个操作:

取值:

当对一个对象不存在(未定义或不可见)的属性进行“取值”时,就会自动调用方法:__GET()方法不区分大小写。

赋值:

当对一个对象不存在(未定义或不可见)的属性进行“赋值”时,就会自动调用方法:__SET()

判断(isset):

当对一个对象不存在(未定义或不可见)的属性进行isset()判断时,就会自动调用方法:isset()

销毁(unset):

当对一个对象不存在的(未定义或不可见)属性进行unset()判断时,就会自动调用方法:unset()

以上4个方法,被称为魔术方法。

魔术方法

__GET($属性名):

在对一个对象不存在的属性进行“取值”的时候,会自动调用的方法,其中该方法可以带一个形参,表示要对之取值而又不存在的属性名(字符串),可以使用该方法对意外情况进行某种特殊的处理。

例如:

  1. <?php
  2. class A{
  3. public $p1 = 1;
  4. }
  5. $a1 = new A();
  6. echo $a1->p1; //1
  7. echo $a1->p2; //未定义$p2,会报错, Notice: Undefined property: A::$p2
  8. ?>

php的重载,使用__get()方法对上面的出错作“优雅处理”。

  1. <?php
  2. class A{
  3. public $p1 = 1;
  4. //private $p2 = 1; //这里将属性私有化,其实和未定义一样,对外部来说都相当于不存在
  5. function __get($prop_name){
  6. /*
  7. //比如可以这样处理
  8. echo "<br />{$prop_name}属性还未定义(不存在)!";
  9. return ""; //也可以返回0,或false等
  10. */
  11. //还可以这样处理
  12. trigger_error("发生错误:属性不存在!", E_USER_ERROR);
  13. die();
  14. }
  15. }
  16. $a1 = new A();
  17. echo $a1->p1; //1
  18. echo $a1->p2; //未定义$p2,但经过"处理"
  19. ?>

这里举一个对所用私有属性获取的操作的例子。

例子:

  1. <?php
  2. class Person{
  3. public $name;
  4. public $sex;
  5. private $age; //年龄私有化,类外不能直接访问这个属性
  6. function __construct($name='', $sex='', $age){
  7. $this->name = $name;
  8. $this->sex = $sex;
  9. $this->age = $age;
  10. }
  11. private function __get($propertyName){ //这里要用private修饰,防止类外部调用
  12. if($propertyName == 'age'){
  13. return $this->age;
  14. }
  15. }
  16. }
  17. $p = new Person('yeoman', '男',23);
  18. $v1 = $p->name;
  19. $v2 = $p->sex;
  20. $v3 = $p->age; //自动调用了__get()方法获取私有属性age(函数定义里面返回)
  21. echo "name=$v1, sex=$v2, age=$v3";
  22. ?>

运行结果为:

name=yeoman, sex=男, age=23

__SET($属性名, 值):

当对一个对象不存在的属性进行“赋值”时,就会自动调用这个内部的魔术方法;其有2个形参,分别代表要对不存在的属性进行赋值的“属性名”和“属性值”。

这个方法,结合_GET方法,往往可以使我们定义的类,有一种可扩展的特性。即:类或对象的属性,可以更为方便自由。

例子:

  1. <?php
  2. class A{
  3. //定义一个属性,
  4. protected $prop_list = array(); //初始为空数组
  5. //这个方法会在A的对象使用一个不存在的属性进行赋值时调用
  6. function __set($p,$v){
  7. //echo "使用不存在的属性!";
  8. $this->prop_list[$p] = $v;
  9. }
  10. function __get($p){
  11. return $this->prop_list[$p];
  12. }
  13. }
  14. $a1 = new A();
  15. $a1->p1 = 1; //不存在的属性名赋值,此时会调用_set(),并传过去"p1"和1
  16. $a1->p2 = 2;
  17. $a1->ac = 'avc';
  18. echo "<br />输出这些“不存在的属性”的值:";
  19. echo "<br />a1->p1:" . $a1->p1; //不存在的属性名取值,此时会调用_get(),并传过去"p1"
  20. echo "<br />a1->p2:" . $a1->p2;
  21. echo "<br />a1->ac:" . $a1->ac;
  22. ?>

运行结果为:

输出这些“不存在的属性”的值:

a1->p1:1

a1->p2:2

a1->ac:avc

__ISSET($属性名):

当对一个对象不存在的属性进行isset()判断时,就会自动调用内部方法:isset();

用法:

$v1 = isset($对象->不存在的属性); //此时会调用这个对象所属类中的魔术方法:isset()

例子:

  1. <?php
  2. class A{
  3. //定义一个属性,
  4. protected $prop_list = array(); //初始为空数组
  5. //这个方法会在A的对象使用一个不存在的属性进行赋值时调用
  6. function __set($p,$v){
  7. //echo "使用不存在的属性!";
  8. $this->prop_list[$p] = $v;
  9. }
  10. function __get($p){
  11. if($this->prop_list[$p]){
  12. return $this->prop_list[$p];
  13. }else{
  14. return "该属性不存在!";
  15. }
  16. }
  17. function __isset($prop){ //__isset()是自定义的方法, isset()是系统函数
  18. $re = isset($this->prop_list[$prop]);
  19. return $re;
  20. }
  21. }
  22. $a1 = new A();
  23. $a1->p1 = 1;//不存在的属性名赋值,此时会调用_set(),并传过去"p1"和1
  24. $a1->p2 = 2;
  25. $a1->ac = 'avc';
  26. echo "<br />输出这些“不存在的属性”的值";
  27. echo "<br />a1->p1:" . $a1->p1;//不存在的属性名取值,此时会调用_get(),并传过去"p1"
  28. echo "<br />a1->p2:" . $a1->p2;
  29. echo "<br />a1->ac:" . $a1->ac;
  30. //下面演示isset判断不存在的属性
  31. $v1 = isset($a1->p1); //存在
  32. $v2 = isset($a1->ppp1); //不存在
  33. var_dump($v1);
  34. echo "<br />";
  35. var_dump($v2);
  36. ?>

运行结果:

输出这些“不存在的属性”的值

a1->p1:1

a1->p2:2

a1->ac:avc

boolean true

boolean false

__UNSET($属性名)

当对一个对象不存在的属性进行unset()销毁时,就会自动调用内部方法:unset();

  1. <?php
  2. class A{
  3. //定义一个属性,
  4. protected $prop_list = array(); //初始为空数组
  5. //这个方法会在A的对象使用一个不存在的属性进行赋值时调用
  6. function __set($p,$v){
  7. //echo "使用不存在的属性!";
  8. $this->prop_list[$p] = $v;
  9. }
  10. function __get($p){
  11. if($this->prop_list[$p]){
  12. return $this->prop_list[$p];
  13. }else{
  14. return "该属性不存在!";
  15. }
  16. }
  17. function __unset($prop){
  18. unset($this->prop_list[$prop]);
  19. }
  20. }
  21. $a1 = new A();
  22. $a1->p1 = 1;//不存在的属性名赋值,此时会调用_set(),并传过去"p1"和1
  23. echo "<br />a1->p1:" . $a1->p1;//不存在的属性名取值,此时会调用_get(),并传过去"p1"
  24. //下面演示unset销毁一个不存在的属性
  25. unset($a1->p1);
  26. echo "<br />a1->p1:" . $a1->p1;
  27. ?>

运行结果为:

a1->p1:1

a1->p1:该属性不存在!

下面的例子中,声明一个Person类,并将所有的成员属性设置成private的。在类中添加自定义的“__isset()”和“__unset()”两个方法。在类外部使用“isset()”和“unset()”函数时,会自动调用这两个方法。代码如下:

  1. <?php
  2. class Person{
  3. private $name; //此属性被封住
  4. private $sex;
  5. private $age;
  6. function __construct($name='', $sex='男', $age){
  7. $this->name = $name;
  8. $this->sex = $sex;
  9. $this->age = $age;
  10. }
  11. private function __isset($propertyName){ //需要一个参数,是测定的私有属性的名称
  12. if($propertyName == 'name'){
  13. return false; //返回假,不允许在类外部测定name属性
  14. }
  15. return isset($this->$propertyName); //这里propertyName要加$符,因为这是参数,不是属性
  16. }
  17. private function __unset($propertyName){
  18. if($propertyName == 'name')
  19. return; //退出方法,不允许删除对象中的name属性
  20. unset($this->$propertyName); //这里propertyName要加$符
  21. }
  22. public function say(){
  23. echo "名字:" . $this->name . ",性别:" . $this->sex . ",年龄:" . $this->age . "<br />";
  24. }
  25. }
  26. $person = new Person("yeoman", "男", 23);
  27. var_dump(isset($person->name)); //输出bool(false),不允许测定name属性
  28. var_dump(isset($person->sex)); //输出bool(true),存在sex私有属性
  29. var_dump(isset($person->age)); //输出bool(true),存在age私有属性
  30. var_dump(isset($person->id)); //输出bool(false),测定对象中不存在id属性
  31. unset($person->name); //删除私有属性name,但在 __unset()中不允许删除
  32. unset($person->sex); //删除对象中的私有属性sex,删除成功
  33. unset($person->age);
  34. $person->say(); //对象中的sex和age属性被删除,输出:名字:yeoman,性别:,年龄:
  35. ?>

运行结果:

  1. boolean false
  2. boolean true
  3. boolean true
  4. boolean false

名字:yeoman,性别:,年龄:

方法重载

当对一个对象不存在的实例方法进行“调用”时,会自动调用类中的__call()这个魔术方法;

当对一个类不存在的静态方法进行“调用”时,会自动调用类中的__callstatic()这个魔术方法。

例子:直接调用不存在的方法

  1. <?php
  2. ini_set('display_errors',1);
  3. class A{
  4. }
  5. $a = new A();
  6. $a->f1(); //不存在的方法
  7. ?>

会报错,报错内容为:

Fatal error: Uncaught Error: Call to undefined method A::f1()

对上面报错作“优雅处理”:

  1. <?php
  2. class A{
  3. //当对这个类的对象不存在的实力方法进行调用时,会自动调用本方法
  4. //这个方法必须带2个形参:
  5. //$methodName:表示要调用的不存在的方法名;
  6. //$argument:表示要调用该不存在的方法时,所使用的实参数据,是一个数组。
  7. function __call($methodName, $argument){
  8. //echo "__call被调用了!";
  9. echo $methodName . "()方法不存在!";
  10. }
  11. }
  12. $a = new A();
  13. $a->f1(); //不存在的方法,但经过处理
  14. ?>

运行结果为:

f1()方法不存在!

当对一个类不存在的静态方法进行“调用”时,会自动调用类中的__callstatic()这个魔术方法。和上面的处理类似。