引言
在上一篇文章《PHP实现类似于Python中的Construct库功能(二)实现适配器功能》介绍了用php解析二进制数据的基本思路。接下来要完成两个工作。
1、在上一篇文章中采用解析函数的方式,现在改成用解析类,在类中包含parse方法。
2、在定义结构体数据项时,加上适配器功能,用管道运算符| 连接数据项与适配器,实现数据的变换。
推荐PHP视频教程:https://www.php.cn/course/list/29/type/2.html
立即学习“PHP免费学习笔记(深入)”;
基本思路
1,修改词法分析规则,使其可以接受管道运算符 |
2,修改语法分析规则,使其可以接受适配器的调用
3,用php语言实现适配器功能
实现内容
准备解析的结构体定义文件
struct student
{
char name[2];
int num|IntOffset(100);
int age;
char addr[3];
};
struct teacher
{
char name[2];
int num|Int2str;
char addr[3];
};与上一篇文章中的结构体定义最大的不同在于以下这两句
int num|IntOffset(100); int num|Int2str; };
这里通过管道调用了两个适配器,IntOffset(100) 表示在原有值的基础上偏移100,Int2str表示将原有的整数值变成字符串
下面看一下这两个适配器的具体实现
<?php
//将整数值偏移一个定值
namespace Ados;
class IntOffset {
public function __construct($offset)
{
$this->offset = $offset;
}
function parse($obj){
if(is_array($obj)){
return array_map(function($value) {
return $value + $this->offset;
},$obj);
}
return $obj+ $this->offset;
}
function build($obj){
throw new \Exception("Int2str build method not implements");
}
}
class Int2str {
function parse($obj){
if(is_array($obj)){
return array_map(function($value) {
return ''.$value;
},$obj);
}
return ''.$obj;
}
function build($obj){
throw new \Exception("Int2str build method not implements");
}
}为了集中注意力在二进制数据的解析上,以上两个类中只实现了parse方法,build方法暂未实现。
相应的,模板文件也要有所调整
<?php
//用于进行替换的模板文本
/*blockHeader{{*/
class structName{
static function parse($context,$size=0){
$valueArray=[];
$totalSize = 0;
/*blockHeader}}*/
/*parseBody{{*/
$expRes = parseByte($context,$filedSize);
/*parseBody}}*/
/*pipeBody{{*/
$expRes['value'] = (new Adapter())->parse($expRes['value']);
/*pipeBody}}*/
/*checkBody{{*/
if($expRes['error']==0){
$filed = '$filedName';
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']];
}
/*checkBody}}*/
/*blockTail{{*/
return ['value'=>$valueArray,'size'=>$totalSize,'error'=>0,'msg'=>'ok'];
}
}
/*blockTail}}*/词法规则文件改动不大,增加对管道运算符的匹配就可以了
['/^\|/','_pipe' ,'|'],
语法规则文件改动得比较大,由于要能够分析这一类语句
int num|IntOffset(100);
修改后的语法规则文件内容如下:
<?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 elementSize($extraArray){
if(count($extraArray)>0){
return intval($extraArray[0]);
}else{
return 0;
}
}
//语法规则处理函数名由规则右边部分与规则左边部分拼接而成
//语法规则定义的先后决定了归约时匹配的顺序,要根据实际的语法安排
//如果不熟悉语法,随意调整语法规则的先后次序将有可能导致语法错误
// 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->pass($stack,1);
}
function _statementList_0_statement($stack,$coder){
//此处0表示statementList是上一级节点,要做特殊处理
return $this->pass($stack,1);
}
// statement list }}}
// statement {{{
function _statement_0_expression_semi($stack,$coder){
$t1= $this->topItem($stack,2);
$elementName = $t1[TokenValueIndex];
$coder->pushCheckBody($elementName);
return $this->pass($stack,2);
}
// statement }}}
// function expression {{{
//函数表达式
function _term_0_funcTerm($stack,$coder){
$t1= $this->topItem($stack,1);
$funcName=$t1[TokenValueIndex];
$paraArray=$t1[TokenExtraIndex];
$paras = implode(",", $paraArray);
$exp = $funcName.'('.$paras.')';
return [$exp,[]];
}
function _funcTerm_0_funcExpLp_rp($stack,$coder){
return $this->pass($stack,2);
}
function _funcTerm_0_funcExpLeft_rp($stack,$coder){
return $this->pass($stack,2);
}
function _funcExpLeft_0_funcExpLeft_comma_expression($stack,$coder){
$t1= $this->topItem($stack,3);
$t2= $this->topItem($stack,1);
//函数的参数列表存放在附加信息中
$paraArray=$t1[TokenExtraIndex];
array_push($paraArray, $t2[TokenValueIndex]);
return [$t1[TokenValueIndex],$paraArray];
}
function _funcExpLeft_0_funcExpLp_expression($stack,$coder){
$t1= $this->topItem($stack,2);
$t2= $this->topItem($stack,1);
//函数的参数列表存放在附加信息中
$paraArray=$t1[TokenExtraIndex];
array_push($paraArray, $t2[TokenValueIndex]);
return [$t1[TokenValueIndex],$paraArray];
}
function _funcExpLp_0_iden_lp($stack,$coder){
return $this->pass($stack,2);
}
// function expression }}}
// Expression {{{
//表达式可以进行管道运算
function _expression_0_expression_pipe_factor($stack,$coder){
$t1= $this->topItem($stack,1);
$handlerName = $t1[TokenValueIndex];
$coder->pushPipeBody($handlerName);
return $this->pass($stack,3);
}
function _expression_0_double_factor($stack,$coder){
$t1= $this->topItem($stack,1);
$elementName = $t1[TokenValueIndex];
$parseFuncName = 'parseDouble';
$coder->pushParseBody($parseFuncName,$elementName);
return $this->pass($stack,1);
}
function _expression_0_float_factor($stack,$coder){
$t1= $this->topItem($stack,1);
$elementName = $t1[TokenValueIndex];
$parseFuncName = 'parseFloat';
$coder->pushParseBody($parseFuncName,$elementName);
return $this->pass($stack,1);
}
function _expression_0_char_factor($stack,$coder){
$t1= $this->topItem($stack,1);
$elementName = $t1[TokenValueIndex];
$size = $this->elementSize($t1[TokenExtraIndex]);
$parseFuncName = 'parseFixStr';
$coder->pushParseBody($parseFuncName,$elementName,$size);
return $this->pass($stack,1);
}
function _expression_0_int_factor($stack,$coder){
$t1= $this->topItem($stack,1);
$elementName = $t1[TokenValueIndex];
$parseFuncName = 'parseInt';
$coder->pushParseBody($parseFuncName,$elementName,4);
return $this->pass($stack,1);
}
function _expression_0_factor($stack,$coder){
return $this->pass($stack,1);
}
// Expression }}}
// factor {{{
function _factor_0_term($stack,$coder){
return $this->pass($stack,1);
}
// factor }}}
// term {{{
function _term_0_iden($stack,$coder){
$t1= $this->topItem($stack,1);
//未指定数据长度时将长度值设为0
$valLen = '0';
$t2= $this->topItem($stack,2);
return [$t1[TokenValueIndex],[$valLen]];
}
function _term_0_num($stack,$coder){
return $this->pass($stack,1);
}
function _term_0_array($stack,$coder){
return $this->pass($stack,1);
}
// term }}}
// array {{{
function _array_0_arrayLb_num_rb($stack,$coder){
$t1= $this->topItem($stack,2);
$valLen = $t1[TokenValueIndex];
$t2= $this->topItem($stack,3);
//将数据长度放入附加信息
return [$t2[TokenValueIndex],[$valLen]];
}
function _arrayLb_0_iden_lb($stack,$coder){
return $this->pass($stack,2);
}
// array }}}
}// end of class编码器也要做相应的调整,增加对管道运算的处理
<?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 pushParseBody($parseFuncName,$filedName='',$filedSize=0){
$content = makeParseBody($parseFuncName,$filedName,$filedSize);
array_push($this->codeLines, $content);
$lineIndex=$this->lineIndex;
$this->lineIndex+=1;
return $lineIndex;
}
//添加一个管道处理
public function pushPipeBody($handler){
$content = makePipeBody($handler);
array_push($this->codeLines, $content);
$lineIndex=$this->lineIndex;
$this->lineIndex+=1;
return $lineIndex;
}
//添加一个检查结果值的body
public function pushCheckBody($filedName=''){
$content = makeCheckBody($filedName);
array_push($this->codeLines, $content);
$lineIndex=$this->lineIndex;
$this->lineIndex+=1;
return $lineIndex;
}
//添加一个块解析类的tail
public function pushBlockTail(){
$content = makeblockTail();
array_push($this->codeLines, $content);
$lineIndex=$this->lineIndex;
$this->lineIndex+=1;
return $lineIndex;
}
}实现结果
自动生成的测试文件如下
<?php
namespace Ados;
//加载常量定义文件
require_once 'const.php';
require_once __STRUCT_PARSE_TEMP__.'templateBuidinFuncs.php';
require_once __STRUCT_PARSE_ADAPTER__.'int2str.adapter.php';
require_once __STRUCT_PARSE_ADAPTER__.'intoffset.adapter.php';
$context['pos']=0;
$context['data']="\x41\x43\x01\x00\x00\x00\x02\x00\x00\x00\x41\x42\x43\x41\x42\x01\x00\x00\x00\x41\x42\x43";
$expRes = Student::parse($context);
$context['pos']+=$expRes['size'];
print_r($expRes);
$expRes = Teacher::parse($context);
$context['pos']+=$expRes['size'];
print_r($expRes);
class Student{
static function parse($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);
$expRes['value'] = (new IntOffset(100))->parse($expRes['value']);
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'];
}
}
class Teacher{
static function parse($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);
$expRes['value'] = (new Int2str)->parse($expRes['value']);
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] => AC
[num] => 101
[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
)对比测试数据
$context['data']="\x41\x43\x01\x00\x00\x00\x02\x00\x00\x00\x41\x42\x43\x41\x42\x01\x00\x00\x00\x41\x42\x43";
如果不加适配器
第一个结构体的num字段的结果应是:[num] => 1
现在加上了适配器int num|IntOffset(100);
所以结果变成了:
[num] => 101
适配器功能已经实现并得到了验证。
更多相关问题请访问PHP中文网相关视频教程:https://www.php.cn/
以上就是PHP实现类似于Python中的Construct库功能(二)实现适配器功能的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号