baiyanphp
所有視頻:https://segmentfault.com/a/11...node
原視頻地址:http://replay.xesv5.com/ll/24...segmentfault
<?php $a = 1; $b = $a + 2;
操做數:參與指令操做的變量或常量等,只需OP1和OP2最多兩個操做數就夠了,由於多元運算能夠轉化成二元運算(op1/op2)
指令操做:用來描述具體的賦值/加減乘除等指令操做(opcode)
返回值:用來存儲中間運算結果(result)
處理函數:用來具體實現加減乘除等指令的操做邏輯(handler)
舉例:$a = 1 + 2; 這行代碼中,$a/1/2是操做數,1+2計算的中間結果3是返回值,加法和賦值是指令作的具體操做,加法對應加法的opcode與相應的handler,賦值對應賦值的opcode與相應的handler
opline:在zend虛擬機中,每條指令都是一個 opline,每一個opline由操做數、指令操做、返回值組成
opcode:每一個指令操做都對應一個 opcode(如ZEND_ASSIGN/ZEND_ADD等等),在PHP7中,有100多種指令操做,全部的指令集被稱做opcodes
handler:每一個opcode指令操做都對應一個 handler指令處理函數,處理函數中有具體的指令操做執行邏輯
static zend_op_array *zend_compile(int type) { zend_op_array *op_array = NULL; zend_bool original_in_compilation = CG(in_compilation); CG(in_compilation) = 1;//CG宏能夠取得compile_globals結構體中的字段 CG(ast) = NULL;//一開始的AST爲NULL CG(ast_arena) = zend_arena_create(1024 * 32);//給AST分配空間 if (!zendparse()) { //進行詞法和語法分析 ....... //初始化op_array,用來存放指令 op_array = emalloc(sizeof(zend_op_array)); init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE); CG(active_op_array) = op_array; ...... //對AST進行遍歷並生成指令,並存儲到zend_op_array中 zend_compile_top_stmt(CG(ast)); ...... //設置handler pass_two(op_array); } return op_array; }
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類型操做數的數組 };
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; //返回值類型 };
typedef union _znode_op { uint32_t constant; uint32_t var; uint32_t num; uint32_t opline_num; /* Needs to be signed */ #if ZEND_USE_ABS_JMP_ADDR zend_op *jmp_addr; #else uint32_t jmp_offset; #endif #if ZEND_USE_ABS_CONST_ADDR zval *zv; #endif } znode_op;
#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這樣的變量
咱們如今僅僅關注$a = 1這一行代碼並對其AST進行遍歷
void zend_compile_top_stmt(zend_ast *ast) { if (!ast) { return; } if (ast->kind == ZEND_AST_STMT_LIST) { //若是是這個AST是LIST類型 zend_ast_list *list = zend_ast_get_list(ast);//將其轉換成ZEND_AST_LIST類型便可(上一篇筆記是在gdb下直接強轉的) uint32_t i; for (i = 0; i < list->children; ++i) { zend_compile_top_stmt(list->child[i]); //遞歸調用,進行深度遍歷 } return; } zend_compile_stmt(ast); //編譯的入口。遞歸調用的時候,若是往下走的時候並不是LIST型結點,會調用這個函數 if (ast->kind != ZEND_AST_NAMESPACE && ast->kind != ZEND_AST_HALT_COMPILER) { zend_verify_namespace(); } if (ast->kind == ZEND_AST_FUNC_DECL || ast->kind == ZEND_AST_CLASS) { CG(zend_lineno) = ((zend_ast_decl *) ast)->end_lineno; zend_do_early_binding(); } }
具體代碼執行過程(按照最開始的AST圖來說):數組
void zend_compile_stmt(zend_ast *ast) { if (!ast) { return; } CG(zend_lineno) = ast->lineno; if ((CG(compiler_options) & ZEND_COMPILE_EXTENDED_INFO) && !zend_is_unticked_stmt(ast)) { zend_do_extended_info(); } switch (ast->kind) { case ZEND_AST_STMT_LIST: zend_compile_stmt_list(ast); break; ...... default: //最終會走到這裏,由於全部的case中,沒有ZEND_AST_ASSIGN類型與之匹配 { znode result; //聲明瞭一個znode類型變量,存儲返回值(像$a = 1 + 2)這種須要存儲中間結果3的表達式才須要使用這個result zend_compile_expr(&result, ast); //調用這個函數處理當前表達式,下面會展開 zend_do_free(&result); } } ...... }
typedef struct _znode { zend_uchar op_type; zend_uchar flag; union { znode_op op; //操做數變量的位置 zval constant; //常量 } u; } znode;
void zend_compile_expr(znode *result, zend_ast *ast) { ...... switch (ast->kind) { ...... case ZEND_AST_ASSIGN: zend_compile_assign(result, ast); //代碼走到這裏,調用這個函數,下面繼續跟進 return; ...... } }
void zend_compile_assign(znode *result, zend_ast *ast) { zend_ast *var_ast = ast->child[0]; //517的第一個孩子256 zend_ast *expr_ast = ast->child[1]; //517的第二個孩子64 znode var_node, expr_node; //存儲編譯後的中間結果 zend_op *opline; uint32_t offset; ... switch (var_ast->kind) { case ZEND_AST_VAR: case ZEND_AST_STATIC_PROP: offset = zend_delayed_compile_begin(); zend_delayed_compile_var(&var_node, var_ast, BP_VAR_W); //編譯$a,中間結果放到var_node這個znode上 zend_compile_expr(&expr_node, expr_ast); //編譯1,中間結果放到expr_node這個znode上 zend_delayed_compile_end(offset); zend_emit_op(result, ZEND_ASSIGN, &var_node, &expr_node); //生成opline return; ...... } }
void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type) { zend_op *opline; switch (ast->kind) { case ZEND_AST_VAR: zend_compile_simple_var(result, ast, type, 1); return; ... } }
static void zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t type, int delayed) { zend_op *opline; if (is_this_fetch(ast)) { ...... } else if (zend_try_compile_cv(result, ast) == FAILURE) { ...... } }
static int zend_try_compile_cv(znode *result, zend_ast *ast) { zend_ast *name_ast = ast->child[0]; if (name_ast->kind == ZEND_AST_ZVAL) { zend_string *name = zval_get_string(zend_ast_get_zval(name_ast)); if (zend_is_auto_global(name)) { zend_string_release(name); return FAILURE; } result->op_type = IS_CV; //將其類型標記爲CV,CV變量在運行時是存在棧上的 result->u.op.var = lookup_cv(CG(active_op_array), name); //返回這個CV變量在運行時棧上的偏移量 name = CG(active_op_array)->vars[EX_VAR_TO_NUM(result->u.op.var)]; return SUCCESS; } return FAILURE; }
static zend_always_inline zend_string *_zval_get_string(zval *op) { return Z_TYPE_P(op) == IS_STRING ? zend_string_copy(Z_STR_P(op)) : _zval_get_string_func(op); }
/* we should never set just Z_TYPE, we should set Z_TYPE_INFO */ #define Z_TYPE(zval) zval_get_type(&(zval)) #define Z_TYPE_P(zval_p) Z_TYPE(*(zval_p)) static zend_always_inline zend_uchar zval_get_type(const zval* pz) { return pz->u1.v.type; } ... #define Z_STR(zval) (zval).value.str #define Z_STR_P(zval_p) Z_STR(*(zval_p))
static zend_always_inline zend_string *zend_string_copy(zend_string *s) { if (!ZSTR_IS_INTERNED(s)) { GC_REFCOUNT(s)++; } return s; }
result->op_type = IS_CV; result->u.op.var = lookup_cv(CG(active_op_array), name);
static int lookup_cv(zend_op_array *op_array, zend_string* name) /* {{{ */{ int i = 0; zend_ulong hash_value = zend_string_hash_val(name); while (i < op_array->last_var) { if (ZSTR_VAL(op_array->vars[i]) == ZSTR_VAL(name) || (ZSTR_H(op_array->vars[i]) == hash_value && ZSTR_LEN(op_array->vars[i]) == ZSTR_LEN(name) && memcmp(ZSTR_VAL(op_array->vars[i]), ZSTR_VAL(name), ZSTR_LEN(name)) == 0)) { zend_string_release(name); return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i); } i++; } i = op_array->last_var; op_array->last_var++; if (op_array->last_var > CG(context).vars_size) { CG(context).vars_size += 16; /* FIXME */ op_array->vars = erealloc(op_array->vars, CG(context).vars_size * sizeof(zend_string*)); } op_array->vars[i] = zend_new_interned_string(name); return (int)(zend_intptr_t)ZEND_CALL_VAR_NUM(NULL, i); }
case ZEND_AST_ZVAL: ZVAL_COPY(&result->u.constant, zend_ast_get_zval(ast)); result->op_type = IS_CONST; return;
static zend_always_inline zval *zend_ast_get_zval(zend_ast *ast) { ZEND_ASSERT(ast->kind == ZEND_AST_ZVAL); return &((zend_ast_zval *) ast)->val; }
struct _zval_struct { zend_value value; /* 存儲變量的值 */ union { struct { ZEND_ENDIAN_LOHI_4( //大小端問題,詳情看"PHP內存管理3筆記」 zend_uchar type, //注意這裏就是存放變量類型的地方,char類型 zend_uchar type_flags, //類型標記 zend_uchar const_flags, //是不是常量 zend_uchar reserved) //保留字段 } v; uint32_t type_info; } u1; union { uint32_t next; /* 數組模擬鏈表,鏈地址法解決哈希衝突時使用 */ uint32_t cache_slot; /* literal cache slot */ uint32_t lineno; /* line number (for ast nodes) */ uint32_t num_args; /* arguments number for EX(This) */ uint32_t fe_pos; /* foreach position */ uint32_t fe_iter_idx; /* foreach iterator index */ uint32_t access_flags; /* class constant access flags */ uint32_t property_guard; /* single property guard */ uint32_t extra; /* not further specified */ } u2; };
#define ZVAL_COPY(z, v) \ do { \ zval *_z1 = (z); \ const zval *_z2 = (v); \ zend_refcounted *_gc = Z_COUNTED_P(_z2); \ uint32_t _t = Z_TYPE_INFO_P(_z2); \ ZVAL_COPY_VALUE_EX(_z1, _z2, _gc, _t); \ if ((_t & (IS_TYPE_REFCOUNTED << Z_TYPE_FLAGS_SHIFT)) != 0) { \ GC_REFCOUNT(_gc)++; \ } \ } while (0)
#define Z_COUNTED(zval) (zval).value.counted #define Z_COUNTED_P(zval_p) Z_COUNTED(*(zval_p))
typedef union _zend_value { zend_long lval; //整型 double dval; //浮點 zend_refcounted *counted; //引用計數,這裏取出這個字段的值 zend_string *str; //字符串 zend_array *arr; //數組 zend_object *obj; //對象 zend_resource *res; //資源 zend_reference *ref; //引用 zend_ast_ref *ast; //抽象語法樹 zval *zv; //內部使用 void *ptr; //不肯定類型,取出來以後強轉 zend_class_entry *ce; //類 zend_function *func;//函數 struct { uint32_t w1; uint32_t w2; } ww; //這個union一共8B,這個結構體每一個字段都是4B,由於全部聯合體字段共用一塊內存,故至關於取了一半的union } zend_value;
#define Z_TYPE_INFO(zval) (zval).u1.type_info #define Z_TYPE_INFO_P(zval_p) Z_TYPE_INFO(*(zval_p))
# define ZVAL_COPY_VALUE_EX(z, v, gc, t) \ do { \ Z_COUNTED_P(z) = gc; \ Z_TYPE_INFO_P(z) = t; \ } while (0) #else
爲何會出現(z)這種語法,不加括號能夠嗎?
爲何要do{}while(0),反正都是隻執行一次這個宏的代碼,能夠去掉do{}while(0)嗎?
#define X(a, b) \ a = b * 3;
#define X(a, b) \ a = b * 3; a = a + 1;
if (true) X(a, b)
if (true) a = b * 3; a = a +1;
if (true) do { a = b * 3; a = a +1; } while(0)
static zend_op *zend_emit_op(znode *result, zend_uchar opcode, znode *op1, znode *op2) /* {{{ */ { zend_op *opline = get_next_op(CG(active_op_array)); opline->opcode = opcode; if (op1 == NULL) { SET_UNUSED(opline->op1); } else { SET_NODE(opline->op1, op1); } if (op2 == NULL) { SET_UNUSED(opline->op2); } else { SET_NODE(opline->op2, op2); } zend_check_live_ranges(opline); if (result) { zend_make_var_result(result, opline); } return opline; }
#define SET_NODE(target, src) do { \ target ## _type = (src)->op_type; \ if ((src)->op_type == IS_CONST) { \ target.constant = zend_add_literal(CG(active_op_array), &(src)->u.constant); \ } else { \ target = (src)->u.op; \ } \ } while (0)