反序列化魔术方法小汇总 作者: ynnddddd 时间: 2025-02-24 分类: 学业,网络安全,反序列化 总览 属性类型小插曲 public属性是公有属性,可以在类外被调用修改。 private属性是私有属性,只能在类中调用与修改。 以下是PHP魔术方法的总结表格: | 魔术方法 | 触发条件 | 作用 | 案例说明 | 案例结果示例 | |-----------------|------------------------------------------|----------------------------------------------------------------------|--------------------------------------------------------------------------|-----------------------------------------------------------------------------| | __construct | 类实例化时 | 初始化对象属性,常用于设置初始值 | 实例化时设置`name`属性并输出提示信息 | "类执行前会执行__construct"输出设置的`name`属性值 | | __destruct | 对象销毁或脚本执行结束时 | 清理资源,如关闭文件或数据库连接 | 对象销毁时输出提示信息 | "类结束之后会执行__destruct" | | __sleep | 对象被序列化(`serialize()`)时 | 控制序列化包含的属性,返回需要序列化的属性数组 | 序列化时修改`name`属性并输出提示 | "当类被序列化时__sleep就会被执行"属性值被修改为"张三" | | __wakeup | 对象被反序列化(`unserialize()`)时 | 重新初始化资源(如数据库连接) | 反序列化时重置`name`属性并输出提示 | "当序列被反序列化时__wakeup就会被执行"属性值恢复为"张三" | | __invoke | 对象被当作函数调用(`$obj()`)时 | 实现对象可调用逻辑 | 调用未定义的函数时触发 | "__invoke函数被执行" | | __toString | 对象被当作字符串处理时 | 定义对象的字符串表示形式 | 输出对象时触发 | "__toString被执行" | | __call | 调用不存在或不可访问的方法时 | 处理未知方法调用 | 调用未定义的`hello()`方法 | "很明显木得hello方法,以及参数也没有,所以__CALL被调用了" | | __get | 读取不存在或不可访问的属性时 | 动态获取属性值 | 访问未定义的`aaa`属性 | "没有aaa这个属性,所以执行了__get" | | __set | 给不存在或不可访问的属性赋值时 | 动态设置属性值 | 给未定义的`aaa`属性赋值 | "没有aaa""记录你设置的值为:111" | | __isset | 对不存在或私有属性使用`isset()`/`empty()` | 控制属性存在性检查行为 | 检查未定义的`b`属性和私有的`age`属性 | "没有b 或者为私有""没有age 或者为私有" | | __unset | 对不存在或私有属性使用`unset()`时 | 控制属性销毁行为 | 尝试销毁私有的`age`属性 | "没有age 或者为私有" | ### 特性说明: 1. **访问控制**:private属性相关魔术方法(__get/__set/__isset/__unset)在外部操作时触发 2. **序列化控制**:__sleep必须返回包含允许序列化属性名的数组 3. **方法重载**:__call处理未定义方法调用,__callStatic处理静态方法 4. **类型转换**:__toString定义对象转字符串的行为,需返回字符串 5. **执行时机**: - __construct/destruct 生命周期相关 - __sleep/wakeup 序列化流程相关 - __invoke/toSting 类型转换相关 6. **错误处理**:相关魔术方法可避免直接报错,实现更优雅的错误处理 ### 典型应用场景: - 对象持久化(__sleep保存必要属性,__wakeup重建连接) - 动态属性管理(__get/__set实现属性代理) - 接口兼容(__toString使对象支持字符串操作) - 方法重载(__call实现动态方法派发) - 资源管理(__destruct自动释放资源) 以下是**完整的魔术方法示例程序与预期运行结果**说明: ### 1. __construct & __destruct ```php name = $name; echo "【构造】对象已创建,名字设置为:$name\n"; } public function __destruct() { echo "【析构】对象 {$this->name} 被销毁\n"; } } $obj = new Lifecycle('测试对象'); unset($obj); // 显式销毁对象 ?> ``` **预期结果**: ``` 【构造】对象已创建,名字设置为:测试对象 【析构】对象 测试对象 被销毁 ``` ### 2. __sleep & __wakeup ```php secret = '已重置'; } } $obj = new Serializer(); $serialized = serialize($obj); $restored = unserialize($serialized); ?> ``` **预期结果**: ``` 【序列化】正在清理敏感数据 【反序列化】重新初始化 ``` ### 3. __invoke ```php ``` **预期结果**: ``` 【调用】对象被作为函数调用,参数:测试参数 ``` ### 4. __toString ```php ``` **预期结果**: ``` 【字符串转换】对象ID:000000003b3d3e6f0000000077c5a774 (实际哈希值会变化) ``` ### 5. __call & __callStatic ```php undefinedMethod(); MethodHandler::undefinedStaticMethod(); ?> ``` **预期结果**: ``` 【方法拦截】尝试调用不存在的方法:undefinedMethod 【静态方法拦截】尝试调用不存在的静态方法:undefinedStaticMethod ``` ### 6. __get & __set ```php data[$name] ?? null; } public function __set($name, $value) { echo "【属性设置】设置不存在的属性:$name = $value\n"; $this->data[$name] = $value; } } $obj = new PropertyHandler(); $obj->newProperty = 100; echo $obj->newProperty . "\n"; ?> ``` **预期结果**: ``` 【属性设置】设置不存在的属性:newProperty = 100 【属性读取】访问不存在的属性:newProperty 100 ``` ### 7. __isset & __unset ```php $name); } public function __unset($name) { echo "【属性销毁】尝试删除不存在的属性:$name\n"; } } $obj = new PropertyChecker(); isset($obj->undefined); unset($obj->undefined); ?> ``` **预期结果**: ``` 【存在检查】检测不存在的属性:undefined 【属性销毁】尝试删除不存在的属性:undefined ``` ### 注意事项 1. 析构函数触发需要满足以下条件之一: - 脚本执行结束 - 对象被显式销毁(unset) - 对象引用计数归零 2. 序列化示例中私有属性`secret`不会被保留 3. __toString必须返回字符串,不能直接输出 4. 静态方法拦截需要使用`__callStatic`声明为静态方法**** 标签: none
选材新颖独特,通过细节描写赋予主题鲜活生命力。