__get 管“读属性”,__call 管“调方法”;二者均仅在访问失败时触发,且严格区分可见性——public 成员永远不触发它们。

__call 和 __get 是 PHP 中两个独立的运行时拦截机制,分别用于处理“方法调用失败”和“属性读取失败”的场景,不是通用代理工具,而是有明确触发前提的兜底逻辑。
__call:只在调用不可访问的实例方法时触发
它不处理静态方法(那是 __callStatic 的事),也不转发所有方法——必须确保目标方法真实存在且可调用,否则容易引发死循环或致命错误。
- 调用条件:对象上执行
$obj->xxx(),而xxx不是 public 方法,或根本不存在 - 必须校验:转发前用
method_exists($this->delegate, $name)或is_callable([$this->delegate, $name])判断,不能只检查对象是否有效 - 避免陷阱:不要在 __call 内部再调用本类其他未定义方法(比如
$this->helper()),否则会二次触发 __call,最终栈溢出 - 推荐写法:PHP 5.6+ 用
return $this->delegate->$name(...$arguments),保留错误堆栈和类型提示信息
__get:只对 private/protected 或完全不存在的属性读取生效
public 属性访问完全绕过 __get,这是设计行为,不是 bug。它不参与赋值操作(那是 __set 的职责),也不能返回引用。
- 触发时机:执行
echo $obj->host时,$host是 private/protected,或类中压根没声明该属性 - 典型用法:把数据存进 private 数组(如
private $data = [];),在 __get 里查键值,实现配置懒加载或字段映射 - 禁止引用赋值:
&$v = $obj->prop会直接报 Fatal error,因为 __get 返回的是副本,不是存储地址 - 注意性能:高频读取场景下,__get 比直接属性访问慢 3–5 倍,别在循环里反复读同一个 key
两者关键区别一句话说清
__get 管“读属性”,__call 管“调方法”;都只在访问失败时才介入,且严格区分可见性——public 成员永远不触发它们。
立即学习“PHP免费学习笔记(深入)”;



















