php实现类似于pyhon中的Construct库的功能(一)基本设计思路

php实现类似于pyhon中的Construct库的功能(一)基本设计思路

内容导读

收集整理的这篇技术教程文章主要介绍了php实现类似于pyhon中的Construct库的功能(一)基本设计思路,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含16946字,纯文字阅读大概需要25分钟

内容图文

引言

在python中有一个库Construct ,可以用来解析二进制数据,用这个工具分析网络包,带格式的数据文件等等很方便。

前一阵子分析sqlite数据库文件格式时要是用这种工具,也可以省不少事。

不过Construct2.9与以前的版本,改动很大,原来用Construct写的python代码,基本上要重新改写一遍了。在查看Construct源码的过程中,发现Construct的基本实现思路是递归下降分析法。用对象的构造方法来动态地定义数据结构,在parse方法中实现对二进制数据的解析。

打算用php实现二进制数据的解析,但是在实现思路上与python的Construct完全不一样。

推荐PHP视频教程:https://www.gxlcms.com/course/list/29/type/2.html

基本思路

由于python的Construct用python的对象语法来实现动态层次结构的定义与解析,受限于python的对象语法,有一些结构定义看起来很晦涩。

我的想法是定义一个小小的,专门用于描述动态层次结构的语言,这样一来就可以尽量的照顾人们常用的表达习惯。

以这个小项目来说,以C语言中的结构体定义语法为蓝本,在此基础上增加条件化,循环环的结构定义。

在二进制数据中有一种常见的结构,前面几个字节存放后面数据块的长度,接下来才是数据块。这种结构用C语言的结构体定义表示时并不方便。整个数据块的长度是变化的,在编译时无法确定,只能在解析时才能确定。因此有必要对C语言的结构体定义语法进行扩展。

第一步,实现对 C语言结构体的解析

在这一步,先不考虑动态的层次的结构体定义,而是实现这个小语言的最核心的部分,至少它要能理解C语言的结构体定义,并能根据这个结构体定义对二进制数据进行解析。这一步完成后再实现动态的结构体的定义与解析。

本项目基于前面文件中简单介绍过的ADOS脚本语言引擎。

先看一下我们的任务

这里有一个完全是C语言规格的结构体定义文件

blockStruct.h

struct student{
char name[2];
int num;
int age;
char addr[3];};struct teacher{
char name[2];
int num;

char addr[3];};

待解析的二进制数据块

"x41x42x01x00x00x00x02x00x00x00x41x42x43x41x42x01x00x00x00x41x42x43"

希望得到的解析结果

[value] => Array



(





[name] => AB





[num] => 1





[age] => 2





[addr] => ABC



)[value] => Array



(





[name] => AB





[num] => 1





[addr] => ABC



)

感兴趣的朋友可以留意一下这三者之间的关系

下而是仅实现了对C语言结构体进行编译的词法规则文件

<?php/*! * struckwkr的词法规则 * 45022300@qq.com * Version 0.9.0 * * Copyright 2019, Zhu Hui * Released under the MIT license */$GLOBALS['structwkr_lexRules'] =	[		['/^"(.*?)"/','_cons'	,'"'],		['/^(/','_lp'			,'('],		['/^)/','_rp'			,')'],		['/^[/','_lb'			,'['],		['/^]/','_rb'			,']'],		['/^{/','_lcb'			,'{'],		['/^}/','_rcb'			,'}'],		['/^;/','_semi'			,';'],		['/^,/','_comma'		,','],		['/^==/','_bieq'		,'='],		['/^!=/','_uneq'		,'!'],				['/^>=/','_greq'		,'>'],				['/^<=/','_leeq'		,'<'],				['/^=/','_equa'			,'='],				['/^>/','_grea'		,'>'],		['/^</','_less'		,'<'],			['/^+/','_add'			,'+'],		['/^-/','_sub'			,'-'],		['/^*/','_mul'			,'*'],		['/^//','_div'			,'/'],			['/^%/','_mod'			,'$'],		['/^&&/','_and'			,'&'],			['/^||/','_or'		,'|'],			['/^!/','_not'			,'!'],			['/^struct/','_strukey'	,'s'],		['/^char/','_char'		,'c'],		['/^int/','_int'		,'i'],		['/^float/','_float'	,'f'],		['/^double/','_double'	,'d'],		['/^[0-9]+([.]{1}[0-9]+){0,1}/','_num',''],		['/^./','_dot'			,'.'],		['/^[x{4e00}-x{9fa5}A-Za-z_][x{4e00}-x{9fa5}A-Za-z0-9_]*b/u','_iden',''],		['/^s*/','_null','']		];

