详解PHP数据压缩、加解密(pack, unpack)


Posted in PHP onDecember 17, 2016

网络通信、文件存储中经常需要交换数据,为了减少网络通信流量、文件存储大小以及加密通信规则,经常需要对数据进行双向加解密以保证数据的安全。

PHP中实现此功能主要需要使用的函数主要是pack及unpack函数

pack

压缩资料到位字符串之中。

语法: string pack(string format, mixed [args]...);

返回值: 字符串

本函数用来将资料压缩打包到位的字符串之中。

a - NUL- 字符串填满[padded string] 将字符串空白以 NULL 字符填满

A - SPACE- 字符串填满[padded string]

h ? 十六进制字符串,低“四位元”[low nibble first] (低位在前)

H - 十六进制字符串,高“四位元”[high nibble first](高位在前)

c ? 带有符号的字符

C ? 不带有符号的字符

s ? 带有符号的短模式[short](通常是16位,按机器字节顺序)

S ? 不带有符号的短模式[short](通常是16位,按机器字节排序)

n -不带有符号的短模式[short](通常是16位,按大endian字节排序)

v -不带有符号的短模式[short](通常是16位,按小endian字节排序)

i ? 带有符号的整数(由大小和字节顺序决定)

I ? 不带有符号的整数(由大小和字节顺序决定)

l? 带有符号的长模式[long](通常是32位,按机器字节顺序)

L ? 不带有符号的长模式[long](通常是32位,按机器字节顺序)

N ? 不带有符号的长模式[long](通常是32位,按大edian字节顺序)

V? 不带有符号的长模式[long](通常是32位,按小edian字节顺序)

f ?浮点(由大小和字节顺序决定)

d ? 双精度(由大小和字节顺序决定)

x ? 空字节[NUL byte]

X- 后面一个字节[Back up one byte](倒回一位)

unpack

解压缩位字符串资料。

语法: string pack(string format, mixed [args]...);

返回值: 数组

本函数用来将位的字符串的资料解压缩。本函数和 Perl 的同名函数功能用法完全相同。

案例一、pack实现缩减文件数据存储大小

<?php 
//存储整数1234567890 
file_put_contents("test.txt", 1234567890);

此时test.txt的文件大小是10byte。注意此时文件大小是10字节,实际占用空间大小是1KB。

上面存储的整数实际是以字符串形式存储于文件test.txt中。

但如果以整数的二进制字符串存jy储,将会缩减至4byte。

<?php 
print_r(unpack("i", file_get_contents("test.txt")));

案例二、数据加密

以字符串形式存储一段有意义数据,7-110-abcdefg-117。

字符"-"分割后,第一位表示字符串长度,第二位表示存储位置,第三位表示实际存储的字符串,第四位表示结尾位置。

<?php 
file_put_contents("test.txt", "7-110-abcdefg-117");

上述方法缺点:

一、数据存储大小

二、数据以明文方式存储,如果是任何敏感信息,都可能造成不安全访问。

三、文件存储大小,以不规则方式递增。

加密:

<?php 
file_put_contents("test.txt", pack("i2a7i1", 7, 110, "abcdefg", 117));

存储一段数据,加密格式为:整数2位长度字符串10位长度整数1位长度。

优点:

一、数据大小最优化

二、在不知道"i2a7i1"这样的压缩格式时,即使拿到文件,也无法正确读出二进制文件转化为明文。

三、数据增加时,文件存储大小是等量递增。每次都是以19byte递增。

案例三、key-value型文件存储

存储生成的文件为两个:索引文件,数据文件

文件中数据存储的格式如下图:

详解PHP数据压缩、加解密(pack, unpack)

代码实现:

<?php 
error_reporting(E_ALL); 
 
class fileCacheException extends Exception{ 
 
} 
 
//Key-Value型文件存储 
class fileCache{ 
   private $_file_header_size = 14; 
   private $_file_index_name; 
   private $_file_data_name; 
   private $_file_index;//索引文件句柄 
   private $_file_data;//数据文件句柄 
   private $_node_struct;//索引结点结构体 
   private $_inx_node_size = 36;//索引结点大小 
 
   public function __construct($file_index="filecache_index.dat", $file_data="filecache_data.dat"){ 
     $this->_node_struct = array( 
        'next'=>array(1, 'V'), 
        'prev'=>array(1, 'V'), 
       'data_offset'=>array(1,'V'),//数据存储起始位置 
       'data_size'=>array(1,'V'),//数据长度 
       'ref_count'=>array(1,'V'),//引用此处,模仿PHP的引用计数销毁模式 
       'key'=>array(16,'H*'),//存储KEY 
     ); 
 
     $this->_file_index_name = $file_index; 
     $this->_file_data_name = $file_data; 
 
     if(!file_exists($this->_file_index_name)){ 
        $this->_create_index(); 
     }else{ 
        $this->_file_index = fopen($this->_file_index_name, "rb+"); 
     } 
 
     if(!file_exists($this->_file_data_name)){ 
        $this->_create_data(); 
     }else{ 
        $this->_file_data = fopen($this->_file_data_name, "rb+");//二进制存储需要使用b 
     } 
   } 
 
