__call($method, $args) 在調用不可訪問的實例方法時觸發(方法不存在或權限受限),是實現動態代理、方法欄位映射、流式接口的核心機制。
class A {
public $name;
function test1($a) { echo "test1111"; }
function test2($a) { echo "test2222"; }
// 捕獲所有未定義方法調用
function __call($method, $args) {
echo "類中找不到方法:$method";
}
}
$obj = new A();
$obj->test(1); // 輸出:類中找不到方法:test
class ServiceProxy {
private $services = [];
public function __construct() {
$this->services = [
'user' => new UserService(),
'order' => new OrderService(),
];
}
public function __call($method, $args) {
// 解析 user:getInfo -> $this->services['user']->getInfo(...)
if (preg_match('/^(\w+):(\w+)$/', $method, $m)) {
[$_, $service, $action] = $m;
if (isset($this->services[$service])) {
return call_user_func_array([$this->services[$service], $action], $args);
}
}
throw new BadMethodCallException("Method $method not found");
}
}
$proxy = new ServiceProxy();
$proxy->user:getInfo(123); // 動態調用 UserService::getInfo(123)
$proxy->order:create(['a'=>1]); // 動態調用 OrderService::create(...)
class Facade {
public static function __callStatic($method, $args) {
$instance = Container::getInstance()->make(static::SERVICE_NAME);
return call_user_func_array([$instance, $method], $args);
}
}
class UserFacade extends Facade {
protected static $SERVICE_NAME = 'user';
}
UserFacade::find(1); // 實際調用 UserService::find(1)
這是 Laravel Facade 底層原理之一。
@method 註解生成提示。__call 內再次調用不存在方法會無限遞歸,需嚴格守衛條件。__call 只能是 public,且不影響 method_exists() 判斷(返回 false)。whereName() -> where('name', ...))$query->where->orderBy->get())善用 __call 能極大提升 API 表達力,但需在「動態便利」與「靜態可分析」間取得平衡。