详解PHP序列化和反序列化原理


Posted in PHP onJanuary 15, 2018

0.前言

对象的序列化和反序列化作用就不再赘述,php中序列化的结果是一个php自定义的字符串格式,有点类似json.

我们在任何语言中设计对象的序列化和反序列化都需要解决几个问题

把某个对象序列化之后,序列化的结果有自描述的功能(从序列化的结果中知道这个对象的具体类型,

知道类型还不够,当然还需要知道这个类型所对应具体的值).

序列化时的权限控制,可以自定义序列化字段等,例如golang中的做的就非常方便.

时间性能问题:在某些性能敏感的场景下,对象序列化就不能拖后腿,例如:高性能服务(我经常使用protobuf来序列化).

空间性能问题:序列化之后的结果不能太长,比如内存中一个int对象,序列化之后数据长度变成了10倍int的长度,那这个序列化算法是有问题的.

本文仅仅从php代码角度来解释php中序列化和反序列化的过程.,记住一点序列化和反序列化操作的仅仅是对象的数据,这一点有面向对象开发经验的都应该容易理解.

1.序列化serialize和反序列化方法unserialize

php原生提供了对象序列化功能,不像c++ ……^_^. 用起来也非常简单,就两个接口.

class fobnn
{
 public $hack_id;
 private $hack_name;
 public function __construct($name,$id)
 {
  $this->hack_name = $name;
  $this->hack_id = $id;
 }
 public function print()
 {
  echo $this->hack_name.PHP_EOL;
 }
}
$obj = new fobnn('fobnn',1);
$obj->print();
$serializedstr = serialize($obj); //通过serialize接口序列化
echo $serializedstr.PHP_EOL;;
$toobj = unserialize($serializedstr);//通过unserialize反序列化
$toobj->print();
fobnn
O:5:"fobnn":2:{s:7:"hack_id";i:1;s:16:"fobnnhack_name";s:5:"fobnn";}
fobnn

看到第二行的输出,这个字符串就是序列化的结果,这个结构其实很容读懂,可以发现是通过对象名称/成员名称来映射的,当然不同访问权限的成员序列化之后的标签名称略有不同.

根据我上面讲到的3个问题,那么我们可以来看看

1.自描述功能

O:5:"fobnn":2 其中o就表示了object类型,且类型名称为fobnn, 采用这种格式,后面的2表示了有2个成员对象.

关于成员对象,其实也是同一套子描述,这是一个递归的定义.

自描述的功能主要是通过字符串记录对象和成员的名称来实现.

2.性能问题

php序列化的时间性能本文就不分析了,详见后面,但序列化结果其实类似json/bson定义的协议,有协议头,协议头说明了类型,协议体则说明了类型所对应的值,并不会对序列化结果进行压缩.

2.反序列化中的魔术方法

对应上述说的第二个问题,其实php中也有解决方法,一种是通过魔术方法,第二种则是自定义序列化函数.先来介绍下魔术方法 __sleep和__wakeup

class fobnn
{
 public $hack_id;
 private $hack_name;
 public function __construct($name,$id)
 {
  $this->hack_name = $name;
  $this->hack_id = $id;
 }
 public function print()
 {
  echo $this->hack_name.PHP_EOL;
 }
 public function __sleep()
 {
  return array("hack_name");
 }
 public function __wakeup()
 {
  $this->hack_name = 'haha';
 }
}
$obj = new fobnn('fobnn',1);
$obj->print();
$serializedstr = serialize($obj);
echo $serializedstr.PHP_EOL;;
$toobj = unserialize($serializedstr);
$toobj->print();
fobnn
O:5:"fobnn":1:{s:16:"fobnnhack_name";s:5:"fobnn";}
haha

在序列化之前会先调用__sleep返回的是一个需要序列化的成员名称数组,通过这样我们就可以控制需要序列化的数据,案例中我只返回了hack_name,可以看到结果中只序列化了hack_name成员.

在序列化完成之后,会跳用__wakeup 在这里我们可以做一些后续工作,例如重连数据库之类的.

3.自定义Serializable接口

interface Serializable {
abstract public string serialize ( void )
abstract public void unserialize ( string $serialized )
}

通过这个接口我们可以自定义序列化和反序列化的行为,这个功能主要可以用来自定义我们的序列化格式.

class fobnn implements Serializable
{
 public $hack_id;
 private $hack_name;
 public function __construct($name,$id)
 {
  $this->hack_name = $name;
  $this->hack_id = $id;
 }
 public function print()
 {
  echo $this->hack_name.PHP_EOL;
 }

 public function __sleep()
 {
  return array('hack_name');
 }

 public function __wakeup()
 {
  $this->hack_name = 'haha';
 }

 public function serialize()
 {
  return json_encode(array('id' => $this->hack_id ,'name'=>$this->hack_name ));
 }

 public function unserialize($var)
 {
  $array = json_decode($var,true);
  $this->hack_name = $array['name'];
  $this->hack_id = $array['id'];
 }
}
$obj = new fobnn('fobnn',1);
$obj->print();
$serializedstr = serialize($obj);
echo $serializedstr.PHP_EOL;;
$toobj = unserialize($serializedstr);
$toobj->print();
fobnn
C:5:"fobnn":23:{{"id":1,"name":"fobnn"}}
fobnn

当使用了自定义序列化接口之后,我们的魔术方法就没用了.

4.PHP动态类型和PHP反序列化

既然上文中提到的自描述功能,那么序列化结果中保存了对象的类型,且php是动态类型语言,那么我们就可以来做个简单的实验.

