购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.14 魔术方法

1.__get()与__set()

我们可以通过__get()、__set()、__call()方法来存取类中没有定义的成员方法和属性。当我们试图写入一个不存在或不可见的属性时,PHP就会执行类中的__set()方法。__set()方法必须接收两个参数,用来存放试图写入的属性名称和属性值。来看下面的脚本例子,如代码清单2-23所示:

代码清单2-23 使用__set与__get魔术方法


<php
      class MyShop {
        private $p = array();
         function __set$name $value { //取得属性名称和值
            echo "set::$name$value <br />"
            $this->p[$name] = $value
        }
        function __get$name { //取得属性名称
            print "get::$name <br />"
            return array_key_exists$name$this->p
    $this->p[$name]  null
        }
      }
      $shop = new MyShop();
      $shop->apple = 2
      $shop->pear = 3
      $shop->pear++
      echo "苹果=". $shop->apple. "<br>"
      echo "=". $shop->pear. "<br />"
   >

执行结果如下:


set::apple2 
set::pear3 
get::pear 
set::pear4 
get::apple 苹果=2
get::pear =4

从以上例子中,我们可以总结出__get()和__set()方法的功能:在引用该类中不存在的成员变量时,可以调用这两个方法,我们还可以用它们实现错误消息的提示,可以通过这两个方法动态地创建新变量来扩展一个类。

2.__call()

当我们试图调用类中一个不存在或不可见的方法时,PHP会执行该类中的__call()方法。__call()也必须接收两个参数,用来存放试图调用的方法名称及其参数(参数会被放在一个与该参数同名的数组中),如代码清单2-24所示:

代码清单2-24 使用__call方法


<php
  class  callClass  {
    function  __call$method_name  $parameters  {
      echo'使用__call尝试调用一个不存在/不可用的成员方法');
      echo'<i>'.$method_name.'</i>');
      echo'<b>    __call()开始调用</b><br>');
      echo'<b>从传递的参数传入parameters数组,内容如下</b><br><pre>');
      print_r$parameters);
    }
  }
  $obj  =  new  callClass();
  $obj->someMethod19.9"测试文本");
   >

该脚本执行的结果如下:


   使用__call尝试调用一个不存在/不可用的成员方法someMethod  __call()开始调用
   从传递的参数传入parameters数组,内容如下:
Array
[0] => 1
[1] => 9.9
[2] => 测试文本
   
  

看了上面例子,相信你应该能够理解__call()的含义了。下面我们运行一个实际应用例子,如代码清单2-25所示:

代码清单2-25 __call方法应用实例


<php
class MyShop {
    private $obj
    function __construct$obj {
        $this->obj = $obj
    }
    function __call$method $args {
        print $method."::".implode$args""."\n"
        if isset$this->obj && method_exists$this->obj $method)) {
           return call_user_func_arrayarray$this->obj $method), $args);
        }
    }
}
class Calculate {
    private $items = 0
    function add$num{
        $this->items += $num
    }
    function sum(){
        return $this->items
    }
}
$obj = new Calculate();
$shop = new MyShop$obj);
$shop->add2);
print $shop->sum();
   >

那么,上面的脚本代码执行后的结果是这样的:


add::2 sum:: 2

3.__sleep()与__wakeup()

__sleep()方法在序列化(serialize)一个实例的时候被调用,__wakeup()则是在反序列化(unserialize)的时候被调用。

需要注意一点,__sleep()必须返回一个数组或者对象(一般返回的是当前对象$this),返回的值将会被用来做序列化的值,如果不返回这个值,则表示序列化失败。这也意味着反序列化不会触发__wakeup()事件。

下面我们再来看一个引用序列化的实用例子,如代码清单2-26所示:

代码清单2-26 序列化引用实例


<php
class myMagic
{
      public $a='xx'
      public $b='55'
      function __sleep(){
            echo "I am sleepy\n"
            return $this
      }
      function __wakeup(){
            echo "wake up\n"
      }
}
$i = new myMagic
$si = serialize$i);
echo "sleeping now.....\n"
print_runserialize$si));
   >

