

) 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
呵呵,怎么样,是不是很好玩呢?
源码地址:http://code.google.com/p/opcodesdumper/







Apache
browser
bug
C++
charset
COOKIE
core
c写PHP扩展
debug
encoding
engine
Extension
GET
IE
javascript
js
json
Module
mysql
namespace
nginx
PHP
php5.4
PHP5.4新特性
PHP extension
php原理
PHP应用
PHP扩展
php源码
php源码分析
SAPI
session
utf8
variable
vim
Yaf
Zend/PHP
乱码
作用域
原理
开发php扩展
性能
扩展PHP
扩展开发
正则 GNU C/C++ (3)
Js/CSS (24)
Linux/Unix (15)
MySQL/PostgreSQL (7)
PHP Extension (13)
PHP应用 (138)
PHP源码分析 (72)
转载 (30)
随笔 (68)
WP Cumulus Flash tag cloud by Roy Tanck and Luke Morton requires Flash Player 9 or better.









很通俗易懂。入门很好。
PS:这代码颜色配色不错,能分享一下吗