首頁 後端開發 PHP問題 PHP實作類似Python中的Construct庫功能(二)實作適配器功能

PHP實作類似Python中的Construct庫功能(二)實作適配器功能

Aug 19, 2019 pm 04:18 PM

引言

在上一篇文章《PHP實作類似Python中的Construct函式庫功能(二)實作適配器功能》介紹了用php解析二進位資料的基本思路。接下來要完成兩個工作。

1、在上一篇文章中採用解析函數的方式,現在改成用解析類,在類別中包含parse方法。

2、在定義結構體資料項目時,加上適配器功能,用管道運算子| 連接資料項目與適配器,實現資料的變換。

推薦PHP影片教學:https://www.php.cn/course/list/29/type/2.html

基本想法

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 &#39;&#39;.$value;
					},$obj);
		}
		return &#39;&#39;.$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[&#39;value&#39;] = (new Adapter())->parse($expRes[&#39;value&#39;]);		
	/*pipeBody}}*/

	/*checkBody{{*/
		if($expRes[&#39;error&#39;]==0){
			$filed = &#39;$filedName&#39;;
			if($filed){
				$valueArray[$filed]=$expRes[&#39;value&#39;];
			}else{
				$valueArray[]=$expRes[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
			$totalSize+= $expRes[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$expRes[&#39;error&#39;],&#39;msg&#39;=>$expRes[&#39;msg&#39;]];
		}
	/*checkBody}}*/
	

	/*blockTail{{*/
		return [&#39;value&#39;=>$valueArray,&#39;size&#39;=>$totalSize,&#39;error&#39;=>0,&#39;msg&#39;=>&#39;ok&#39;];
	}
}
	/*blockTail}}*/
登入後複製

詞法規則檔案改動不大,增加對管道運算子的匹配就可以了

[&#39;/^\|/&#39;,&#39;_pipe&#39;		,&#39;|&#39;],
登入後複製

語法規則文件改動得比較大,由於要能夠分析這一類語句

 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 &#39;const.php&#39;;
require_once __SCRIPTCORE__.&#39;syntax_rule/base_rules_handler.php&#39;;

class StructwkrRulesHandler extends BaseRulesHandler{

//语法分析的起始记号,归约到最后得到就是若干个结构体定义的列表
function startToken(){

	return &#39;_structList&#39;;
}


//求出放在附加信息中的数组长度
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 [&#39;#&#39;,[]];
	
	
}

function _structList_0_struct($stack,$coder){	
	$coder->pushBlockTail();
	return [&#39;#&#39;,[]];
}

// 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.&#39;(&#39;.$paras.&#39;)&#39;;	
	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 = &#39;parseDouble&#39;;	
	$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 = &#39;parseFloat&#39;;	
	$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 = &#39;parseFixStr&#39;;	
	$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 = &#39;parseInt&#39;;	
	$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 = 	&#39;0&#39;;	
	$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__.&#39;coder/base_coder.php&#39;;
require_once __STRUCT_PARSE_TEMP__.&#39;templateReplaceFuncs.php&#39;;


class StructwkrCoder extends BaseCoder{

	public function __construct($engine)
	{
		if($engine){ 
			$this->engine = $engine;
		}else{ 
			exit(&#39;the engine is not valid in StructwkrCoder construct.&#39;);
		}
	}

	//编译得到的最终结果
	public function codeLines(){
		if(count($this->codeLines)<1){
			return &#39;&#39;;
		}
		$script=&#39;&#39;;		
		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=&#39;&#39;,$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=&#39;&#39;){
		$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 &#39;const.php&#39;;
require_once __STRUCT_PARSE_TEMP__.&#39;templateBuidinFuncs.php&#39;;
require_once __STRUCT_PARSE_ADAPTER__.&#39;int2str.adapter.php&#39;;
require_once __STRUCT_PARSE_ADAPTER__.&#39;intoffset.adapter.php&#39;;

$context[&#39;pos&#39;]=0;
$context[&#39;data&#39;]="\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[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
print_r($expRes);


$expRes = Teacher::parse($context);
$context[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
print_r($expRes);



class Student{

	static function parse($context,$size=0){
		$valueArray=[];
		$totalSize = 0;
	
		$expRes = parseFixStr($context,2);
	
		if($expRes[&#39;error&#39;]==0){
			$filed = &#39;name&#39;;
			if($filed){
				$valueArray[$filed]=$expRes[&#39;value&#39;];
			}else{
				$valueArray[]=$expRes[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
			$totalSize+= $expRes[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$expRes[&#39;error&#39;],&#39;msg&#39;=>$expRes[&#39;msg&#39;]];
		}
	
		$expRes = parseInt($context,4);
	
		$expRes[&#39;value&#39;] = (new IntOffset(100))->parse($expRes[&#39;value&#39;]);		
	
		if($expRes[&#39;error&#39;]==0){
			$filed = &#39;num&#39;;
			if($filed){
				$valueArray[$filed]=$expRes[&#39;value&#39;];
			}else{
				$valueArray[]=$expRes[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
			$totalSize+= $expRes[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$expRes[&#39;error&#39;],&#39;msg&#39;=>$expRes[&#39;msg&#39;]];
		}
	
		$expRes = parseInt($context,4);
	
		if($expRes[&#39;error&#39;]==0){
			$filed = &#39;age&#39;;
			if($filed){
				$valueArray[$filed]=$expRes[&#39;value&#39;];
			}else{
				$valueArray[]=$expRes[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
			$totalSize+= $expRes[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$expRes[&#39;error&#39;],&#39;msg&#39;=>$expRes[&#39;msg&#39;]];
		}
	
		$expRes = parseFixStr($context,3);
	
		if($expRes[&#39;error&#39;]==0){
			$filed = &#39;addr&#39;;
			if($filed){
				$valueArray[$filed]=$expRes[&#39;value&#39;];
			}else{
				$valueArray[]=$expRes[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
			$totalSize+= $expRes[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$expRes[&#39;error&#39;],&#39;msg&#39;=>$expRes[&#39;msg&#39;]];
		}
	
		return [&#39;value&#39;=>$valueArray,&#39;size&#39;=>$totalSize,&#39;error&#39;=>0,&#39;msg&#39;=>&#39;ok&#39;];
	}
}
	
class Teacher{

	static function parse($context,$size=0){
		$valueArray=[];
		$totalSize = 0;
	
		$expRes = parseFixStr($context,2);
	
		if($expRes[&#39;error&#39;]==0){
			$filed = &#39;name&#39;;
			if($filed){
				$valueArray[$filed]=$expRes[&#39;value&#39;];
			}else{
				$valueArray[]=$expRes[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
			$totalSize+= $expRes[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$expRes[&#39;error&#39;],&#39;msg&#39;=>$expRes[&#39;msg&#39;]];
		}
	
		$expRes = parseInt($context,4);
	
		$expRes[&#39;value&#39;] = (new Int2str)->parse($expRes[&#39;value&#39;]);		
	
		if($expRes[&#39;error&#39;]==0){
			$filed = &#39;num&#39;;
			if($filed){
				$valueArray[$filed]=$expRes[&#39;value&#39;];
			}else{
				$valueArray[]=$expRes[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
			$totalSize+= $expRes[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$expRes[&#39;error&#39;],&#39;msg&#39;=>$expRes[&#39;msg&#39;]];
		}
	
		$expRes = parseFixStr($context,3);
	
		if($expRes[&#39;error&#39;]==0){
			$filed = &#39;addr&#39;;
			if($filed){
				$valueArray[$filed]=$expRes[&#39;value&#39;];
			}else{
				$valueArray[]=$expRes[&#39;value&#39;];
			}		
			$context[&#39;pos&#39;]+=$expRes[&#39;size&#39;];
			$totalSize+= $expRes[&#39;size&#39;];
		}else{
			return [&#39;value&#39;=>False,&#39;size&#39;=>0,&#39;error&#39;=>$expRes[&#39;error&#39;],&#39;msg&#39;=>$expRes[&#39;msg&#39;]];
		}
	
		return [&#39;value&#39;=>$valueArray,&#39;size&#39;=>$totalSize,&#39;error&#39;=>0,&#39;msg&#39;=>&#39;ok&#39;];
	}
}
登入後複製

執行測試檔案的結果

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[&#39;data&#39;]="\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中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1321
25
PHP教程
1269
29
C# 教程
1249
24