4.__toString()

我们先看一个应用实例,如代码清单2-27所示:

代码清单2-27 错误应用实例


<php
class Person {
    private $name
    function __construct$name{
        $this->name = $name
    }
}
$obj = new Person"Raymond du");
echo $obj
   >

该程序将输出以下的信息:


Catchable fatal error Object of class Person could not be converted to string in toString.php on line 9

上面这句话提示我们:捕捉到致命的错误,Person类不能被转换为String。

PHP提供一个叫__toString()的魔术方法,可以把类的实例转化为字符串,所以对于上面的实例,可以做以下修改,如代码清单2-28所示:

代码清单2-28 __toString方法应用实例


<php
class Person {
      private $name
      function __construct$name{
            $this->name = $name
      }
      function __toString(){
            return $this->name
      }
}
$obj = new Person"Raymond du");
echo $obj
   >

以上的代码将输出为:


Raymond du

__toString()成员方法调用并打印当前类中构建器中的值。在这个结构中,调用了公共的字符串操作,这样字符串的连接也就形成了一个字符串。

5.__autoload()

在编写面向对象程序时,常规做法是将每一个类保存为一个PHP源文件,这样做的好处是很容易找到一个类在什么地方,并且在需要调用某个类的时候,直接使用include或者require引用到当前文件就可以了。缺点是,我们每次都要包含一些源文件,如果调用的类或函数很多,需要在源代码头部包含一堆include或require的代码。

__autoload()方法可以方便地解决这个问题,在一些情况下省却使用include或require语句的麻烦。

如果我们在类中定义了__autoload()方法(一个类中仅能使用一次),而你访问一个类还未定义,则__autoload()方法开始将这个类名作为文件名参数来调用该类,如果能够成功引入该类,脚本将继续顺利执行下去,如果没有调用到该类,PHP引擎则抛出一个fatal错误,停止该脚本的执行。

下面的例子显示了如何调用__autoload()方法。这个文件名为MyClass.php,文件内容如代码清单2-29所示:

代码清单2-29 MyClass.php脚本内容


<php
class MyClass {
      function printWorld(){
            echo "物有本末,事有始终,知所先后,则近道矣\n"
      }
}>

general.inc.php文件的内容如代码清单2-30所示:

代码清单2-30 general.inc.php脚本内容


<php
function __autoload$class_name{
      $file = dirname__FILE__  . "/libs/classes/$class_name.php");
      if(!file_exists$file)){
            return false
      } else {
            require_once$file);
      }
}>

main.php文件用于包含上面的脚本,如代码清单2-30所示:

代码清单2-31 main.php脚本内容


<php
require_once"general.inc.php");
$obj = new MyClass();
ifis_object$obj)){
      $obj->printWorld();
}else{
      echo "类文件调入失败。"
}>

上面的脚本例子将显示下列词句:


   物有本末,事有始终,知所先后,则近道矣
  

词语本意可以忽略,从结果我们可以看到在MyClass.php中并未明确指出包含哪个文件,但是由于使用了__autoload()方法,把经常使用的类自动引入进来,如general.inc文件的内容,所以能显示上述的内容。

需要注意的是,虽然PHP对类名的大小写不敏感,但__autoload()方法会按你发送给它的类名严格进行大小写匹配。

如果你喜欢大小写混合的方式来命名一个类,那么在源代码内也要保持大小写一致,这样才可能引入你需要的类库。如果不太喜欢这样做,可以用strtolower()函数,在类引入之前强制命名为小写的形式。

另外,这里的内容可以再结合本书的include内容部分,更好地融会贯通。 T/Wi3BhN5nhCwbxeKB3tUtjKW4sCNImIZaorrz6LcqFwy138KOEIJpDPOEk4vlpZ

点击中间区域
呼出菜单
上一章
目录
下一章
×