PHP序列化的四种实现方法与横向对比


Posted in PHP onNovember 29, 2018

一、PHP 序列化变量的 4 种方法

序列化是将变量转换为可保存或传输的字符串的过程;反序列化就是在适当的时候把这个字符串再转化成原来的变量使用。这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性。

1. serialize和unserialize函数

这两个是序列化和反序列化PHP中数据的常用函数。

$a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');

//序列化数组
$s = serialize($a);
echo $s;
//输出结果:a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";}

echo '<br /><br />';

//反序列化
$o = unserialize($s);

print_r($o);

当数组值包含如双引号、单引号或冒号等字符时,它们被反序列化后,可能会出现问题。为了克服这个问题,一个巧妙的技巧是使用base64_encode和base64_decode。

$obj = array();
//序列化
$s = base64_encode(serialize($obj)); 
//反序列化
$original = unserialize(base64_decode($s));

但是base64编码将增加字符串的长度。为了克服这个问题,可以和gzcompress一起使用。

//定义一个用来序列化对象的函数

function my_serialize( $obj ) 
{ 
  return base64_encode(gzcompress(serialize($obj))); 
} 

//反序列化
function my_unserialize($txt) 
{ 
  return unserialize(gzuncompress(base64_decode($txt))); 
}

2. json_encode 和 json_decode

使用JSON格式序列化和反序列化是一个不错的选择:

  • 使用json_encode和json_decode格式输出要serialize和unserialize格式快得多。
  • JSON格式是可读的。
  • JSON格式比serialize返回数据结果小。
  • JSON格式是开放的、可移植的。其他语言也可以使用它。
$a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');

//序列化数组
$s = json_encode($a);
echo $s;
//输出结果:{"a":"Apple","b":"banana","c":"Coconut"}

echo '<br /><br />';

//反序列化
$o = json_decode($s);

在上面的例子中,json_encode输出长度比上个例子中serialize输出长度显然要短。

3. var_export 和 eval

var_export 函数把变量作为一个字符串输出;eval把字符串当成PHP代码来执行,反序列化得到最初变量的内容。

$a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');

//序列化数组
$s = var_export($a , true);
echo $s;
//输出结果: array ( 'a' => 'Apple', 'b' => 'banana', 'c' => 'Coconut', )

echo '<br /><br />';

//反序列化
eval('$my_var=' . $s . ';');

print_r($my_var);

4. wddx_serialize_value 和 wddx deserialize

wddx_serialize_value函数可以序列化数组变量,并以XML字符串形式输出。

$a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');

//序列化数组
$s = wddx_serialize_value($a);
echo $s;

//输出结果(查看输出字符串的源码):<wddxPacket version='1.0'><header/><data><struct><var name='a'><string>Apple</string></var><var name='b'><string>banana</string></var><var name='c'><string>Coconut</string></var></struct></data></wddxPacket>

echo '<br /><br />';

//反序列化
$o = wddx_deserialize($s);

print_r($o);
//输出结果:Array ( [a] => Apple [b] => banana 1 => Coconut )

可以看出,XML标签字符较多,导致这种格式的序列化还是占了很多空间。

结论

上述所有的函数在序列化数组变量时都能正常执行,但运用到对象就不同了。例如json_encode序列化对象就会失败。反序列化对象时,unserialize和eval将有不同的效果。

英文原文:3 ways to serialize variables in php

二、PHP四种序列化方案横向对比

数据的序列化是一个非常有用的功能,然而目测很多人跟我一样,在刚接触这玩意的时候压根就不理解这货色到底是干啥用的,反正老师说了,实在理解不了就先背过再说。

其实将数据序列化的作用无外乎有两个:

  • 方便传输
  • 方便存储

方便存储如何理解呢?比如我们有个PHP对象或者一个PHP数组需要存储到数据库甚至文件中,这显然是不可能的,这个时候必须要将PHP对象或者PHP数组序列化后再执行存储操作。不过这将PHP数组序列化后存起来还能理解,这对象也能存储啊?这操作是否过于风骚?少年,这一点儿都不风骚。有些时候将对象直接存储起来,用的时候只需要简单的反序列化后就可以投产使用了,避免了new一次带来的性能耗费。

方便传输如何理解呢?其实序列化在传输中应用的相对更多更常见些许。最简单的一个例子,一个码前端的码了一个ajax找你给TA提供一个API,那么这个时候你俩得商量返回什么数据,比如json或者xml,甚至你俩自己作死约定私有数据格式。比如在一个比较典型的服务架构中,网关服务器和内部RPC服务器之间通过msgpack传递数据。这都是典型的序列化为了传输的典型应用案例。

这里序列化的概念可能更为广泛和笼统一些,包括传统的serialize、json、msgpack、protobuf等。( 如果你觉得序列化这个称呼不太严谨的话,可以用encode来代替;反序列化则用decode来代替。反正我就用统统用序列化和反序列化来称呼了,如果你觉得实在不舒服,可以顺着网线来砍我!)。

