PHP提供了一个嵌入的SAPI,这意味着PHP允许你调用由C或C++
语言PHP /泽提供的
功能,本文实现了一个基于PHP
操作码查看器嵌入SAPI。
首先,
下载编译PHP源代码,和我使用PHP5.3α2
输入源
目录:
用
配置文件扫描与文件配置文件一起进行配置。
/制作
安装 最后,记得要
复制生成的libphp5.so的
运行时库的目录,我直接复制到/lib /,否则会
报告错误当你运行你自己的嵌入式
程序。
。 /嵌入:加载共享库时出错:libphp5.so:不能打开共享对象文件:
如果你不熟悉PHP SAPI,我建议你看看我的文章:对Zend SAPIs有深刻的理解(Zend SAPI内部)
此时,您可以在您的C代码中嵌入PHP
脚本解析器,这是我的示例:
#包括 / / php_embed SAPI嵌入。H
int main(int argc、argv { } { char *)
php_embed_start_block(argc、argv);
char *脚本=print'hello世界!;
zend_eval_string(脚本、空,
简单的Hello World程序tsrmls_cc);
php_embed_end_block();
返回0;
}
然后你要指出包括
路径,一个简单的makefile
CC = GCC
CFLAGS =我/ usr / / /当地包括PHP /
我/ usr / / /当地包括PHP /主要
我/ usr / / /当地包括PHP Zend /
我/ usr / / /当地包括PHP / TSRM
-墙- G
lstdc LDFLAGS建立= - + L / usr /局部/库lphp5
所有:
o嵌入(CC)embed.cpp美元$(CFLAGS)$(LDFLAGS建立)
在编译成功,运行,我们可以看到,stdout输出hello world!
基于此,我们可以很容易地实现一个操作码翻车机类似VLD:
首先,我们定义操作码
转换功能(所有的操作码,可以看到Zend / zend_vm_opcodes。H);
char * opname(zend_uchar码){
开关(码){
案例zend_nop:返回zend_nop ;打破;
案例zend_add:返回zend_add ;打破;
案例zend_sub:返回zend_sub ;打破;
案例zend_mul:返回zend_mul ;打破;
案例zend_div:返回zend_div ;打破;
案例zend_mod:返回zend_mod ;打破;
案例zend_sl:返回zend_sl ;打破;
案例zend_sr:返回zend_sr ;打破;
案例zend_concat:返回zend_concat ;打破;
案例zend_bw_or:返回zend_bw_or ;打破;
案例zend_bw_and:返回zend_bw_and ;打破;
案例zend_bw_xor:返回zend_bw_xor ;打破;
案例zend_bw_not:返回zend_bw_not ;打破;
…*省略号…
默认值:返回未知;打破;
然后机制及其Znode输出
函数定义:
char * format_zval(zval * Z)
{
静态数据的缓冲区buffer_len } {;
Int len;
开关(Z >
类型){
案例is_null:
返回null;
案例is_long:
案例is_bool:
Snprintf(缓冲区,buffer_len,%d
返回缓冲区;
案例is_double:
Snprintf(缓冲区,buffer_len,% F
返回缓冲区;
案例is_string:
Snprintf(缓冲区,buffer_len,%s
返回缓冲区;
案例is_array:
案例is_object:
案例is_resource:
案例is_constant:
案例is_constant_array:
返回;
违约:
返回未知;
}
}
char * format_znode(znode×n){
静态数据的缓冲区buffer_len } {;
开关(n>op_type){
案例is_const:
返回format_zval(n>u.constant);
打破;
案例is_var:
Snprintf(缓冲区,buffer_len,$ % d
返回缓冲区;
打破;
案例is_tmp_var:
Snprintf(缓冲区,buffer_len,~ %d
返回缓冲区;
打破;
违约:
返回;
打破;
}
}
然后op_array输出函数的定义:
无效dump_op(zend_op *
运算,int Num){
printf(% % % % 5d 5d 30s 040s % 040s % 040s
Opname(OP ->码),
format_znode(OP -> OP1),
format_znode(OP -> OP2),
format_znode(OP ->结果));
}
无效dump_op_array(zend_op_array * op_array){
如果(op_array){
int i;
printf(% % % % 5s 5s 30s 040s % 040s % 040s
为(i = 0;我最后;i + +){
dump_op(op_array ->操作码,{我},我);
}
}
}
最后,程序的主要功能是:
int main(int argc、argv char *){
zend_op_array * op_array;
zend_file_handle file_handle;
如果(argc!= 2){
printf(用法:op_dumper ;
返回1;
}
php_embed_start_block(argc、argv);
Printf (script:%s
file_handle.filename = argv { 1 };
file_handle.free_filename = 0;
file_handle.type = zend_handle_filename;
file_handle.opened_path = null;
op_array = zend_compile_file(file_handle,zend_include tsrmls_cc);
如果(!op_array){
printf(分析脚本时出错:%s
返回1;
}
dump_op_array(op_array);
php_embed_end_block();
返回0;
}
编译、运行测试脚本(示例PHP):
复制代码代码如下所示:
Sample.php:
回声laruence ;
秩序:
复制代码代码如下所示:
opcodes_dumper sample.php /。
得到的输出(如果你困惑的下面,结果那就建议你看我的文章:一看PHP的原则操作码的深刻理解):
脚本:sample.php
opnum线码OP1 OP2的结果
02 zend_echolaruence
14 zend_return 1
哦,怎么了不是很有趣吗