误解的一般解释

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

写PHP的老王
593 声望8 粉丝