教学之友,学习之友。

站长教学网

当前位置: 站长教学网 > 网站编程 > PHP教程 >

PHP类中的魔术方法使用实例讲解(3)

时间:2012-08-03 16:09来源:未知 作者:ken 点击:

昨天答辩实在曲折,到现在还没有开始,废话不说了,感觉继续奉上教程笔记。

今天我们要说的是__call() 和其他一些不常用的模式方法,我也许只会点到一下,如果你想搞的非常透彻还需要自己下功夫。

__call() 是个非常重要的魔术方法,可以说非常非常的重要。至于为什么,相信你学习完今天的笔记就会明白了。

当我们实例化一个类的对象后,调用类中的一个方法,比如$test->fun();。系统就会从这个类中去找fun()这个方法,如果找到了就去执行它,如果没有找到就去调用 __call()。
我们来看下例子:

[php]
class Test {

        public function __call($fun, $args) {  //第一次参数是方法名,第二个参数是传过来的参数,是以数组的方式传过来的。
                echo "你在调用".$fun."方法";
                print_r($args);
        }
}
$test = new Test();
$test->fun("a","b");  //调用一个不存在的方法 参数是a和b .
//输出  你在调用test方法Array ( [0] => a [1] => b )
[/php]

从上面的例子,我们可以清晰的分析出__call()方法的执行过程。但是我们想,如果类中存在一个同名的private方法,PHP会如何处理呢?我们来看下面的例子。

[php]
class Test {

        public function __call($fun, $args) {
                echo "你在调用".$fun."方法";
                print_r($args);
        }

        private function fun($a, $b) {
                echo "你再调用类中的private方法";
        }
}
$test = new Test();
$test->fun("a","b");  //调用类中存在的private方法
//输出  Fatal error: Call to private method Test::fun() from context '' in PHPDocument3 on line 14
[/php]

结果输出了错误,看来这种时候,魔术方法也不起作用。系统直接报错了。

现在我们再来想,如果调用本类中不存在的方法,而父类中存在的方法,是先调用本类的__call()呢还是先调用父类中存在的方法呢?我们继续来做实验。

[php]
class Test {

        public function __call($fun, $args) {
                echo "你在调用父类的".$fun."方法";
                print_r($args);
        }

        public function fun() {
                echo "你在调用父类的test方法";
        }
}
class Test2 extends Test {

        public function __call($fun, $args) {
                echo "你在调用子类的".$fun."方法";
                print_r($args);
        }
}
$test2 = new Test2();
$test2->fun("a","b");  //调用父类存在的方法  输出  你在调用父类的test方法
$test2->abc();  //调用都不存在的方法  输出  你在调用子类的abc方法Array ( )
[/php]

结果,我们可到这样的现象,子类跳过了__call()而直接寻找父类中的方法,如果调用一个都不存在的方法,就调用本类的__call()方法,这个是由于子类的__call()把父类的__call()给覆盖了。

所以我们就可以得出这样的结论,在PHP中,系统对__call()方法的调用顺序是这样的:

下面说我为什么说__call()重要。

当我们实例化一个类的对象后,调用类中的一个方法,比如$test->fun();。系统就会从这个类中去找fun()这个方法,如果存在就调用 之,如果不存在就去父类中找这个方法,如果父类中也不存在就去找本类的__call()方法,如果本类中不存在__call()方法就去找父类中的方法, 以此类推。当然这个是在调用权限允许的范围内的。

我们都知道,在PHP5中并没有真正的支持重载。但是我们可以想办法去实现它,借助的工具就是__call()。我们来看例子。

[php]
class Test {

        public function __call($fun, $argps) {
                if (method_exists($this, $fun.count($argps))) {
                        return call_user_func_array(array(&$this, $fun.count($argps)), $argps);
                      
                } else {
                        throw new Exception('调用了未知方法:'.get_class($this).'->'.$fun);  //不存在的方法
                }
        }
    //一个参数的方法
        public function fun1($a) {
                echo "你在调用一个参数的方法,参数为:".$a;
        }
    //两个参数的方法
        public function fun2($a, $b) {
                echo "你在调用两个参数的方法,参数为:".$a."和".$b;
        }
}
$test = new Test();
$test->fun("a");  //输出  你在调用一个参数的方法,参数为:a
$test->fun("a","b");  //输出  你在调用两个参数的方法,参数为:a和b
$test->fun("a","b","c");  //输出 Fatal error: Uncaught exception 'Exception' with message '调用了未知方法:Test->fun'
[/php]

这样我们基本就可以完成了重载了。

另外提下下面不常用的两个魔术方法。
第一个是__toString() 。这个著名的方法在Java中是非常常见的,但是到PHP这里我基本没见多少人用过了。原因是这样的,我们看下例子:

