誤解的一般解釋
__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的老王】,轉載註明出處