VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > PHP >
  • 如何使用PHP Embed SAPI实现Opcodes查看器

这篇文章主要介绍了如何使用PHP Embed SAPI实现Opcodes查看器的相关资料,需要的朋友可以参考下。

PHP提供了一个Embed SAPI,也就是说,PHP容许你在C/C++语言中调用PHP/ZE提供的函数。本文就通过基于Embed SAPI实现一个PHP的opcodes查看器。

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

进入源码目录:

  1. ./configure --enable-embed --with-config-file-scan-dir=/etc/php.d --with-mysql  --with-config-file-path=/etc/ 
  2. ./make 
  3. ./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脚本解析器了, 我的例子:

  1. #include "sapi/embed/php_embed.h" 
  2. int main(int argc, char * argv[]){ 
  3.  PHP_EMBED_START_BLOCK(argc,argv); 
  4.  char * script = " print 'Hello World!';"; 
  5.  zend_eval_string(script, NULL, 
  6.           "Simple Hello World App" TSRMLS_CC); 
  7.  PHP_EMBED_END_BLOCK(); 
  8.  return 0; 
  9. } 

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

  1. CC = gcc 
  2. CFLAGS = -I/usr/local/include/php/ \ 
  3.    -I/usr/local/include/php/main \ 
  4.    -I/usr/local/include/php/Zend \ 
  5.    -I/usr/local/include/php/TSRM \ 
  6.    -Wall -g 
  7. LDFLAGS = -lstdc++ -L/usr/local/lib -lphp5 
  8. ALL: 
  9.  $(CC) -o embed embed.cpp $(CFLAGS) $(LDFLAGS) 

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

基于这个,我们就可以很容易的实现一个类似于vld的Opcodes dumper:

首先我们定义opcode的转换函数(全部的opcodes可以查看Zend/zend_vm_opcodes.h);

  1. char *opname(zend_uchar opcode){ 
  2.  switch(opcode) { 
  3.   case ZEND_NOP: return "ZEND_NOP"; break; 
  4.   case ZEND_ADD: return "ZEND_ADD"; break; 
  5.   case ZEND_SUB: return "ZEND_SUB"; break; 
  6.   case ZEND_MUL: return "ZEND_MUL"; break; 
  7.   case ZEND_DIV: return "ZEND_DIV"; break; 
  8.   case ZEND_MOD: return "ZEND_MOD"; break; 
  9.   case ZEND_SL: return "ZEND_SL"; break; 
  10.   case ZEND_SR: return "ZEND_SR"; break; 
  11.   case ZEND_CONCAT: return "ZEND_CONCAT"; break; 
  12.   case ZEND_BW_OR: return "ZEND_BW_OR"; break; 
  13.   case ZEND_BW_AND: return "ZEND_BW_AND"; break; 
  14.   case ZEND_BW_XOR: return "ZEND_BW_XOR"; break; 
  15.   case ZEND_BW_NOT: return "ZEND_BW_NOT"; break; 
  16.   /*...省略 ....*/ 
  17.   default : return "UNKNOW"; break; 

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

  1. char *format_zval(zval *z) 
  2. { 
  3.  static char buffer[BUFFER_LEN]; 
  4.  int len; 
  5.  switch(z->type) { 
  6.   case IS_NULL: 
  7.    return "NULL"; 
  8.   case IS_LONG: 
  9.   case IS_BOOL: 
  10.    snprintf(buffer, BUFFER_LEN, "%d", z->value.lval); 
  11.    return buffer; 
  12.   case IS_DOUBLE: 
  13.    snprintf(buffer, BUFFER_LEN, "%f", z->value.dval); 
  14.    return buffer; 
  15.   case IS_STRING: 
  16.    snprintf(buffer, BUFFER_LEN, "\"%s\"", z->value.str.val); 
  17.    return buffer; 
  18.   case IS_ARRAY: 
  19.   case IS_OBJECT: 
  20.   case IS_RESOURCE: 
  21.   case IS_CONSTANT: 
  22.   case IS_CONSTANT_ARRAY: 
  23.    return ""; 
  24.   default: 
  25.    return "unknown"; 
  26.  } 
  27. } 
  28. char * format_znode(znode *n){ 
  29.  static char buffer[BUFFER_LEN]; 
  30.  switch (n->op_type) { 
  31.   case IS_CONST: 
  32.    return format_zval(&n->u.constant); 
  33.    break; 
  34.   case IS_VAR: 
  35.    snprintf(buffer, BUFFER_LEN, "$%d", n->u.var/sizeof(temp_variable)); 
  36.    return buffer; 
  37.    break; 
  38.   case IS_TMP_VAR: 
  39.    snprintf(buffer, BUFFER_LEN, "~%d", n->u.var/sizeof(temp_variable)); 
  40.    return buffer; 
  41.    break; 
  42.   default: 
  43.    return ""; 
  44.    break; 
  45.  } 
  46. } 

然后定义op_array的输出函数:

  1. void dump_op(zend_op *op, int num){ 
  2.  printf("%5d %5d %30s %040s %040s %040s\n", num, op->lineno, 
  3.    opname(op->opcode), 
  4.    format_znode(&op->op1), 
  5.    format_znode(&op->op2), 
  6.    format_znode(&op->result)) ; 
  7. } 
  8. void dump_op_array(zend_op_array *op_array){ 
  9.  if(op_array) { 
  10.   int i; 
  11.   printf("%5s %5s %30s %040s %040s %040s\n", "opnum", "line", "opcode", "op1", "op2", "result"); 
  12.   for(i = 0; i < op_array->last; i++) { 
  13.    dump_op(&op_array->opcodes[i], i); 
  14.   } 
  15.  } 
  16. } 

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

  1. int main(int argc, char **argv){ 
  2.  zend_op_array *op_array; 
  3.  zend_file_handle file_handle; 
  4.  if(argc != 2) { 
  5.   printf("usage: op_dumper <script>\n"); 
  6.   return 1; 
  7.  } 
  8.  PHP_EMBED_START_BLOCK(argc,argv); 
  9.  printf("Script: %s\n", argv[1]); 
  10.  file_handle.filename = argv[1]; 
  11.  file_handle.free_filename = 0; 
  12.  file_handle.type = ZEND_HANDLE_FILENAME; 
  13.  file_handle.opened_path = NULL; 
  14.  op_array = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC); 
  15.  if(!op_array) { 
  16.   printf("Error parsing script: %s\n", file_handle.filename); 
  17.   return 1; 
  18.  } 
  19.  dump_op_array(op_array); 
  20.  PHP_EMBED_END_BLOCK(); 
  21.  return 0; 
  22. } 

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

sample.php:

echo "laruence";

命令:./opcodes_dumper  sample.php

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

  1. Script: sample.php 
  2. opnum   line                         opcode                                      op1                                      op2                                   result 
  3.     0      2                      ZEND_ECHO                               "laruence" 
  4.     1      4                    ZEND_RETURN                                        1 

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

 
 
出处:http://www.phpfensi.com/php/20210626/16684.html

 


相关教程