[php]
class Test {
        public $a;
        public function fun(){
                echo "我只是一个方法而已.";
        }
}
$test = new Test();
echo $test;  //输出错误 Catchable fatal error: Object of class Test could not be converted to string
[/php]

如果我们想打印出一个对象,就需要调用__toString()这个魔术方法了,我们给他加上一个__toString()就不会出错了。

[php]
class Test {
        public $a;
        public function fun(){
                echo "我只是一个方法而已.";
        }
        public function __toString() {
                return "你在打印一个对象";  //这个方法必须返回一个字符串
        }
}
$test = new Test();
echo $test;  //输出  你在打印一个对象
[/php]

这个比较简单,我不多说了,还有一个方法,在Google搜索都搜不到几个有价值的东西,可见其少用的程度,汗一下。这个魔术方法就是:__set_state();
这个方法其实也比较简单,就是var_export()的回调函数。我们来看下例子。

[php]
class Test {
        public $a;
        public function fun(){
                echo "我只是一个方法而已.";
        }
}
$test = new Test();
var_export($test);  //输出  Test::__set_state(array( 'a' => NULL, ))
[/php]

注意a是NULL,没有赋值,下面我写个回调

[php]
class Test {
        public $a;
        static function __set_state($array) {  //必须为静态方法.参数是个数组
                $tmp = new Test();
                $tmp->a = "abc";  //我直接赋值,
                return $tmp;  //必须返回一个对象.可以是其他类的对象
        }
}
$test = new Test();
eval( '$b = '  . var_export($test, true).  ';' );
var_dump($b);  //得到的$b就是Test的一个对象.并且a="abc"
[/php]

有人问这有什么用?是没多大用,最大的作用可以复制一个对象。只需改下代码而已。

[php]
class Test {
        public $a;
        static function __set_state($array) {  //必须为静态方法.参数是个数组
                $tmp = new Test();
                $tmp->a = $array['a'];
                return $tmp;  //必须返回一个对象.可以是其他类的对象
        }
}
$test = new Test();
$test->a = "我是$test";
eval( '$b = '  . var_export($test, true).  ';' );
var_dump($b);  //得到的$b就是$test的复制.并且a="abc"
[/php]

有人又会问了,克隆可以直接clone()方法啊!!那么好,这样的情况请问你如何克隆,我们来看下代码:

[php]
class Test {
        public $a;
        static function __set_state($array) {  //必须为静态方法.参数是个数组
                $tmp = new Test();
                $tmp->a = str_replace('$test','$b',$array['a']);  //!important
                return $tmp;  //必须返回一个对象.可以是其他类的对象
        }
}
$test = new Test();
$test->a = '我是$test';
eval( '$b = '  . var_export($test, true).  ';' );
var_dump($b);  //得到的$b就是$test的复制.但是b做相应的改变b='我是$b'
[/php]

这样的话,我们虽然克隆了一个$test,但是我们又做了相应的改变。请允许我称为这种方法为“进化”。进化就是在克隆的基础上做一些自己相应的改变。
有的人问,你为什么不用__clone进行回调呢?这正是我想说的地方,个人认为__clone() 没有 __set_state()强大,因为__clone()没有可以接受的参数,局限了“进化”的范围,我用个例子说明。

[php]
class Test {
        public $a;

        function __clone(){
                $this->a = str_replace("a","克隆a",$this->a);  //无法通过参数改变a,但是我们还是可以想办法的:)
        }
}
$test = new Test();
$test->a = '我是a';
$b = clone $test;
var_dump($b);  //得到的$b就是$test的复制.并且a='我是克隆a'
[/php]

真累啊,基本方法都提到了,但是我只是带大家入门,更多更深的内容还需要大家自己挖掘。有些地方我还没有提到,所以大家要学会去主动学习。

另外,顺便提下,这些魔术方法都必须被定义为public方式的。因为你要在所有的地方都得调用它们。

好了。这个还没完,下此会有更神奇的东西,改变你对PHP的看法,请关注。

[php]

 

<?php

class Test {
    private $mySecret;  //我的秘密不想让人知道
    public function __construct($secret) {
        $this->mySecret = $secret;
    }
    public function __sleep() {
        $this->mySecret = "这是个秘密!";    // 外部不可见.必须调用成员方法输出值.
        echo '不让你知道';  

    }
    public function __wakeup() {
        
                 
      

    }
       
        public function get(){
       
                return $this->mySecret;
        }
}

$obj = new Test('i like php !');
echo $obj->get();
echo '<br />';

serialize($obj);          // __sleep()
echo $obj->get();
echo '<br />';

unserialize($obj);   // __wakeup()
echo $obj->get();

$obj = new Test('php');
echo $obj->get();

?>

[/php]

(责任编辑:ken)

TAG标签: php 魔术方法
顶一下
(2)
100%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
注册登录:不允许匿名留言,登录后留言无需输入验证码。
栏目列表
最新内容