实际上,从更高的层面看,数据的序列化可以分为两种:

  • 文本序列化,常见如json、serialize、xml等
  • 二进制序列化,常见如msgpack、protobuf、thrift等

一般说来,考验序列化技术的性能指标一共有两个,一个是序列化的速度,一个是序列化后数据的大小,自然是序列化速度越快、序列化后的数据越小为佳。就目前来看,protobuf、msgpack等二进制序列化无论是速度上还是数据大小上,都要比文本序列化更好。不过话说回来,文本序列化有更好的可读性,一眼就能瞪出来数据内容大概是啥玩意。

今天带到这里的这里的有四个具体的方案,这四种方案都是简单粗暴、开箱即用类型的,我们分别测试感受下,看哪个更适合我们。

参会的四个哥们:PHP内置的serialize、PHP内置的JSON解析器、PHP扩展JSOND、PHP扩展msgpack。其中前三个都是文本类型的,msgpack则是二进制类型的。

JSOND作为PHP内置的JSON解析器的高级版本,坊间一直传闻速度上要比内置的更牛X一些,作为扩展,这货需要额外安装,附送地址:https://pecl.php.net/get/jsond-1.4.0.tgz。

msgpack是一个鸟哥等人搞的一套二进制序列化工具,slogan就是“It's like JSON.but fast and small.”,附送地址:https://pecl.php.net/get/msgpack-2.0.2.tgz

PHP序列化的四种实现方法与横向对比

1、serialize用法
serialize(),序列化方法。
unserialize(),反序列化方法。

2、json用法
json_encode(),没啥好说的吧?
json_decode(),没啥好说的吧?

3、jsond用法
jsond_encode(),和json_encode()一样,后面多个字母d而已。
jsond_decode(),和json_decode()一样,后面多个字母d而已。

4、msgpack用法
msgpack_pack(),序列化方法。
msgpack_unpack(),反序列化方法。

测试代码如下:

<?php
// 故意搞了一个还算大的php数组,更容易看出差距来
$arr = array(
 array(
 'uid' => 22193123,
 'gender' => 'famale',
 'username' => 'elarity',
 'password' => md5('www123'),
 'relation' => array(
 array(
 'uid' => 22193123,
 'gender' => 'famale',
 'username' => 'elarity',
 'password' => md5('www123'),
 ),
 array(
 'uid' => 22193123,
 'gender' => 'famale',
 'username' => 'elarity',
 'password' => md5('www123'),
 ),
 array(
 'uid' => 22193123,
 'gender' => 'famale',
 'username' => 'elarity',
 'password' => md5('www123'),
 ),
 array(
 'uid' => 22193123,
 'gender' => 'famale',
 'username' => 'elarity',
 'password' => md5('www123'),
 ),
 array(
 'uid' => 22193123,
 'gender' => 'famale',
 'username' => 'elarity',
 'password' => md5('www123'),
 ),
 array(
 'uid' => 22193123,
 'gender' => 'famale',
 'username' => 'elarity',
 'password' => md5('www123'),
 ),
 array(
 'uid' => 22193123,
 'gender' => 'famale',
 'username' => 'elarity',
 'password' => md5('www123'),
 ),
 array(
 'uid' => 22193123,
 'gender' => 'famale',
 'username' => 'elarity',
 'password' => md5('www123'),
 ),
 array(
 'uid' => 22193123,
 'gender' => 'famale',
 'username' => 'elarity',
 'password' => md5('www123'),
 ),
 ),
 )
);

// 每种序列化方案都执行100000次
$counter = 100000;

// json序列化方案,执行100000次
echo PHP_EOL.PHP_EOL;
$start = microtime( true );
for( $i = 1; $i <= $counter; $i++ ){
 $json = json_encode( $arr ); 
}
$size = strlen( $json );
$end = microtime( true );
$cost_time = $end - $start;
echo "json_encode : 耗费时间为{$cost_time} , 数据体积为{$size}".PHP_EOL;

// jsond序列化方案,执行100000次
$start = microtime( true );
for( $i = 1; $i <= $counter; $i++ ){
 $jsond = jsond_encode( $arr ); 
}
$size = strlen( $jsond );
$end = microtime( true );
$cost_time = $end - $start;
echo "jsond_encode : 耗费时间为{$cost_time} , 数据体积为{$size}".PHP_EOL;

// serialize序列化方案,执行100000次
$start = microtime( true );
for( $i = 1; $i <= $counter; $i++ ){
 $serialize = serialize( $arr ); 
}
$size = strlen( $serialize );
$end = microtime( true );
$cost_time = $end - $start;
echo "serialize : 耗费时间为{$cost_time} , 数据体积为{$size}".PHP_EOL;