class fobnn
{
 public $hack_id;
 public $hack_name;
 public function __construct($name,$id)
 {
  $this->hack_name = $name;
  $this->hack_id = $id;
 }
 public function print()
 {
  var_dump($this->hack_name);
 }
}
$obj = new fobnn('fobnn',1);
$obj->print();
$serializedstr = serialize($obj);
echo $serializedstr.PHP_EOL;;
$toobj = unserialize($serializedstr);
$toobj->print();
$toobj2 = unserialize("O:5:\"fobnn\":2:{s:7:\"hack_id\";i:1;s:9:\"hack_name\";i:12345;}");
$toobj2->print();

我们修改hack_name反序列化的结果为int类型, i:12345

string(5) "fobnn"
O:5:"fobnn":2:{s:7:"hack_id";i:1;s:9:"hack_name";s:5:"fobnn";}
string(5) "fobnn"
int(12345)

可以发现,对象成功序列化回来了!并且可以正常工作!. 当然php的这种机制提供了灵活多变的语法,但也引入了安全风险. 后续继续分析php序列化和反序列化特性带来的安全问题.

以上就是我们整理的关于PHP序列化和反序列化原理的全部知识内容,感谢你对三水点靠木的支持。

PHP 相关文章推荐
3.从实例开始
Oct 09 PHP
一个自定义位数的php多用户计数器代码
Mar 11 PHP
由php if 想到的些问题
Mar 22 PHP
Discuz! Passport 通行证整合
Mar 27 PHP
PHP生成UTF8文件的方法
May 15 PHP
php学习笔记 PHP面向对象的程序设计
Jun 13 PHP
PHP输出时间差函数代码
Jan 28 PHP
常见PHP数据库解决方案分析介绍
Sep 24 PHP
PHP与jquery实时显示网站在线人数实例详解
Dec 02 PHP
thinkphp实现分页显示功能
Dec 03 PHP
PHP使用preg_split()分割特殊字符(元字符等)的方法分析
Feb 04 PHP
Laravel学习教程之IOC容器的介绍与用例
Aug 15 PHP
PHP使用两个栈实现队列功能的方法
Jan 15 #PHP
php获取ajax的headers方法与内容实例
Dec 27 #PHP
PHP实现求连续子数组最大和问题2种解决方法
Dec 26 #PHP
PHP基于双向链表与排序操作实现的会员排名功能示例
Dec 26 #PHP
thinkphp5 URL和路由的功能详解与实例
Dec 26 #PHP
php-msf源码详解
Dec 25 #PHP
关于 Laravel Redis 多个进程同时取队列问题详解
Dec 25 #PHP
You might like
在PHP中使用反射技术的架构插件使用说明
2010/05/18 PHP
php 判断数组是几维数组
2013/03/20 PHP
php中DOMElement操作xml文档实例演示
2013/03/26 PHP
ECshop 迁移到 PHP7版本时遇到的兼容性问题
2016/02/15 PHP
php cookie 详解使用实例
2016/11/03 PHP
php array_reverse 以相反的顺序返回数组实例代码
2017/04/11 PHP
laravel多条件查询方法(and,or嵌套查询)
2019/10/09 PHP
Dom 学习总结以及实例的使用介绍
2013/04/24 Javascript
使用js实现雪花飘落效果
2013/08/26 Javascript
浅析Node在构建超媒体API中的作用
2014/07/30 Javascript
js和jquery中循环的退出和继续下一个循环
2014/09/03 Javascript
jQuery对val和atrr("value")赋值的区别介绍
2014/09/26 Javascript
jQuery实现仿腾讯微博滑出效果报告每日天气的方法
2015/05/11 Javascript
jquery Easyui快速开发总结
2015/08/20 Javascript
jQuery仿360导航页图标拖动排序效果代码分享
2015/08/24 Javascript
javascript十六进制数字和ASCII字符之间的转换方法
2016/12/27 Javascript
原生JS实现图片网格式渐显、渐隐效果
2017/06/05 Javascript
基于JavaScript实现百度搜索框效果
2020/06/28 Javascript
使用express+multer实现node中的图片上传功能
2018/02/02 Javascript
Python监控主机是否存活并以邮件报警
2015/09/22 Python
浅谈Python 字符串格式化输出(format/printf)
2016/07/21 Python
Python正则表达式实现截取成对括号的方法
2017/01/06 Python
Python的argparse库使用详解
2018/10/09 Python
python操作小程序云数据库实现简单的增删改查功能
2019/06/06 Python
Python基础学习之基本数据结构详解【数字、字符串、列表、元组、集合、字典】
2019/06/18 Python
Django文件存储 自己定制存储系统解析
2019/08/02 Python
详解python 利用echarts画地图(热力图)(世界地图,省市地图,区县地图)
2019/08/06 Python
django中使用事务及接入支付宝支付功能
2019/09/15 Python
html5中canvas学习笔记1-画板的尺寸与实际显示尺寸
2013/01/06 HTML / CSS
WEB控件可以激发服务端事件,请谈谈服务端事件是怎么发生并解释其原理?自动传回是什么?为什么要使用自动传回?
2012/02/21 面试题
中学生校园广播稿
2014/01/16 职场文书
《兰亭集序》教学反思
2014/02/11 职场文书
业务部门经理岗位职责
2014/02/23 职场文书
学校学习雷锋活动总结
2014/07/03 职场文书
教育实践活动对照检查材料
2014/09/23 职场文书
2019年农民幸福观调查的实践感悟
2019/12/19 职场文书