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 相关文章推荐
PHP 截取字符串 分别适合GB2312和UTF8编码情况
Feb 12 PHP
php模拟用户自动在qq空间发表文章的方法
Jan 07 PHP
PHP获取POST数据的几种方法汇总
Mar 03 PHP
PHP生成plist数据的方法
Jun 16 PHP
Yii2 rbac权限控制之菜单menu实例教程
Apr 28 PHP
php mysql 封装类实例代码
Sep 18 PHP
Laravel日志用法详解
Oct 09 PHP
使用PHP连接多种数据库的实现代码(mysql,access,sqlserver,Oracle)
Dec 21 PHP
PHP双向链表定义与用法示例
Jan 31 PHP
PHP+Session防止表单重复提交的解决方法
Apr 09 PHP
PHP里的$_GET数组介绍
Mar 22 PHP
php和html的区别点详细总结
Sep 24 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
咖啡语言
2021/03/03 咖啡文化
PHP 多维数组排序(usort,uasort)
2010/06/30 PHP
PHP之浮点数计算比较以及取整数不准确的解决办法
2015/07/29 PHP
深入解析WordPress中加载模板的get_template_part函数
2016/01/11 PHP
php自定义函数实现二维数组排序功能
2016/07/20 PHP
Laravel下生成验证码的类
2017/11/15 PHP
分享一个自定义的console类 让你不再纠结JS中的调试代码的兼容
2012/04/20 Javascript
JavaScript ES5标准中新增的Array方法
2016/06/28 Javascript
AngularJS入门教程之路由与多视图详解
2016/08/19 Javascript
微信小程序 教程之WXSS
2016/10/18 Javascript
Angular表单验证实例详解
2016/10/20 Javascript
javascript的document中的动态添加标签实现方法
2016/10/24 Javascript
AngularJS动态绑定HTML的方法分析
2016/11/07 Javascript
Vue-cli 使用json server在本地模拟请求数据的示例代码
2017/11/02 Javascript
vuejs实现标签选项卡动态更改css样式的方法
2018/05/31 Javascript
浅谈TypeScript的类型保护机制
2020/02/23 Javascript
vue+render+jsx实现可编辑动态多级表头table的实例代码
2020/04/01 Javascript
SpringBoot+Vue开发之Login校验规则、实现登录和重置事件
2020/10/19 Javascript
Python栈类实例分析
2015/06/15 Python
基于Python实现扑克牌面试题
2019/12/11 Python
如何在python中判断变量的类型
2020/07/29 Python
五分钟学会怎么用python做一个简单的贪吃蛇
2021/01/12 Python
canvas画布实现手写签名效果的示例代码
2019/04/23 HTML / CSS
谷歌浏览器小字体处理方案即12px以下字体
2013/12/17 HTML / CSS
Clarks西班牙官方在线商店:clarks鞋
2019/05/03 全球购物
Cecil Mode法国在线商店:女性时尚
2021/01/08 全球购物
护理专业个人求职简历的自我评价
2013/10/13 职场文书
公司承诺书范文
2014/05/19 职场文书
二人合伙经营协议书
2014/09/13 职场文书
单位法人授权委托书范本
2014/10/09 职场文书
博士给导师的自荐信
2015/03/06 职场文书
开天辟地观后感
2015/06/09 职场文书
2016天猫双十一广告语
2016/01/28 职场文书
导游词之台湾安平古堡
2019/12/25 职场文书
mysql优化
2021/04/06 MySQL
Spring Boot 实现敏感词及特殊字符过滤处理
2021/06/29 Java/Android