對於魔術方法__call,__callStatic 新的認識

誤解的一般解釋

__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的老王】,轉載註明出處
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章