// msgpack序列化方案,执行100000次
$start = microtime( true );
for( $i = 1; $i <= $counter; $i++ ){
 $msgpack = msgpack_pack( $arr );
}
$size = strlen( $msgpack );
$end = microtime( true );
$cost_time = $end - $start;
echo "msgpack耗费时间为 : {$cost_time} , 数据体积为{$size}".PHP_EOL;
echo PHP_EOL.PHP_EOL;

将文件保存为test.php,然后php test.php执行,结果如下图所示:

PHP序列化的四种实现方法与横向对比

总结一下:

  1. jsond确实是要比json快一些的
  2. 总有刁民张嘴就来json要比serialize()快
  3. serialize()数据体积确实大(因为还保留了数据类型说明)
  4. msgpack最佳???不知道昂,你们自己感受
PHP 相关文章推荐
mysql 字段类型说明
Apr 27 PHP
PHP读取PDF内容配合Xpdf的使用
Nov 24 PHP
PHP连接MySQL查询结果中文显示乱码解决方法
Oct 25 PHP
ThinkPHP打开验证码页面显示乱码的解决方法
Dec 18 PHP
WordPress中创建用户角色的相关PHP函数使用详解
Dec 25 PHP
php从身份证获取性别和出生年月
Feb 09 PHP
Zend Framework框架中实现Ajax的方法示例
Jun 27 PHP
PHP微信公众号开发之微信红包实现方法分析
Jul 14 PHP
PHP实现网站访问量计数器
Oct 27 PHP
php从数据库中获取数据用ajax传送到前台的方法
Aug 20 PHP
laravel 多图上传及图片的存储例子
Oct 14 PHP
Mac M1安装mnmp (Mac+Nginx+MySQL+PHP) 开发环境
Mar 29 PHP
PHP中如何使用Redis接管文件存储Session详解
Nov 28 #PHP
php基于Redis消息队列实现的消息推送的方法
Nov 28 #PHP
php获取用户真实IP和防刷机制的实例代码
Nov 28 #PHP
PHP实现小程序批量通知推送
Nov 27 #PHP
Laravel学习笔记之Artisan命令生成自定义模板的方法
Nov 22 #PHP
关于PHP虚拟主机概念及如何选择稳定的PHP虚拟主机
Nov 20 #PHP
phpMyAdmin通过密码漏洞留后门文件
Nov 20 #PHP
You might like
常见的5个PHP编码小陋习以及优化实例讲解
2021/02/27 PHP
picChange 图片切换特效的函数代码
2010/05/06 Javascript
发布一个基于javascript的动画类 Fx.js
2010/11/05 Javascript
javascript复制对象使用说明
2011/06/28 Javascript
createElement与createDocumentFragment的点点区别小结
2011/12/19 Javascript
jquery实现盒子下拉效果示例代码
2013/09/12 Javascript
jquery ajax请求方式与提示用户正在处理请稍等
2014/09/01 Javascript
Javascript学习笔记之函数篇(六) : 作用域与命名空间
2014/11/23 Javascript
基于BootStarp的Dailog
2016/04/28 Javascript
浅谈jQuery 选择器和dom操作
2016/06/07 Javascript
用nodeJS搭建本地文件服务器的几种方法小结
2017/03/16 NodeJs
Angular4学习笔记之新建项目的方法
2017/07/18 Javascript
微信小程序 获取javascript 里的数据
2017/08/17 Javascript
webpack+vue中使用别名路径引用静态图片地址
2017/11/20 Javascript
Vue中this.$router.push参数获取方法
2018/02/27 Javascript
Element中Slider滑块的具体使用
2020/07/29 Javascript
python正常时间和unix时间戳相互转换的方法
2015/04/23 Python
Python基于回溯法子集树模板解决选排问题示例
2017/09/07 Python
对变量赋值的理解--Pyton中让两个值互换的实现方法
2017/11/29 Python
对Python中DataFrame选择某列值为XX的行实例详解
2019/01/29 Python
详解将Python程序(.py)转换为Windows可执行文件(.exe)
2019/07/19 Python
python中property属性的介绍及其应用详解
2019/08/29 Python
Python 内置函数globals()和locals()对比详解
2019/12/23 Python
python清空命令行方式
2020/01/13 Python
Python 文件数据读写的具体实现
2020/01/24 Python
Python ini文件常用操作方法解析
2020/04/26 Python
基于python3.7利用Motor来异步读写Mongodb提高效率(推荐)
2020/04/29 Python
HTML5离线缓存在tomcat下部署可实现图片flash等离线浏览
2012/12/13 HTML / CSS
固特异美国在线轮胎店:Goodyear Tire
2019/02/23 全球购物
电脑教师的自我评价
2013/12/18 职场文书
应用英语专业自荐信
2014/01/26 职场文书
高二物理教学反思
2014/02/08 职场文书
开学典礼演讲稿
2014/05/23 职场文书
竞选班长演讲稿500字
2014/08/22 职场文书
赔偿协议书范本
2014/09/12 职场文书
什么是css原子化,有什么用?
2022/04/24 HTML / CSS