您的位置:首页 > 编程语言 > PHP开发

PHP函数源码之VLD实现原理

2016-09-04 14:08 477 查看
vld功能的实现要依赖 Zend引擎初始化(zend_startup)的时候 将zend_execute和zend_compile_file定义为函数指针的功劳了  

默认的

zend_execute
指向 execute

zend_compile_file
指向 compile_file

 

试想如果我们在实际编译和执行之前将zend_execute和zend_compile_file重写为其他的编译和执行函数,我们不就可以干点什么事吗

vld正是由此思路实现的 ,在每次请求初始化的钩子函数(PHP_RINIT_FUNCTION)中,将zend_execute和zend_compile_file替换成自己的vld_execute和vld_compile_file,这两个函数其实是对原始函数进行了封装,添加了输出opcode信息的附加功能,因为引擎初始化是发生在模块请求初始化之前,而模块请求初始化又是在编译和执行之前,这样就实现了查看opcode的功能。

基本思路了解清楚,下面我们就看代码实现部分:

原本zend_compile_file zend_compile_string还是要执行的

static zend_op_array* (*old_compile_file)(zend_file_handle* file_handle, int type TSRMLS_DC);
static zend_op_array* vld_compile_file(zend_file_handle*, int TSRMLS_DC);

static zend_op_array* (*old_compile_string)(zval *source_string, char *filename TSRMLS_DC);
static zend_op_array* vld_compile_string(zval *source_string, char *filename TSRMLS_DC);

#if PHP_VERSION_ID >= 50500
static void (*old_execute_ex)(zend_execute_data *execute_data TSRMLS_DC);
static void vld_execute_ex(zend_execute_data *execute_data TSRMLS_DC);
#else
static void (*old_execute)(zend_op_array *op_array TSRMLS_DC);
static void vld_execute(zend_op_array *op_array TSRMLS_DC);
#endif

接下来是请求初始化的钩子函数

PHP_RINIT_FUNCTION(vld)
{
/* 记录下原本的zend_compile_file, zend_compile_string, zend_execute_ex */
old_compile_file = zend_compile_file;
#if (PHP_MAJOR_VERSION > 5) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 2)
old_compile_string = zend_compile_string;
#endif
#if PHP_VERSION_ID >= 50500
old_execute_ex = zend_execute_ex;
#else
old_execute = zend_execute;
#endif

/* 如果激活vld挂钩 */
if (VLD_G(active)) {
/* 使用 自定义的 vld_compile_file */
zend_compile_file = vld_compile_file;
#if (PHP_MAJOR_VERSION > 5) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 2)
/* 使用 自定义的 vld_compile_string */
zend_compile_string = vld_compile_string;
#endif
/* 是否执行php脚本 */
if (!VLD_G(execute)) {
#if PHP_VERSION_ID >= 50500
zend_execute_ex = vld_execute_ex;
#else
zend_execute = vld_execute;
#endif
}
}

/* 是否输出至文件 */
if (VLD_G(save_paths)) {
char *filename;

filename = malloc(strlen("paths.dot") + strlen(VLD_G(save_dir)) + 2);
sprintf(filename, "%s/%s", VLD_G(save_dir), "paths.dot");

VLD_G(path_dump_file) = fopen(filename, "w");
free(filename);

if (VLD_G(path_dump_file)) {
fprintf(VLD_G(path_dump_file), "digraph {\n");
}
}
return SUCCESS;
}


接下来 在运行脚本 php_execute_script 中 调用 

vld_compile_file---------------->old_compile_file--------------->vld_dump_oparray

vld_compile_string------------->old_compile_file--------------->vld_dump_oparray

我们来看看vld_dump_oparray

void vld_dump_oparray(zend_op_array *opa TSRMLS_DC)
{
unsigned int i;
vld_set *set;
vld_branch_info *branch_info;
unsigned int base_address = (unsigned int)(zend_intptr_t)&(opa->opcodes[0]);

set = vld_set_create(opa->last);
branch_info = vld_branch_info_create(opa->last);

if (VLD_G(dump_paths)) {
vld_analyse_oparray(opa, set, branch_info TSRMLS_CC);
}
if (VLD_G(format)) {
vld_printf (stderr, "filename:%s%s\n", VLD_G(col_sep), ZSTRING_VALUE(opa->filename));
vld_printf (stderr, "function name:%s%s\n", VLD_G(col_sep), ZSTRING_VALUE(opa->function_name));
vld_printf (stderr, "number of ops:%s%d\n", VLD_G(col_sep), opa->last);
} else {
vld_printf (stderr, "filename: %s\n", ZSTRING_VALUE(opa->filename));
vld_printf (stderr, "function name: %s\n", ZSTRING_VALUE(opa->function_name));
vld_printf (stderr, "number of ops: %d\n", opa->last);
}
#ifdef IS_CV /* PHP >= 5.1 */
vld_printf (stderr, "compiled vars: ");
for (i = 0; i < opa->last_var; i++) {
vld_printf (stderr, "!%d = $%s%s", i, OPARRAY_VAR_NAME(opa->vars[i]), ((i + 1) == opa->last_var) ? "\n" : ", ");
}
if (!opa->last_var) {
vld_printf(stderr, "none\n");
}
#endif
if (VLD_G(format)) {
vld_printf(stderr, "line%s# *%s%s%sop%sfetch%sext%sreturn%soperands\n",VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep),VLD_G(col_sep));
} else {
vld_printf(stderr, "line #* E I O op fetch ext return operands\n");
vld_printf(stderr, "-------------------------------------------------------------------------------------\n");
}
/* 遍历opcode */
for (i = 0; i < opa->last; i++) {
vld_dump_op(i, opa->opcodes, base_address, vld_set_in(set, i), vld_set_in(branch_info->entry_points, i), vld_set_in(branch_info->starts, i), vld_set_in(branch_info->ends, i), opa TSRMLS_CC);
}
vld_printf(stderr, "\n");

if (VLD_G(dump_paths)) {
vld_branch_post_process(opa, branch_info);
vld_branch_find_paths(branch_info);
vld_branch_info_dump(opa, branch_info TSRMLS_CC);
}

vld_set_free(set);
vld_branch_info_free(branch_info);
}

就这样opcode 信息就出来了

最后在请求结束后 要恢复原本的指针函数

PHP_RSHUTDOWN_FUNCTION(vld)
{
zend_compile_file = old_compile_file;
#if PHP_VERSION_ID >= 50500
zend_execute_ex = old_execute_ex;
#else
zend_execute = old_execute;
#endif

if (VLD_G(path_dump_file)) {
fprintf(VLD_G(path_dump_file), "}\n");
fclose(VLD_G(path_dump_file));
}

return SUCCESS;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息