php魔术方法
一.__construct() – 构造函数
1.一、定义与触发条件
__construct() 是 PHP 类中最常见的魔术方法,在使用 new 关键字实例化对象时自动调用。它通常用于初始化对象的属性、建立数据库连接等。
触发 __construct() 的情况:
$obj = new ClassName();
$obj = new ClassName($arg1, $arg2);
反射实例化 (new ReflectionClass(‘ClassName’))->newInstance();
注意:unserialize() 不会触发 __construct()(反序列化时触发的是 __wakeup())。
2.代码示例:
1 | class Database { |
二.__destruct() – 析构函数
1.定义与触发条件
当对象的引用计数归零、脚本执行结束、或显式调用 unset($obj) 时,PHP 会调用 __destruct()。它用于释放资源(关闭文件、断开连接等)。
触发 __destruct() 的情况:
脚本结束时所有仍存活的对象。
unset($obj) / $obj = null。
对象超出作用域(例如函数返回时局部变量销毁)。
重要:即使反序列化过程抛出异常,__destruct() 仍会在对象销毁时执行。
2.代码示例;
1 | class TempFile { |
三. __wakeup() – 反序列化唤醒
1.定义与触发条件
__wakeup() 是专门为 unserialize() 设计的魔术方法,在反序列化操作恢复对象的所有属性之后、返回对象之前自动调用。常用于重建数据库连接、初始化缓存等。
触发条件:
仅当调用 unserialize() 且对象所属的类定义了 __wakeup() 时触发。
PHP 5.6.0 ~ 7.0.0 存在绕过漏洞:当序列化字符串中声明的属性数量大于实际属性数量时,__wakeup() 不会被调用(CVE-2016-7124)。PHP 7.4+ 已修复,但老旧环境仍受影响。
1 | class Connection { |
四.__sleep() – 序列化前清理
1.定义与触发条件
__sleep() 在调用 serialize() 函数时自动触发,执行完毕后必须返回一个包含需要序列化的属性名称的数组。通常用于清理不需要保存的资源(如数据库连接)。
触发条件:
- 仅当调用 serialize($obj) 且类定义了 __sleep() 时触发。
1 | class User { |
五.__toString() – 字符串化陷阱
1.定义与触发条件
当对象被当作字符串使用时,PHP 会自动调用 __toString() 方法。
常见触发场景:
echo $obj; / print $obj;
$str = “前缀” . $obj;
$len = strlen($obj);
printf(“%s”, $obj);
$obj . ‘’(字符串拼接)
触发条件:
对象被隐式或显式转换为字符串。
必须返回一个字符串,否则抛出致命错误。
2.代码示例:
1 | class FileReader { |
六.__invoke() – 对象函数化
1.定义与触发条件
当对象被当作函数调用时,PHP 会执行 __invoke() 方法。
触发条件:
$obj($arg1, $arg2, …);
call_user_func($obj, $arg1);
任何将对象作为回调函数使用的场景。
2.代码示例:
1 | class Executor { |
七. __call() 和 __callStatic() – 方法拦截
1.定义与触发条件
__call($method, $args):当调用一个对象中不存在或不可访问的实例方法时触发。
__callStatic($method, $args):当以静态方式调用一个类中不存在或不可访问的静态方法时触发。
触发条件示例:
1 | $obj->unknownMethod($a, $b); // 触发 __call |
2.代码示例:
1 | class Router { |
八. __get() 和 __set() – 属性访问拦截
1.定义与触发条件
__get($name):当读取不可访问的属性(未定义、private、protected)时触发。
__set($name, $value):当给不可访问的属性赋值时触发。
触发条件:
1 | $obj->unknownProp; // 触发 __get |
2.代码示例:
1 | class DataContainer { |
九. __unset() – 属性销毁拦截
1.定义与触发条件
- 当对不可访问的属性使用 unset() 时,PHP 会调用 __unset($name)。
触发条件:
1 | unset($obj->unknownProp); // 触发 __unset |
2.代码示例:
1 | class FileHandler { |
十. __clone() – 克隆拦截
一、定义与触发条件
- 当使用 clone $obj 复制对象时,新对象创建完成后会调用 __clone() 方法。
2.代码示例:
1 | class Singleton { |
十一. __debugInfo() – 调试输出拦截
一、定义与触发条件
- 当使用 var_dump($obj) 或 print_r($obj) 打印对象时,若类定义了 __debugInfo(),PHP 会调用它并显示返回的数组,而不是对象的全部属性。
2.代码示例:
1 | class User { |
十二. __isset() 魔术方法详解
1.定义与触发条件
__isset() 是 PHP 中用于属性重载(Property Overloading)的魔术方法。当对一个不可访问的属性调用 isset() 或 empty() 函数时,PHP 会自动调用该方法。
触发 __isset() 的几种情况(满足任意一条即会触发):
| 属性状态 | 示例 | 是否触发 __isset() |
|---|---|---|
| 属性未定义 | $obj->foobar | ✅ 触发 |
| 属性为 private / protected,从类外部访问 | $obj->privateProp | ✅ 触发 |
| 属性为 public 且存在(即使值为 null) | $obj->publicProp | ❌ 不触发 |
| 属性为 public 但直接调用 empty() 且属性存在 | empty($obj->publicProp) | ❌ 不触发(因为属性可访问,empty() 直接读取属性值判断) |
关键点:__isset() 只在属性本身不可见或不存在时才介入。如果属性是 public 且已定义,PHP 会直接访问该属性,不会调用 __isset()。
2.代码示例:
1 | class User { |
