PHP

PHP中__call和__callStatic总结

Posted by Alex Kinhoom on July 21, 2018

定义

__call()__callStatic()方法实现了方法的重载。

__call(),__callStatic()方法

PHP5.3.0之后,又增加了一个__callStatic()方法。它们都是PHP中的魔术方法,所谓魔术方法,就是系统在特定时刻自动调用的方法!除了它们俩,PHP中还有其它一些魔术方法(见手册)。对于魔术方法,各魔术方法有一个共同点:系统自动调用,有两个不同点:调用的时间、调用之后产生的作用。
对于__call()__callStatic()的调用时间和功能(通俗点就是调用之后产生的结果)。例子如下

class A {
        public function test () {
                static::who();
                A::who();
                self::who();
                $this->who();
        }

        /**
         *私有方法
        */
        private function test2(){

        }

        public static function __callStatic($a, $b) {
                var_dump('A static');
        }   
            
        public function __call($a, $b) {
                var_dump('A call');
        }   
}

$a = new A;
$a->test();
A::test1();
$a->test2();//访问私有或者保护方法会提前被__call()截取

输出为

string(6) "A call" string(6) "A call" string(6) "A call" string(6) "A call" string(8) "A static" string(6) "A call"

在类内部调用本类当中的一个不可访问(如果是本类中,那就只能是不存在才不可访问,如果是在本类外不可访问还可能是没有访问权限)的方法时,不管是对象方式,还是静态方式,都只能触发__call()方法
在类外部调用一个类中的一个不可访问的方法时,对象方式就触发__call()方法,静态方式就触发__callStatic()方法
不可访问不仅仅代表不存在

具体实例

class  MethodTest {
    public function __call($name,$arguments) 
    {
         // 注意: $name 的值区分大小写
         echo "Calling object method '$name'的参数有多个,分别是:".implode ('、',$arguments)."<br/>" ;
    }

     /**  PHP 5.3.0之后版本  */
     public static function __callStatic($name,$arguments) 
    {
         // 注意: $name 的值区分大小写
         echo  "Calling static method'$name'的参数有多个,分别是:".implode ('、',$arguments)."<br/>" ;
    }
}

 $obj=new MethodTest ;
 $obj->runTest ('in object context','另外一个参数');

 MethodTest::runTest ('in static context','另外一个参数');   // PHP 5.3.0之后版本

输出为

Calling object method 'runTest'的参数有多个,分别是:in object context、另外一个参数
Calling static method'runTest'的参数有多个,分别是:in static context、另外一个参数

注:触发__call()__callStatic()方法时,系统会自动将所调用的那个不可访问的方法的方法名作为第一个参数传入__call()__callStatic()方法中,而将所调用的不存在的方法传入的参数,作为第二个参数(而且是封装成了一个数组,即每一个元素就是调用不可访问方法时传入的一个参数)传入__call()__callStatic()方法中,那么在__call__callStatic()方法内部,就可以根据所传入的两个参数做一些操作,这就可以与重载挂上勾了!

class Foo{
        public function __call($name,$arguments){

                print("你是想调用$name"."()方法吗?&nbsp;额...不好意思呦,该方法不可访问!<br/>");
        }
}

$foo=new Foo;

$foo->doStuff();

$foo->doStuff1();

输出:

你是想调用doStuff()方法吗? 额...不好意思呦,该方法不可访问!
你是想调用doStuff1()方法吗? 额...不好意思呦,该方法不可访问!

重载

/**
 *-------------------------------------------------------------
 *正是鉴于__call()或__callStatic()方法的这种特性,即所传入的$name和$arguments,它们就用来实现重载!这点与JS每一个普通方法中都可以获取到一个arguments数组其实异曲同工
 *举个例子
*/
class Foo1{
        public function __call($name,$arguments){
                if($name=="doStuff"){
                        /**
                         *实际上,不仅仅可以第一个参数类型,还可以判断参数个数,以及参数顺序,那么就和C++等强数据类型语言的重载效果是一样的了!
                        */
                        if(is_int($arguments[0])){
                                $this->doStuffForInt($arguments[0]);
                        }else if(is_string($arguments[0])){
                                $this->doStuffForString($arguments[0]);
                        }
                }
        }

        private function doStuffForInt($a){
                echo "执行的是doStuffForInt()方法";
        }

        private function doStuffForString($a){
                echo "执行的是doStuffForString()方法";
        }
}

$foo1=new Foo1;

$foo1->doStuff('1');