详解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 相关文章推荐
数据库相关问题
Oct 09 PHP
php 文件夹删除、php清除缓存程序
Aug 25 PHP
PHP 字符串加密函数(在指定时间内加密还原字符串,超时无法还原)
Apr 28 PHP
PHP中使用CURL伪造来路抓取页面或文件
May 04 PHP
php使用mb_check_encoding检查字符串在指定的编码里是否有效
Nov 07 PHP
PHP写日志的实现方法
Nov 05 PHP
PHP 前加at符合@的作用解析
Jul 31 PHP
PHP数据对象PDO操作技巧小结
Sep 27 PHP
ThinkPHP中create()方法自动验证实例
Apr 26 PHP
Laravel框架基于ajax实现二级联动功能示例
Jan 17 PHP
php解决约瑟夫环算法实例分析
Sep 30 PHP
PHP7 参数处理机制修改
Mar 09 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
SONY ICF-SW7600的电路分析
2021/03/02 无线电
Base64在线编码解码实现代码 演示与下载
2011/01/08 PHP
php遍历文件夹和文件列表示例分享
2014/03/11 PHP
Codeigniter中mkdir创建目录遇到权限问题和解决方法
2014/07/25 PHP
javascript 浏览器判断 绑定事件 arguments 转换数组 数组遍历
2009/07/06 Javascript
JavaScript写的一个自定义弹出式对话框代码
2010/01/17 Javascript
Jquery ajax传递复杂参数给WebService的实现代码
2011/08/08 Javascript
JavaScript/jQuery 表单美化插件小结
2012/02/14 Javascript
本地图片预览(支持IE6/IE7/IE8/Firefox3)经验总结
2013/03/25 Javascript
js过滤HTML标签以及空格的思路及代码
2013/05/24 Javascript
php读取sqlite数据库入门实例代码
2014/06/25 Javascript
JavaScript中对象介绍
2014/12/31 Javascript
使用npm发布Node.JS程序包教程
2015/03/02 Javascript
jquery实现无限分级横向导航菜单的方法
2015/03/12 Javascript
Angular外部使用js调用Angular控制器中的函数方法或变量用法示例
2016/08/05 Javascript
微信小程序实现图片预加载组件
2017/01/18 Javascript
Webpack实战加载SVG的方法
2017/12/26 Javascript
Angular实现的简单定时器功能示例
2017/12/28 Javascript
js实现图片实时时钟
2020/01/15 Javascript
[17:45]DOTA2 HEROES教学视频教你分分钟做大人-军团指挥官
2014/06/11 DOTA
[55:56]NB vs Infamous 2019国际邀请赛淘汰赛 败者组 BO3 第二场 8.22
2019/09/05 DOTA
在Django的视图中使用form对象的方法
2015/07/18 Python
使用Python编写Prometheus监控的方法
2018/10/15 Python
修改默认的pip版本为对应python2.7的方法
2018/11/06 Python
使用Python在Windows下获取USB PID&VID的方法
2019/07/02 Python
Python3爬虫中识别图形验证码的实例讲解
2020/07/30 Python
Annoushka英国官网:英国奢侈珠宝品牌
2018/10/20 全球购物
中专毕业生自荐信
2013/11/16 职场文书
公司运动会策划方案
2014/05/25 职场文书
公务员个人考察材料
2014/12/23 职场文书
求职简历自荐信怎么写
2015/03/26 职场文书
党员发展大会主持词
2015/07/03 职场文书
2016年暑假家长对孩子评语
2015/12/01 职场文书
2016优秀青年志愿者事迹材料
2016/02/25 职场文书
七年级作文之关于奶奶
2019/10/29 职场文书
Redis集群新增、删除节点以及动态增加内存的方法
2021/09/04 Redis