   //创建索引文件 
   private function _create_index(){ 
     $this->_file_index = fopen($this->_file_index_name, "wb+");//二进制存储需要使用b 
     if(!$this->_file_index)  
        throw new fileCacheException("Could't open index file:".$this->_file_index_name); 
 
     $this->_index_puts(0, '<'.'?php exit()?'.'>');//定位文件流至起始位置0, 放置php标记防止下载 
     $this->_index_puts($this->_file_header_size, pack("V1", 0)); 
   } 
 
 
   //创建存储文件 
   private function _create_data(){ 
     $this->_file_data = fopen($this->_file_data_name, "wb+");//二进制存储需要使用b 
     if(!$this->_file_index)  
        throw new fileCacheException("Could't open index file:".$this->_file_data_name); 
 
     $this->_data_puts(0, '<'.'?php exit()?'.'>');//定位文件流至起始位置0, 放置php标记防止下载 
   } 
 
   private function _index_puts($offset, $data, $length=false){ 
     fseek($this->_file_index, $offset); 
 
     if($length) 
     fputs($this->_file_index, $data, $length); 
     else 
     fputs($this->_file_index, $data); 
   } 
 
   private function _data_puts($offset, $data, $length=false){ 
     fseek($this->_file_data, $offset); 
     if($length) 
     fputs($this->_file_data, $data, $length); 
     else 
     fputs($this->_file_data, $data); 
   } 
 
   /** 
   * 文件锁 
   * @param $is_block 是否独占、阻塞锁 
   */ 
   private function _lock($file_res, $is_block=true){ 
     flock($file_res, $is_block ? LOCK_EX : LOCK_EX|LOCK_NB); 
   } 
 
   private function _unlock($file_res){ 
     flock($file_res, LOCK_UN); 
   } 
 
   public function add($key, $value){ 
     $key = md5($key); 
     $value = serialize($value); 
     $this->_lock($this->_file_index, true); 
     $this->_lock($this->_file_data, true); 
 
     fseek($this->_file_index, $this->_file_header_size); 
 
     list(, $index_count) = unpack('V1', fread($this->_file_index, 4)); 
 
     $data_size = filesize($this->_file_data_name); 
 
     fseek($this->_file_data, $data_size); 
 
     $value_size = strlen($value); 
 
     $this->_data_puts(filesize($this->_file_data_name), $value); 
 
     $node_data =  
     pack("V1V1V1V1V1H32", ($index_count==0) ? 0 : $index_count*$this->_inx_node_size, 0, filesize($this->_file_data_name), strlen($value), 0, $key); 
 
     $index_count++; 
 
     $this->_index_puts($this->_file_header_size, $index_count, 4); 
 
     $this->_index_puts($this->get_new_node_pos($index_count), $node_data); 
 
     $this->_unlock($this->_file_data); 
     $this->_unlock($this->_file_index); 
   } 
 
   public function get_new_node_pos($index_count){ 
     return $this->_file_header_size + 4 + $this->_inx_node_size * ($index_count-1); 
   } 
 
   public function get_node($key){ 
     $key = md5($key); 
     fseek($this->_file_index, $this->_file_header_size); 
     $index_count = fread($this->_file_index, 4); 
 
     if($index_count>0) { 
        for ($i=0; $i < $index_count ; $i++) {  
          fseek($this->_file_index, $this->_file_header_size + 4 + $this->_inx_node_size * $i); 
          $data = fread($this->_file_index, $this->_inx_node_size); 
          $node = unpack("V1next/V1prev/V1data_offset/V1data_size/V1ref_count/H32key", $data); 
 
          if($key == $node['key']){ 
             return $node; 
          } 
        } 
     }else{ 
        return null; 
     } 
   } 
 
   public function get_data($offset, $length){ 
     fseek($this->_file_data, $offset); 
     return unserialize(fread($this->_file_data, $length)); 
   } 
} 
 
//使用方法 
$cache = new fileCache(); 
$cache->add('abcdefg' , 'testabc'); 
$data = $cache->get_node('abcdefg'); 
print_r($data); 
echo $cache->get_data($data['data_offset'], $data['data_size']);

 案例四、socket通信加密

通信双方都定义好加密格式:

例如:

$LOGIN = array( 
   'COMMAND'=>array('a30', 'LOGIN'), 
   'DATA'=>array('a30', 'HELLO') 
); 
 
