PHP provides a embed SAPI, that is, PHP allows you to invoke the functions provided by php/ze in C + + language. This article implements a PHP Opcodes viewer based on embed SAPI.
First of all, download the PHP source code for compiling, I now use PHP5.3 alpha2
Access to the source directory:
./configure--enable-embed--WITH-CONFIG-FILE-SCAN-DIR=/ETC/PHP.D--with-mysql--with-config-file-path=/etc/
./make
./make Install
Finally, remember to copy the generated libphp5.so to the runtime library directory, I copied directly to the/lib/, otherwise you will be running your own embed program when the error:
./embed:error while loading shared libraries:libphp5.so:cannot open Shared object file:no such file or directory
If you're not familiar with PHP's SAPI, I suggest you take a look at my article: a deep understanding of Zend Sapis (Zend SAPI Internals)
This time, you can embed PHP script parser in your C code, my example:
#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;
}
And then you're going to indicate include path, a simple 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)
After compiling successfully, run, we can see that stdout output Hello world!
Based on this, we can easily implement a VLD-like opcodes dumper:
First we define the opcode transformation functions (all opcodes can view zend/zend_vm_opcodes.h);
Char *opname (Zend_uchar opcode) {
switch (opcode) {case
Zend_nop:return "Zend_nop";
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;
/*... Omit .... * *
Default:return "Unknow";
Then define the output functions for Zval and 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 }
}
Then define the output function of the 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);}}
And finally, the main function of the program:
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;
}
Compile, run test script (sample.php):
Copy Code code as follows:
sample.php:
echo "Laruence";
Command:
Copy Code code as follows:
./opcodes_dumper sample.php
Get the output (if you're confused about the results below, I suggest you take a look at my article: a deep understanding of the opcodes of PHP Principles):
Script:sample.php
opnum line opcode OP1 OP2 Result
0 2 Zend_echo "laruence"
1 4 Zend_return 1
Oh, how, is not very fun?