本篇文章的內容介紹的是關於PHP7核心剖析5之 PHP代碼的編譯 ,現在分享給大家,有需要的朋友可以參考一下
1.PHP代碼的編譯
PHP的解析過程任務就是將PHP代碼轉化為opcode數組,代碼裡的所有資訊都儲存在opcode數組中,然後將opcode數組交給zend引擎執行,opcode就是核心具體執行的命令,比如賦值、加減操作、函數調用等,每一條opcode都對應一個處理handle,這些handler是提前定義好的C函數。
2.PHP代碼->抽象文法樹(AST)
PHP使用re2c、bison完成這個階段的工作: re2c: 詞法分析器,將輸入分割為一個個有意義的詞塊,稱為token bison: 文法分析器,確定詞法分析器分割出的token是如何彼此關聯的
詞法、文法解析過程1.yyparse(zendparse)調用yylex(zendlex),當讀取到一個合法的token時,傳回值為token類型2.yylex調用lex_scan讀取合法的token值3.yyparse將token類型與token值構造抽象文法樹,最後將根節點儲存到CG(compiler_globals ,Zend編譯器相關的全域變數)的ast中
3.AST節點結構
typedef struct _zend_ast zend_ast;//普通節點類型struct _zend_ast { zend_ast_kind kind; //節點類型 zend_ast_attr attr; //節點附加屬性 uint32_t lineno; //行號 zend_ast *child[1]; //子節點數組};//普通節點類型,但有子節點的個數typedef struct _zend_ast_list { zend_ast_kind kind; //節點類型 zend_ast_attr attr; //節點附加屬性 uint32_t lineno; //行號 uint32_t children; //子節點數量 zend_ast *child[1];//子節點數組} zend_ast_list;//函數、類的ast節點結構typedef struct _zend_ast_decl { zend_ast_kind kind; //節點類型 zend_ast_attr attr; //節點附加屬性 uint32_t start_lineno; //開始行號 uint32_t end_lineno; //結束行號 uint32_t flags; unsigned char *lex_pos; zend_string *doc_comment; zend_string *name; zend_ast *child[4]; //類中會將繼承的父類、實現的介面以及類中的語句解析儲存在child中} zend_ast_decl;
執行個體:$a = 123;$b = "hi~";echo $a,$b;
4.zend_op_array
zend_op *opcodes; //opcode指令數組zval *literals; //字面量(常量)數組,這些都是在PHP代碼定義的一些值zend_string **vars; //PHP變數名數組,根據變數編號可以擷取相應的變數
//opcode指令結構struct _zend_op { const void *handler; //指令執行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; //傳回值類型};
5.handler處理函數
handler為每條opcode對應的C語言編寫的處理過程,所有opcode對應的處理流程定義在zend_vm_def.h中,opcode的處理過程有三種不同的提供形式:CALL、SWITCH、GOTO,預設為CALL
CALL:把每種opcode負責的工作封裝成不同的function,然後執行器迴圈調用執行SWITCH:把所有的處理方式寫到一個switch下,然後通過case不同的opcode執行具體的操作GOTO:把所有opcode的處理方式通過C語言裡面的label標籤區分開,然後執行器執行的時候goto到相應的位置處理
6.抽象文法樹->Opcodes
void zend_compile_top_stmt(zend_ast *ast){ .... if (ast->kind == ZEND_AST_STMT_LIST) { //第一次進來一定是這種類型 zend_ast_list *list = zend_ast_get_list(ast); uint32_t i; for (i = 0; i < list->children; ++i) { zend_compile_top_stmt(list->child[i]);//list各child語句相互獨立,遞迴編譯 } return; } //各語句編譯入口 zend_compile_stmt(ast); ....}1.zend_compile_top_stmt接收文法樹,首先判斷節點類型是否為ZEND_AST_STMT_LIST(表示當前節點下有多個獨立的節點),若是則進行遞迴2.當遞迴結束後,調用zend_compile_stmt進行編譯成opcodes