乐趣区

对于魔术方法callcallStatic-新的认识

误解的一般解释

__call方法在对象方法不存在的时候被调用

__callStatic方法在调用对象静态方法不存在的时候被调用

例如


class Car{public function __call($method,$params=[]){echo "car call\n";}
}

(new Car())->color();

class Bus{public static function __callStatic($method,$params=[]){echo "Bus callStatic\n";}
}

Bus::isSale();

特殊情况

其实上面的解释在某些情况下是正确的。但是在一些特殊情形,如果按照这个解释来理解,就会觉得结果不可思议了。

以下面几个例子进行说明。

__call 的调用关注的是方法能不能被访问

class Car{public function __call($method,$params=[]){echo "car call\n";}

    public function color(){echo "color red\n";}

    protected function isRed(){echo "yes is Red\n";}

    public function checkColor(){$this->color();
        $this->isRed();}
}
$car = new Car();
$car->color();
$car->isRed();

$car->checkColor();

输出的结果是

color red
car call isRed
color red
yes is Red

从上面可以看出,其实是否调用__call, 依赖的是当前调用方能否访问到要调用的函数,如果可以访问到,则直接调用函数,如果不能访问到,则调用魔术方法__call。所以,调用与否关注的是可访问性。

__callStatic 关注的是方法能否被静态的方式访问

接下来看另外一个静态调用的例子

class Car{public static function __callStatic($method,$params=[]){echo "car callStatic\n";}

    public function color(){echo "color red\n";}

    protected function isRed(){echo "yes is Red\n";}

    public function checkColor(){Car::color();
        Car::isRed();}
}

Car::color();
Car::isRed();
(new Car())->checkColor();

输出内容是

color red
car callStatic isRed
color red
yes is Red

并且在外部以静态方式调用 Car::color 伴有 Notice 级别错误提示,但是内部调用是没有的。

所以,__callStatic关注的是函数在调用位置能否被静态的方式访问到。如果能访问到,则直接执行该方法。如果不能则执行 __callStatic 方法

__call 与__callStatic 同时存在的情况

方法不可访问的时候,具体调用__call,__callStatic 方法,依据的并不是调用方式是否是静态调用,而是所在的上下文。如果上下文是在可访问调用对象的对象里,则调用__call,在静态上下文中调用一个不可访问方法时,调用__callStatic

class Car{public static function __callStatic($method,$params=[]){echo "car callStatic $method\n";}

    public  function __call($method,$params=[]){echo "car call $method\n";}

    public function checkColor(){Car::color();
        Car::isRed();}
}


$car = new Car();
Car::color();
Car::isRed();

$car->color();
$car->isRed();

(new Car())->checkColor();

输出内容是

car callStatic color
car callStatic isRed
car call color
car call isRed
car call color
car call isRed

从结果看出,外部静态调用 color,isRed 方法,上下文是静态方式,所以执行的是__callStatic

而在 checkColor 方法中,调用的上下文处于当前类对象 Car 当中,即使是以静态方式调用 color,isRed,最终执行的是__call 方法。

总结

1)__call方法关注方法能否被访问到,而不仅仅是关注是否存在

2)__callStatic方法关注的是方法能否被静态的访问到,而不是关注方法是否存在,是否是静态方法。

3)具体执行__call,__callStatic,是根据调用的上下文。如果处于静态上下文内,则调用__callStatic。如果处于对象的上线文内,则调用__call

文章首发于公众号【写 PHP 的老王】,转载注明出处

退出移动版