如何使用PHP Embed SAPI實(shí)現(xiàn)Opcodes查看器
PHP提供了一個(gè)Embed SAPI,也就是說,PHP容許你在C/C++語言中調(diào)用PHP/ZE提供的函數(shù)。本文就通過基于Embed SAPI實(shí)現(xiàn)一個(gè)PHP的opcodes查看器。
首先,下載PHP源碼以供編譯, 我現(xiàn)在使用的是PHP5.3 alpha2
進(jìn)入源碼目錄:
./configure --enable-embed --with-config-file-scan-dir=/etc/php.d --with-mysql --with-config-file-path=/etc/
./make
./make install
最后,記得要將生成的libphp5.so復(fù)制到運(yùn)行時(shí)庫的目錄,我直接拷貝到了/lib/, 否則會(huì)在運(yùn)行你自己的embed程序的時(shí)候報(bào)錯(cuò):
./embed: error while loading shared libraries: libphp5.so: cannot open shared object file: No such file or directory
如果你對(duì)PHP的SAPI還不熟悉的話,我建議你看看我的這篇文章:深入理解Zend SAPIs(Zend SAPI Internals)
這個(gè)時(shí)候,你就可以在你的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了,一個(gè)簡(jiǎn)單的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)
編譯成功以后, 運(yùn)行,我們可以看到, stdout輸出 Hello World!
基于這個(gè),我們就可以很容易的實(shí)現(xiàn)一個(gè)類似于vld的Opcodes dumper:
首先我們定義opcode的轉(zhuǎn)換函數(shù)(全部的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的輸出函數(shù):
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的輸出函數(shù):
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); } } }
最后,就是程序的主函數(shù)了:
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; }
編譯,運(yùn)行測(cè)試腳本(sample.php):
sample.php:
echo "laruence";
命令:
./opcodes_dumper sample.php
得到輸出結(jié)果(如果你對(duì)下面的結(jié)果很迷惑,那么建議你再看看我的這篇文章:深入理解PHP原理之Opcodes):
Script: sample.php
opnum line opcode op1 op2 result
0 2 ZEND_ECHO "laruence"
1 4 ZEND_RETURN 1
呵呵,怎么樣,是不是很好玩呢?
相關(guān)文章
用php和jQuery來實(shí)現(xiàn)“頂”和“踩”的投票功能
本文結(jié)合實(shí)例,講解使用PHP+MySql+jQuery實(shí)現(xiàn)的“頂”和“踩”投票功能,判斷用戶的投票行為是否有效。2016-10-10Zend Framework動(dòng)作助手Url用法詳解
這篇文章主要介紹了Zend Framework動(dòng)作助手Url用法,結(jié)合實(shí)例形式分析了動(dòng)作助手Url的功能,定義與相關(guān)使用技巧,需要的朋友可以參考下2016-03-03PHP實(shí)現(xiàn)的英文名字全拼隨機(jī)排號(hào)腳本
這篇文章主要介紹了PHP實(shí)現(xiàn)的英文名字全拼隨機(jī)排號(hào)腳本,根據(jù)一個(gè)需求寫出的一個(gè)解決方案,需要的朋友可以參考下2014-07-07ThinkPHP基于PHPExcel導(dǎo)入Excel文件的方法
這篇文章主要介紹了ThinkPHP基于PHPExcel導(dǎo)入Excel文件的方法,對(duì)于Excel文件的上傳、讀取操作以及寫入數(shù)據(jù)庫等都做了較為詳盡的講述,在進(jìn)行項(xiàng)目開發(fā)的過程中非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10Laravle eloquent 多對(duì)多模型關(guān)聯(lián)實(shí)例詳解
Eloquent中一個(gè)模型就是一個(gè)數(shù)據(jù)表,數(shù)據(jù)表之間通常會(huì)有關(guān)聯(lián),多對(duì)多關(guān)聯(lián)就是2個(gè)表之間相互有很多關(guān)聯(lián),這篇文章給大家介紹了Laravle eloquent 多對(duì)多模型關(guān)聯(lián)實(shí)例詳解,需要的朋友參考下吧2017-11-11php使用多個(gè)進(jìn)程同時(shí)控制文件讀寫示例
這篇文章主要介紹了php使用多個(gè)進(jìn)程同時(shí)控制文件讀寫示例,需要的朋友可以參考下2014-02-02ZendFramework框架實(shí)現(xiàn)連接兩個(gè)或多個(gè)數(shù)據(jù)庫的方法
這篇文章主要介紹了ZendFramework框架實(shí)現(xiàn)連接兩個(gè)或多個(gè)數(shù)據(jù)庫的方法,涉及ZendFramework框架配置文件與數(shù)據(jù)庫操作相關(guān)技巧,需要的朋友可以參考下2016-12-12