误解的一般解释
__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的老王】,转载注明出处