下面是仅实现了对C语言结构体进行编译的语法规则文件

<?php/*! * structwkr的语法规则处理器 * 45022300@qq.com * Version 0.9.0 * * Copyright 2019, Zhu Hui * Released under the MIT license */namespace Ados;require_once 'const.php';require_once __SCRIPTCORE__.'syntax_rule/base_rules_handler.php';class StructwkrRulesHandler extends BaseRulesHandler{//语法分析的起始记号,归约到最后得到就是若干个结构体定义的列表function startToken(){	return '_structList';}//附加类型function extraType($extraArray){	if(count($extraArray)>0){		return $extraArray[0];	}else{		return '';	}}//求出放在附加信息中的数组长度function elementSize($extraArray){	if(count($extraArray)>0){		return intval($extraArray[0]);	}else{		return 0;	}}//处理源代码中的多条声明语句function handleStatementList($stack,$coder,$listIndex,$statementIndex){		$t1= $this->topItem($stack,$statementIndex);		if($listIndex>0){		$t2= $this->topItem($stack,$listIndex);		//将元素名所在项的附加信息数组找出来		$extraArray=$t2[TokenExtraIndex];	}else{ //$listIndex=0表示,statementList是上级节点,附加信息数组应该是空数组		$extraArray=[];	}	//将statement中的附加信息添加到statementList的附加信息中去	$extraArray[]=$t1[TokenExtraIndex];	return ['#',$extraArray];}//处理源代码中的一条声明语句function handleStatement($stack,$coder,$typeItemIndex,$idenItemIndex){		$t1= $this->topItem($stack,$typeItemIndex);	$elementType = 	$t1[TokenValueIndex];	$t2= $this->topItem($stack,$idenItemIndex);	$elementName = 	$t2[TokenValueIndex];		//将元素名所在项的附加信息数组找出来	$extraArray=$t2[TokenExtraIndex];	$valLen =$extraArray[0];	//附加信息中包含 元素名称,元素类型,数据长度	return [$elementName,[$elementName,$elementType,$valLen]];}//语法规则处理函数名由规则右边部分与规则左边部分拼接而成//语法规则定义的先后决定了归约时匹配的顺序,要根据实际的语法安排//如果不熟悉语法,随意调整语法规则的先后次序将有可能导致语法错误// struct list
{{{function _structList_0_structList_struct($stack,$coder){	$coder->pushBlockTail();	return ['#',[]];		}function _structList_0_struct($stack,$coder){		$coder->pushBlockTail();	return ['#',[]];}// struct list
}}}// struct
 {{{function _struct_0_structName_blockStatement_semi($stack,$coder){	$t1= $this->topItem($stack,3);	$structName = 	$t1[TokenValueIndex];	$t2= $this->topItem($stack,2);	$extraArray=$t2[TokenExtraIndex];		return [$structName,$extraArray];}// struct
 }}}// struct name

 {{{function _structName_0_strukey_iden($stack,$coder){
	$t1= $this->topItem($stack,1);	$structName = 	$t1[TokenValueIndex];	$coder->pushBlockHeader($structName);		return $this->pass($stack,1);}// struct name

 }}}// blockStatement

{{{function _blockStatement_0_lcb_statementList_rcb($stack,$coder){	return $this->pass($stack,2);}// blockStatement

}}}// statement list
{{{function _statementList_0_statementList_statement($stack,$coder){		return $this->handleStatementList($stack,$coder,2,1);}function _statementList_0_statement($stack,$coder){	//此处0表示statementList是上一级节点,要做特殊处理	return $this->handleStatementList($stack,$coder,0,1);	}// statement list
}}}// statement


 {{{function _statement_0_double_term_semi($stack,$coder){		$t1= $this->topItem($stack,2);	$elementName = 	$t1[TokenValueIndex];	$parseFuncName = 'parseDouble';		$coder->pushBlockBody($parseFuncName,$elementName);			return $this->handleStatement($stack,$coder,3,2);}function _statement_0_float_term_semi($stack,$coder){		$t1= $this->topItem($stack,2);	$elementName = 	$t1[TokenValueIndex];	$parseFuncName = 'parseFloat';		$coder->pushBlockBody($parseFuncName,$elementName);			return $this->handleStatement($stack,$coder,3,2);}function _statement_0_char_term_semi($stack,$coder){	$t1= $this->topItem($stack,2);	$elementName = 	$t1[TokenValueIndex];	$size = $this->elementSize($t1[TokenExtraIndex]);	$parseFuncName = 'parseFixStr';		$coder->pushBlockBody($parseFuncName,$elementName,$size);				return $this->handleStatement($stack,$coder,3,2);}function _statement_0_int_term_semi($stack,$coder){		$t1= $this->topItem($stack,2);	$elementName = 	$t1[TokenValueIndex];	$parseFuncName = 'parseInt';		$coder->pushBlockBody($parseFuncName,$elementName,4);			return $this->handleStatement($stack,$coder,3,2);}// statement



}}}//
 term

{{{function _term_0_term_array($stack,$coder){	$t1= $this->topItem($stack,1);	$valLen = 	$t1[TokenValueIndex];	//将数据长度放入附加信息 	$t2= $this->topItem($stack,2);	return [$t2[TokenValueIndex],[$valLen]];		}function _term_0_iden($stack,$coder){	$t1= $this->topItem($stack,1);	//未指定数据长度时将长度值设为0 	$valLen = 	'0';		$t2= $this->topItem($stack,2);	return [$t1[TokenValueIndex],[$valLen]];	}//
 term

 }}}// array 	
{{{function _array_0_lb_num_rb($stack,$coder){	return $this->pass($stack,2);	}// array 	
 }}}} // end of class

下面是对基本数据类型,整数,字符串的解析函数(备注,用于实验,还没有覆盖全部的基本数据类型)

<?php//用于解析基本数据类型的内置函数namespace Ados;//解析一个字节,得到无符号整数值function parseByte($context,$size = 0){	$pos=$context['pos'];	$data = substr($context['data'], $pos);	if(strlen($data)>0){		$raw = substr($data, 0,1);		$value = unpack("C1",$raw)[1];		return ['value'=>$value,'size'=>1,'error'=>0,'msg'=>'ok'];	}else{		return ['value'=>False,'size'=>0,'error'=>1,'msg'=>'not enough for parseByte'];	}	}//解析一个有符号整数function parseInt($context,$size = 4){	if(!($size == 2 or $size == 4 or $size == 8 )){		return ['value'=>False,'size'=>0,'error'=>2,'msg'=>'not a valid size'];	}	if($size == 2) $format = "s1";	if($size == 4) $format = "l1";	if($size == 8) $format = "q1";	$pos=$context['pos'];	$data = substr($context['data'], $pos);	if(strlen($data)>=$size){		$raw = substr($data, 0,$size);		$value = unpack($format,$raw)[1];		return ['value'=>$value,'size'=>$size,'error'=>0,'msg'=>'ok'];	}else{		return ['value'=>False,'size'=>0,'error'=>1,'msg'=>'no data for parseInt'];	}	}//解析一个大端无符号整数function parseBUInt($context,$size = 4){	if(!($size == 2 or $size == 4 or $size == 8 )){		return ['value'=>False,'size'=>0,'error'=>2,'msg'=>'not a valid size'];	}	if($size == 2) $format = "n1";	if($size == 4) $format = "N1";	if($size == 8) $format = "J1";	$pos=$context['pos'];	$data = substr($context['data'], $pos);	if(strlen($data)>=$size){		$raw = substr($data, 0,$size);		$value = unpack($format,$raw)[1];		return ['value'=>$value,'size'=>$size,'error'=>0,'msg'=>'ok'];	}else{		return ['value'=>False,'size'=>0,'error'=>1,'msg'=>'no data for parseBUInt'];	}	}//解析一个小端无符号整数function parseLUInt($context,$size = 4){	if(!($size == 2 or $size == 4 or $size == 8 )){		return ['value'=>False,'size'=>0,'error'=>2,'msg'=>'not a valid size'];	}	if($size == 2) $format = "v1";	if($size == 4) $format = "NL";	if($size == 8) $format = "P1";	$pos=$context['pos'];	$data = substr($context['data'], $pos);	if(strlen($data)>=$size){		$raw = substr($data, 0,$size);		$value = unpack($format,$raw)[1];		return ['value'=>$value,'size'=>$size,'error'=>0,'msg'=>'ok'];	}else{		return ['value'=>False,'size'=>0,'error'=>1,'msg'=>'no data for parseLUInt'];	}	}//解析一个null结束的字符串function parseString($context,$size=0){	$pos=$context['pos'];	$data = substr($context['data'], $pos);	$p=0;	$raw = substr($data, $p,1);	$byte = unpack("C1",$raw)[1];		$result ='';	while($byte){		$result.=$raw;		$p++;		if($p>=strlen($data)){			return ['value'=>False,'size'=>0,'error'=>1,'msg'=>'not find null end for parseString'];		}		$raw = substr($data, $p,1);		$byte = unpack("C1",$raw)[1];			}	return ['value'=>$result,'size'=>$p,'error'=>0,'msg'=>'ok'];}//解析一个定长字符串function parseFixStr($context,$size=0){	$pos=$context['pos'];	$data = substr($context['data'], $pos);	//var_dump($data);		if(strlen($data)>=$size){		$result ='';		for($i=0;$i<$size;$i++){			$raw = substr($data, $i,1);			$value = unpack("a1",$raw)[1];			$result.=$value;					}		return ['value'=>$result,'size'=>$size,'error'=>0,'msg'=>'ok']; 	}	return ['value'=>False,'size'=>0,'error'=>1,'msg'=>'not enough for parseFixedString'];}

下面是用于模板替换的工作函数

<?php//用于进行模板替换的工作函数namespace Ados;defined('__STRUCT_PARSE_TEMP__') or define('__STRUCT_PARSE_TEMP__', './');define('_blockParsTempFile_',__STRUCT_PARSE_TEMP__.'parseFunc.template.php');//取出两个记号串之间的内容function strBetweenToke($src,$toke1,$toke2){	$p1 = strpos($src,$toke1);	$p2 = strpos($src,$toke2);	return substr($src,$p1+strlen($toke1),$p2-$p1-strlen($toke1));}//取得块分析函数的headerfunction blockHeaderStr(){	$src = file_get_contents(_blockParsTempFile_);	return strBetweenToke($src,'/*blockHeader{{*/','/*blockHeader}}*/');}//取得块分析函数的bodyfunction blockBodyStr(){	$src = file_get_contents(_blockParsTempFile_);	return strBetweenToke($src,'/*blockBody{{*/','/*blockBody}}*/');}//取得块分析函数的tailfunction blockTailStr(){	$src = file_get_contents(_blockParsTempFile_);	return strBetweenToke($src,'/*blockTail{{*/','/*blockTail}}*/');}define('_blockHeaderStr_',blockHeaderStr());define('_blockBodyStr_',blockBodyStr());define('_blockTailStr_',blockTailStr());function makeBlockHeader($blockName){	return str_replace('parseBlock_Temp', 'parse'.$blockName, _blockHeaderStr_);}function makeblockBody($parseFuncName,$filedName='',$filedSize=0){	$tmp = str_replace('parseByte', $parseFuncName, _blockBodyStr_);	$tmp = str_replace('$filedName', $filedName, $tmp);	$tmp = str_replace('$filedSize', $filedSize, $tmp);	return $tmp;}function makeblockTail(){	return _blockTailStr_ ;}/*echo blockHeaderStr();echo blockBodyStr();echo blockTailStr();echo makeBlockHeader('Test1');echo makeblockBody('parseInt');echo makeblockBody('parseStr');echo makeblockTail();*/

有了这些准备工作后,就可以实现一个语法制导的编码器,用于生成最终的解析函数。
下而就是一个简单的编码器,在对结构体定义进行语法分析时,生成可用于解析二进制数据的脚本代码

<?php/*! * structwkr编码器, * * 45022300@qq.com * Version 0.9.0 * * Copyright 2019, Zhu Hui * Released under the MIT license */namespace Ados;require_once __SCRIPTCORE__.'coder/base_coder.php';require_once __STRUCT_PARSE_TEMP__.'templateReplaceFuncs.php';class StructwkrCoder extends BaseCoder{	public function __construct($engine)	{		if($engine){ 			$this->engine = $engine;		}else{ 			exit('the engine is not valid in StructwkrCoder construct.');		}	}	//编译得到的最终结果	public function codeLines(){		if(count($this->codeLines)<1){			return '';		}		$script='';				for ($i=0;$i< count($this->codeLines);$i+=1) {			$script.=$this->codeLines[$i];		}			return $script;	}		//
输出编译后的结果 public function printCodeLines(){ echo $this->codeLines(); } //添加一个块解析函数头 public function pushBlockHeader($structName){ $structName=ucfirst($structName); $content = makeBlockHeader($structName); array_push($this->codeLines, $content); $lineIndex=$this->lineIndex; $this->lineIndex+=1; return $lineIndex; } //添加一个块解析函数体 public function pushBlockBody($parseFuncName,$filedName='',$filedSize=0){ $content = makeblockBody($parseFuncName,$filedName,$filedSize); array_push($this->codeLines, $content); $lineIndex=$this->lineIndex; $this->lineIndex+=1; return $lineIndex; } //添加一个块解析函数尾 public function pushBlockTail(){ $content = makeblockTail(); array_push($this->codeLines, $content); $lineIndex=$this->lineIndex; $this->lineIndex+=1; return $lineIndex; }}

自动生成的用于解析的脚本文件

对c语言的结构体进行编译,最终得到可以用于解析二进制数据的一系列函数,比如上述结构体定义文件中定义了两个结构体

struct student{
char name[2];
int num;
int age;
char addr[3];};struct teacher{
char name[2];
int num;

char addr[3];};

那么就会生成与这两个结构体对应的解析函数parseStudentparseTeacher

下面就是自动生成的测试脚本

<?phpnamespace Ados;//加载常量定义文件require_once 'const.php';require_once __STRUCT_PARSE_TEMP__.'templateBuidinFuncs.php';$context['pos']=0;$context['data']="x41x42x01x00x00x00x02x00x00x00x41x42x43x41x42x01x00x00x00x41x42x43";$expRes = parseStudent($context);$context['pos']+=$expRes['size'];print_r($expRes);$expRes = parseTeacher($context);$context['pos']+=$expRes['size'];print_r($expRes);function parseStudent($context,$size=0){	$valueArray=[];	$totalSize = 0;	$expRes = parseFixStr($context,2);	if($expRes['error']==0){		$filed = 'name';		if($filed){			$valueArray[$filed]=$expRes['value'];		}else{			$valueArray[]=$expRes['value'];		}				$context['pos']+=$expRes['size'];		$totalSize+= $expRes['size'];	}else{		return ['value'=>False,'size'=>0,'error'=>$expRes['error'],'msg'=>$expRes['msg']];	}	$expRes = parseInt($context,4);	if($expRes['error']==0){		$filed = 'num';		if($filed){			$valueArray[$filed]=$expRes['value'];		}else{			$valueArray[]=$expRes['value'];		}				$context['pos']+=$expRes['size'];		$totalSize+= $expRes['size'];	}else{		return ['value'=>False,'size'=>0,'error'=>$expRes['error'],'msg'=>$expRes['msg']];	}	$expRes = parseInt($context,4);	if($expRes['error']==0){		$filed = 'age';		if($filed){			$valueArray[$filed]=$expRes['value'];		}else{			$valueArray[]=$expRes['value'];		}				$context['pos']+=$expRes['size'];		$totalSize+= $expRes['size'];	}else{		return ['value'=>False,'size'=>0,'error'=>$expRes['error'],'msg'=>$expRes['msg']];	}	$expRes = parseFixStr($context,3);	if($expRes['error']==0){		$filed = 'addr';		if($filed){			$valueArray[$filed]=$expRes['value'];		}else{			$valueArray[]=$expRes['value'];		}				$context['pos']+=$expRes['size'];		$totalSize+= $expRes['size'];	}else{		return ['value'=>False,'size'=>0,'error'=>$expRes['error'],'msg'=>$expRes['msg']];	}	return ['value'=>$valueArray,'size'=>$totalSize,'error'=>0,'msg'=>'ok'];}function parseTeacher($context,$size=0){	$valueArray=[];	$totalSize = 0;	$expRes = parseFixStr($context,2);	if($expRes['error']==0){		$filed = 'name';		if($filed){			$valueArray[$filed]=$expRes['value'];		}else{			$valueArray[]=$expRes['value'];		}				$context['pos']+=$expRes['size'];		$totalSize+= $expRes['size'];	}else{		return ['value'=>False,'size'=>0,'error'=>$expRes['error'],'msg'=>$expRes['msg']];	}	$expRes = parseInt($context,4);	if($expRes['error']==0){		$filed = 'num';		if($filed){			$valueArray[$filed]=$expRes['value'];		}else{			$valueArray[]=$expRes['value'];		}				$context['pos']+=$expRes['size'];		$totalSize+= $expRes['size'];	}else{		return ['value'=>False,'size'=>0,'error'=>$expRes['error'],'msg'=>$expRes['msg']];	}	$expRes = parseFixStr($context,3);	if($expRes['error']==0){		$filed = 'addr';		if($filed){			$valueArray[$filed]=$expRes['value'];		}else{			$valueArray[]=$expRes['value'];		}				$context['pos']+=$expRes['size'];		$totalSize+= $expRes['size'];	}else{		return ['value'=>False,'size'=>0,'error'=>$expRes['error'],'msg'=>$expRes['msg']];	}	return ['value'=>$valueArray,'size'=>$totalSize,'error'=>0,'msg'=>'ok'];}

运行此测试脚本,得到结果如下:

Array(

[value] => Array



(





[name] => AB





[num] => 1





[age] => 2





[addr] => ABC



)

[size] => 13

[error] => 0

[msg] => ok)Array(

[value] => Array



(





[name] => AB





[num] => 1





[addr] => ABC



)

[size] => 9

[error] => 0

[msg] => ok)

OK ! 第一步的任务已经完成,接下来考虑实现一个带有条件判断的结构体的解析工作。

更多相关问题请访问PHP中文网:https://www.gxlcms.com/

以上就是php 实现类似于pyhon中的Construct库的功能(一) 基本设计思路的详细内容,更多请关注Gxl网其它相关文章!

内容总结

以上是为您收集整理的php实现类似于pyhon中的Construct库的功能(一)基本设计思路全部内容,希望文章能够帮你解决php实现类似于pyhon中的Construct库的功能(一)基本设计思路所遇到的程序开发问题。 如果觉得技术教程内容还不错,欢迎将网站推荐给程序员好友。

内容备注

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。


本文关键词:

联系我们

在线咨询:点击这里给我发消息

邮件:w420220301@qq.com