$LOGOUT = array( 
   'COMMAND'=>array('a30', 'LOGOUT'), 
   'DATA'=>array('a30', 'GOOD BYE') 
); 
 
$LOGIN_SUCCESS = array( 
   'COMMAND'=>array('a30', 'LOGIN_SUCCESS'), 
   'DATA'=>array('V1', 1) 
); 
 
$LOGOUT_SUCCESS = array( 
   'COMMAND'=>array('a30', 'LOGIN_SUCCESS'), 
   'DATA'=>array('V1', time()) 
);

服务器端与客户端根据解析COMMAND格式,找到对应的DATA解码方式,得到正确的数据

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

PHP 相关文章推荐
php算开始时间到过期时间的相隔的天数
Jan 12 PHP
php分页思路以及在ZF中的使用
May 30 PHP
解析php防止form重复提交的方法
Jul 01 PHP
php文字水印和php图片水印实现代码(二种加水印方法)
Dec 25 PHP
PHP面向对象程序设计之接口用法
Aug 20 PHP
php查询mysql大量数据造成内存不足的解决方法
Mar 04 PHP
php防止CC攻击代码 php防止网页频繁刷新
Dec 21 PHP
zen_cart实现支付前生成订单的方法
May 06 PHP
基于PHP实现商品成交时发送短信功能
May 11 PHP
Laravel 5使用Laravel Excel实现Excel/CSV文件导入导出的功能详解
Oct 11 PHP
PHP日期和时间函数的使用示例详解
Aug 06 PHP
php png失真的原因及解决办法
Nov 17 PHP
Yii2中datetime类的使用
Dec 17 #PHP
php生成二维码图片方法汇总
Dec 17 #PHP
PHP二维数组去重算法
Dec 17 #PHP
php格式化时间戳
Dec 17 #PHP
PHP生成唯一ID之SnowFlake算法
Dec 17 #PHP
简单解决微信文章图片防盗链问题
Dec 17 #PHP
PHP 7.1新特性的汇总介绍
Dec 16 #PHP
You might like
php下网站防IP攻击代码,超级实用
2010/10/24 PHP
php文件下载处理方法分析
2015/04/22 PHP
Yii2设置默认控制器的两种方法
2017/05/19 PHP
PHP使用数组实现矩阵数学运算的方法示例
2017/05/29 PHP
PHP中localeconv()函数的用法
2019/03/26 PHP
JQuery 常用操作代码
2010/03/14 Javascript
JS注册/移除事件处理程序(ExtJS应用程序设计实战)
2013/05/07 Javascript
3分钟写出来的Jquery版checkbox全选反选功能
2013/10/23 Javascript
jquery引用方法时传递参数原理分析
2014/10/13 Javascript
js实现Select列表各项上移和下移的方法
2015/08/14 Javascript
canvas简单快速的实现知乎登录页背景效果
2017/05/08 Javascript
JS实现的简单下拉框联动功能示例
2018/05/11 Javascript
深入理解javascript prototype的相关知识
2019/09/19 Javascript
[08:29]DOTA2每周TOP10 精彩击杀集锦vol.7
2014/06/25 DOTA
Python基于二分查找实现求整数平方根的方法
2016/05/12 Python
python调用百度语音识别api
2018/08/30 Python
python判断所输入的任意一个正整数是否为素数的两种方法
2019/06/27 Python
Python字符串格式化输出代码实例
2019/11/22 Python
Python中filter与lambda的结合使用详解
2019/12/24 Python
python如何实现不可变字典inmutabledict
2020/01/08 Python
python操作docx写入内容,并控制文本的字体颜色
2020/02/13 Python
python3光学字符识别模块tesserocr与pytesseract的使用详解
2020/02/26 Python
Pycharm如何自动生成头文件注释
2020/11/14 Python
python3 googletrans超时报错问题及翻译工具优化方案 附源码
2020/12/23 Python
html5实现多图片预览上传及点击可拖拽控件
2018/03/15 HTML / CSS
欧缇丽英国官方网站:Caudalie英国
2016/08/17 全球购物
大学生安全责任书
2014/07/25 职场文书
工程索赔意向书
2014/08/30 职场文书
离婚协议书范本(2014版)
2014/09/28 职场文书
合作合同协议书范本
2015/01/27 职场文书
何玥事迹观后感
2015/06/16 职场文书
2016年师德师风学习心得体会
2016/01/12 职场文书
Python+uiautomator2实现自动刷抖音视频功能
2021/04/29 Python
详解Python生成器和基于生成器的协程
2021/06/03 Python
Python如何将list中的string转换为int
2022/07/15 Ruby
VW、VH适配移动端的解决方案与常见问题
2023/05/21 HTML / CSS