baiyanphp
所有視頻:https://segmentfault.com/a/11...node
首先複習幾個基本概念:segmentfault
opline:在zend虛擬機中,每條指令都是一個 opline,每一個opline由操做數、指令操做、返回值組成
opcode:每一個指令操做都對應一個 opcode(如ZEND_ASSIGN/ZEND_ADD等等),在PHP7中,有100多種指令操做,全部的指令集被稱做opcodes
handler:每一個opcode指令操做都對應一個 handler指令處理函數,處理函數中有具體的指令操做執行邏輯
struct _zend_op { const void *handler; //操做執行的函數 znode_op op1; //操做數1 znode_op op2; //操做數2 znode_op result; //返回值 uint32_t extended_value; //擴展值 uint32_t lineno; //行號 zend_uchar opcode; //opcode值 zend_uchar op1_type; //操做數1的類型 zend_uchar op2_type; //操做數2的類型 zend_uchar result_type; //返回值的類型 };
#define IS_CONST (1<<0) #define IS_TMP_VAR (1<<1) #define IS_VAR (1<<2) #define IS_UNUSED (1<<3) /* Unused variable */ #define IS_CV (1<<4) /* Compiled variable */
IS_CONST類型:值爲1,表示常量,如$a = 1中的1或者$a = "hello world"中的hello world
IS_TMP_VAR類型:值爲2,表示臨時變量,如$a=」123」.time(); 這裏拼接的臨時變量」123」.time()的類型就是IS_TMP_VAR,通常用於操做的中間結果
IS_VAR類型:值爲4,表示變量,可是這個變量並非PHP中常見的聲明變量,而是返回的臨時變量,如$a = time()中的time()
IS_UNUSED:值爲8,表示沒有使用的操做數
IS_CV:值爲16,表示形如$a這樣的變量
struct _zend_op_array { uint32_t last; //下面oplines數組大小 zend_op *opcodes; //oplines數組,存放全部指令 int last_var;//操做數類型爲IS_CV的個數 uint32_t T;//操做數類型爲IS_VAR和IS_TMP_VAR的個數之和 zend_string **vars;//存放IS_CV類型操做數的數組 ... int last_literal;//下面常量數組大小 zval *literals;//存放IS_CONST類型操做數的數組 };
<?php $a = 2;
ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value) { zend_execute_data *execute_data; if (EG(exception) != NULL) { return; } execute_data = zend_vm_stack_push_call_frame(ZEND_CALL_TOP_CODE | ZEND_CALL_HAS_SYMBOL_TABLE, (zend_function*)op_array, 0, zend_get_called_scope(EG(current_execute_data)), zend_get_this_object(EG(current_execute_data))); if (EG(current_execute_data)) { execute_data->symbol_table = zend_rebuild_symbol_table(); } else { execute_data->symbol_table = &EG(symbol_table); } EX(prev_execute_data) = EG(current_execute_data); i_init_code_execute_data(execute_data, op_array, return_value); zend_execute_ex(execute_data); zend_vm_stack_free_call_frame(execute_data); }
struct _zend_execute_data { const zend_op *opline; //當前執行的指令 8B zend_execute_data *call; //指向本身的指針 8B zval *return_value; //存儲返回值 8B zend_function *func; //執行的函數 8B zval This; /* this + call_info + num_args 16B */ zend_execute_data *prev_execute_data; //鏈表,指向前一個zend_execute_data 8B zend_array *symbol_table; //符號表 8B #if ZEND_EX_USE_RUN_TIME_CACHE void **run_time_cache; /* cache op_array->run_time_cache 8B*/ #endif #if ZEND_EX_USE_LITERALS zval *literals; /* cache op_array->literals 8B */ #endif };
static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame(uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object) { uint32_t used_stack = zend_vm_calc_used_stack(num_args, func); return zend_vm_stack_push_call_frame_ex(used_stack, call_info, func, num_args, called_scope, object); }
static zend_always_inline zend_execute_data *zend_vm_stack_push_call_frame_ex(uint32_t used_stack, uint32_t call_info, zend_function *func, uint32_t num_args, zend_class_entry *called_scope, zend_object *object) { zend_execute_data *call = (zend_execute_data*)EG(vm_stack_top); ZEND_ASSERT_VM_STACK_GLOBAL; if (UNEXPECTED(used_stack > (size_t)(((char*)EG(vm_stack_end)) - (char*)call))) { call = (zend_execute_data*)zend_vm_stack_extend(used_stack); ZEND_ASSERT_VM_STACK_GLOBAL; zend_vm_init_call_frame(call, call_info | ZEND_CALL_ALLOCATED, func, num_args, called_scope, object); return call; } else { EG(vm_stack_top) = (zval*)((char*)call + used_stack); zend_vm_init_call_frame(call, call_info, func, num_args, called_scope, object); return call; } }
EG(vm_stack_top) = (zval*)((char*)call + used_stack);
int *p; p+3;
static zend_always_inline void i_init_code_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value) /* {{{ */ { ZEND_ASSERT(EX(func) == (zend_function*)op_array); EX(opline) = op_array->opcodes; EX(call) = NULL; EX(return_value) = return_value; zend_attach_symbol_table(execute_data); if (!op_array->run_time_cache) { op_array->run_time_cache = emalloc(op_array->cache_size); memset(op_array->run_time_cache, 0, op_array->cache_size); } EX_LOAD_RUN_TIME_CACHE(op_array); EX_LOAD_LITERALS(op_array); EG(current_execute_data) = execute_data; }
ZEND_API void zend_attach_symbol_table(zend_execute_data *execute_data) /* {{{ */ { zend_op_array *op_array = &execute_data->func->op_array; HashTable *ht = execute_data->symbol_table; /* copy real values from symbol table into CV slots and create INDIRECT references to CV in symbol table */ // 從符號表中拷貝真實的值到CV槽中,而且建立對符號表中CV變量的間接引用 if (EXPECTED(op_array->last_var)) { zend_string **str = op_array->vars; zend_string **end = str + op_array->last_var; zval *var = EX_VAR_NUM(0); do { zval *zv = zend_hash_find(ht, *str); if (zv) { if (Z_TYPE_P(zv) == IS_INDIRECT) { zval *val = Z_INDIRECT_P(zv); ZVAL_COPY_VALUE(var, val); } else { ZVAL_COPY_VALUE(var, zv); } } else { ZVAL_UNDEF(var); zv = zend_hash_add_new(ht, *str, var); } ZVAL_INDIRECT(zv, var); str++; var++; } while (str != end); } }
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_CONST_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zval *value; zval *variable_ptr; SAVE_OPLINE(); //從literals數組中獲取op2對應的值,也就是值2 value = EX_CONSTANT(opline->op2); //在execute_data的符號表中獲取op1的位置,也就是$a variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var); ... //最終將1賦值給$a value = zend_assign_to_variable(variable_ptr, value, IS_CONST); ... }