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 %040sn", 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 %040sn", "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: %sn", 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: %sn", 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
呵呵,怎么样,是不是很好玩呢?
【说明】:本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:)!