当前位置: 移动技术网 > IT编程>开发语言>PHP > 如何使用PHP Embed SAPI实现Opcodes查看器

如何使用PHP Embed SAPI实现Opcodes查看器

2018年04月29日  | 移动技术网IT编程  | 我要评论

php提供了一个embed sapi,也就是说,php容许你在c/c++语言中调用php/ze提供的函数。本文就通过基于embed sapi实现一个php的opcodes查看器。

首先,下载php源码以供编译, 我现在使用的是php5.3 alpha2

进入源码目录:

 ./configure --enable-embed --with-config-file-scan-dir=/etc/php.d --with-mysql  --with-config-file-path=/etc/
 ./make
 ./make install

最后,记得要将生成的libphp5.so复制到运行时库的目录,我直接拷贝到了/lib/, 否则会在运行你自己的embed程序的时候报错:

./embed: error while loading shared libraries: libphp5.so: cannot open shared object file: no such file or directory

如果你对php的sapi还不熟悉的话,我建议你看看我的这篇文章:深入理解zend sapis(zend sapi internals)
这个时候,你就可以在你的c代码中,嵌入php脚本解析器了, 我的例子:

#include "sapi/embed/php_embed.h"
int main(int argc, char * argv[]){
 php_embed_start_block(argc,argv);
 char * script = " print 'hello world!';";
 zend_eval_string(script, null,
          "simple hello world app" tsrmls_cc);
 php_embed_end_block();
 return 0;
}

然后就是要指明include path了,一个简单的makefile

cc = gcc
cflags = -i/usr/local/include/php/ \
   -i/usr/local/include/php/main \
   -i/usr/local/include/php/zend \
   -i/usr/local/include/php/tsrm \
   -wall -g
ldflags = -lstdc++ -l/usr/local/lib -lphp5
all:
 $(cc) -o embed embed.cpp $(cflags) $(ldflags)

编译成功以后, 运行,我们可以看到, stdout输出 hello world!

基于这个,我们就可以很容易的实现一个类似于vld的opcodes dumper:
首先我们定义opcode的转换函数(全部的opcodes可以查看zend/zend_vm_opcodes.h);

char *opname(zend_uchar opcode){
 switch(opcode) {
  case zend_nop: return "zend_nop"; break;
  case zend_add: return "zend_add"; break;
  case zend_sub: return "zend_sub"; break;
  case zend_mul: return "zend_mul"; break;
  case zend_div: return "zend_div"; break;
  case zend_mod: return "zend_mod"; break;
  case zend_sl: return "zend_sl"; break;
  case zend_sr: return "zend_sr"; break;
  case zend_concat: return "zend_concat"; break;
  case zend_bw_or: return "zend_bw_or"; break;
  case zend_bw_and: return "zend_bw_and"; break;
  case zend_bw_xor: return "zend_bw_xor"; break;
  case zend_bw_not: return "zend_bw_not"; break;
  /*...省略 ....*/
  default : return "unknow"; break;

然后定义zval和znode的输出函数:

 char *format_zval(zval *z)
{
 static char buffer[buffer_len];
 int len;
 switch(z->type) {
  case is_null:
   return "null";
  case is_long:
  case is_bool:
   snprintf(buffer, buffer_len, "%d", z->value.lval);
   return buffer;
  case is_double:
   snprintf(buffer, buffer_len, "%f", z->value.dval);
   return buffer;
  case is_string:
   snprintf(buffer, buffer_len, "\"%s\"", z->value.str.val);
   return buffer;
  case is_array:
  case is_object:
  case is_resource:
  case is_constant:
  case is_constant_array:
   return "";
  default:
   return "unknown";
 }
}
char * format_znode(znode *n){
 static char buffer[buffer_len];
 switch (n->op_type) {
  case is_const:
   return format_zval(&n->u.constant);
   break;
  case is_var:
   snprintf(buffer, buffer_len, "$%d", n->u.var/sizeof(temp_variable));
   return buffer;
   break;
  case is_tmp_var:
   snprintf(buffer, buffer_len, "~%d", n->u.var/sizeof(temp_variable));
   return buffer;
   break;
  default:
   return "";
   break;
 }
}

 然后定义op_array的输出函数:

void dump_op(zend_op *op, int num){
 printf("%5d %5d %30s %040s %040s %040s\n", num, op->lineno,
   opname(op->opcode),
   format_znode(&op->op1),
   format_znode(&op->op2),
   format_znode(&op->result)) ;
}
void dump_op_array(zend_op_array *op_array){
 if(op_array) {
  int i;
  printf("%5s %5s %30s %040s %040s %040s\n", "opnum", "line", "opcode", "op1", "op2", "result");
  for(i = 0; i < op_array->last; i++) {
   dump_op(&op_array->opcodes[i], i);
  }
 }
}

最后,就是程序的主函数了:

int main(int argc, char **argv){
 zend_op_array *op_array;
 zend_file_handle file_handle;
 if(argc != 2) {
  printf("usage: op_dumper <script>\n");
  return 1;
 }
 php_embed_start_block(argc,argv);
 printf("script: %s\n", argv[1]);
 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);
 if(!op_array) {
  printf("error parsing script: %s\n", file_handle.filename);
  return 1;
 }
 dump_op_array(op_array);
 php_embed_end_block();
 return 0;
}

编译,运行测试脚本(sample.php):

复制代码 代码如下:

sample.php:
   echo "laruence";

命令:

复制代码 代码如下:

./opcodes_dumper  sample.php

得到输出结果(如果你对下面的结果很迷惑,那么建议你再看看我的这篇文章:深入理解php原理之opcodes):

script: sample.php

opnum   line                         opcode                                      op1                                      op2                                   result
    0      2                      zend_echo                               "laruence"
    1      4                    zend_return                                        1

呵呵,怎么样,是不是很好玩呢?

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网