摘要:代碼的編譯的解析過程任務(wù)就是將代碼轉(zhuǎn)化為數(shù)組,代碼里的所有信息都保存在數(shù)組中,然后將數(shù)組交給引擎執(zhí)行,就是內(nèi)核具體執(zhí)行的命令,比如賦值加減操作函數(shù)調(diào)用等,每一條都對應(yīng)一個處理,這些是提前定義好的函數(shù)。
1.PHP代碼的編譯
PHP的解析過程任務(wù)就是將PHP代碼轉(zhuǎn)化為opcode數(shù)組,代碼里的所有信息都保存在opcode數(shù)組中,然后將opcode數(shù)組交給zend引擎執(zhí)行,opcode就是內(nèi)核具體執(zhí)行的命令,比如賦值、加減操作、函數(shù)調(diào)用等,每一條opcode都對應(yīng)一個處理handle,這些handler是提前定義好的C函數(shù)。
2.PHP代碼->抽象語法樹(AST)
PHP使用re2c、bison完成這個階段的工作: re2c: 詞法分析器,將輸入分割為一個個有意義的詞塊,稱為token bison: 語法分析器,確定詞法分析器分割出的token是如何彼此關(guān)聯(lián)的
詞法、語法解析過程 1.yyparse(zendparse)調(diào)用yylex(zendlex),當讀取到一個合法的token時,返回值為token類型 2.yylex調(diào)用lex_scan讀取合法的token值 3.yyparse將token類型與token值構(gòu)造抽象語法樹,最后將根節(jié)點保存到CG(compiler_globals ,Zend編譯器相關(guān)的全局變量)的ast中
3.AST節(jié)點結(jié)構(gòu)
typedef struct _zend_ast zend_ast; //普通節(jié)點類型 struct _zend_ast { zend_ast_kind kind; //節(jié)點類型 zend_ast_attr attr; //節(jié)點附加屬性 uint32_t lineno; //行號 zend_ast *child[1]; //子節(jié)點數(shù)組 }; //普通節(jié)點類型,但有子節(jié)點的個數(shù) typedef struct _zend_ast_list { zend_ast_kind kind; //節(jié)點類型 zend_ast_attr attr; //節(jié)點附加屬性 uint32_t lineno; //行號 uint32_t children; //子節(jié)點數(shù)量 zend_ast *child[1];//子節(jié)點數(shù)組 } zend_ast_list; //函數(shù)、類的ast節(jié)點結(jié)構(gòu) typedef struct _zend_ast_decl { zend_ast_kind kind; //節(jié)點類型 zend_ast_attr attr; //節(jié)點附加屬性 uint32_t start_lineno; //開始行號 uint32_t end_lineno; //結(jié)束行號 uint32_t flags; unsigned char *lex_pos; zend_string *doc_comment; zend_string *name; zend_ast *child[4]; //類中會將繼承的父類、實現(xiàn)的接口以及類中的語句解析保存在child中 } zend_ast_decl;
實例: $a = 123; $b = "hi~"; echo $a,$b;
4.zend_op_array
struct _zend_op_array { zend_op *opcodes; //opcode指令數(shù)組 int last_var; //編譯前此值為0,然后發(fā)現(xiàn)一個新變量這個值就加1(op_type為IS_CV的變量) uint32_t T;//臨時變量數(shù):op_type為IS_TMP_VAR、IS_VAR的變量 int last_literal; //字面量數(shù)量 zval *literals; //字面量(常量)數(shù)組,這些都是在PHP代碼定義的一些值 zend_string **vars; //PHP變量名數(shù)組,這個數(shù)組在ast編譯期間配合last_var用來確定各個變量的編號 HashTable *static_variables;//靜態(tài)變量符號表:通過static聲明的 int cache_size; //運行時緩存數(shù)組大小 void **run_time_cache; //運行時緩存,主要用于緩存一些znode_op以便于快速獲取數(shù)據(jù) };
//opcode指令結(jié)構(gòu) struct _zend_op { const void *handler; //指令執(zhí)行handler znode_op op1; //操作數(shù)1 znode_op op2; //操作數(shù)2 znode_op result; //返回值 uint32_t extended_value; uint32_t lineno; zend_uchar opcode; //opcode指令 zend_uchar op1_type; //操作數(shù)1類型 zend_uchar op2_type; //操作數(shù)2類型 zend_uchar result_type; //返回值類型 }; //操作數(shù)類型 #define IS_CONST (1<<0) //1:字面量,編譯時就可確定且不會改變的值,比如:$a = "hello~",其中字符串"hello~"就是常量 #define IS_TMP_VAR (1<<1) //2:臨時變量,比如:$a = "hello~" . time(),其中"hello~" . time()的值類型就是IS_TMP_VAR #define IS_VAR (1<<2) //4:PHP變量是沒有顯式的在PHP腳本中定義的,不是直接在代碼通過$var_name定義的。這個類型最常見的例子是PHP函數(shù)的返回值 #define IS_UNUSED (1<<3) //8:表示操作數(shù)沒有用 #define IS_CV (1<<4) //16:PHP腳本變量,即腳本里通過$var_name定義的變量,這些變量是編譯階段確定的
5.handler處理函數(shù)
handler為每條opcode對應(yīng)的C語言編寫的處理過程,所有opcode對應(yīng)的處理過程定義在zend_vm_def.h中,opcode的處理過程有三種不同的提供形式:CALL、SWITCH、GOTO,默認方式為CALL
CALL:把每種opcode負責的工作封裝成不同的function,然后執(zhí)行器循環(huán)調(diào)用執(zhí)行 SWITCH:把所有的處理方式寫到一個switch下,然后通過case不同的opcode執(zhí)行具體的操作 GOTO:把所有opcode的處理方式通過C語言里面的label標簽區(qū)分開,然后執(zhí)行器執(zhí)行的時候goto到相應(yīng)的位置處理
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接收語法樹,首先判斷節(jié)點類型是否為ZEND_AST_STMT_LIST(表示當前節(jié)點下 有多個獨立的節(jié)點),若是則進行遞歸 2.當遞歸結(jié)束后,調(diào)用zend_compile_stmt進行編譯成opcodes
實例: $a = 123; $b = "hi~"; echo $a,$b;
注意:這里變量的編號從0、1、2、3...依次遞增的,但是實際使用中并不是直接用的這個下標,而是轉(zhuǎn)化成了內(nèi)存偏移量offset,這個是ZEND_CALL_VAR_NUM宏處理的,所以變量偏移量實際是96、112、128...遞增的
pass_two()主要有兩個重要操作: 1.將IS_CONST、IS_VAR、IS_TMP_VAR類型的操作數(shù)、返回值轉(zhuǎn)化為內(nèi)存偏移量 2.另外一個重要操作就是設(shè)置各指令的處理handler
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/28560.html
摘要:局部變量中局部變量分配在結(jié)構(gòu)上,每次執(zhí)行都會生成一個新的,局部變量在執(zhí)行之初分配,然后在執(zhí)行結(jié)束時釋放,這是局部變量的生命周期。 1.局部變量 PHP中局部變量分配在zend_execute_data結(jié)構(gòu)上,每次執(zhí)行zend_op_array都會生成一個新的zend_execute_data,局部變量在執(zhí)行之初分配,然后在執(zhí)行結(jié)束時釋放,這是局部變量的生命周期。 讀寫操作:局部變量通過...
摘要:是與之間數(shù)據(jù)交換的一種協(xié)議。當收到這個請求后,會啟動對應(yīng)的程序,這里就是的解析器。接下來解析器會解析文件,初始化執(zhí)行環(huán)境,然后處理請求,再以規(guī)定規(guī)定的格式返回處理后的結(jié)果,退出進程,再把結(jié)果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數(shù)據(jù)交換的一種協(xié)議。FastCGI:同 CGI,是一種通信協(xié)議,但比 CGI 在效率上做了一些優(yōu)化。PHP-...
摘要:是與之間數(shù)據(jù)交換的一種協(xié)議。當收到這個請求后,會啟動對應(yīng)的程序,這里就是的解析器。接下來解析器會解析文件,初始化執(zhí)行環(huán)境,然后處理請求,再以規(guī)定規(guī)定的格式返回處理后的結(jié)果,退出進程,再把結(jié)果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數(shù)據(jù)交換的一種協(xié)議。FastCGI:同 CGI,是一種通信協(xié)議,但比 CGI 在效率上做了一些優(yōu)化。PHP-...
摘要:是與之間數(shù)據(jù)交換的一種協(xié)議。當收到這個請求后,會啟動對應(yīng)的程序,這里就是的解析器。接下來解析器會解析文件,初始化執(zhí)行環(huán)境,然后處理請求,再以規(guī)定規(guī)定的格式返回處理后的結(jié)果,退出進程,再把結(jié)果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數(shù)據(jù)交換的一種協(xié)議。FastCGI:同 CGI,是一種通信協(xié)議,但比 CGI 在效率上做了一些優(yōu)化。PHP-...
1.EG(executor_globals/zend_executor_globals) PHP整個生命周期中最主要的一個結(jié)構(gòu),是一個全局變量,在main執(zhí)行前分配(非ZTS下),直到PHP退出,它記錄著當前請求全部的信息 showImg(https://segmentfault.com/img/bV8fW0?w=960&h=777); 2.EX(execute_data/zend_execut...
閱讀 2134·2021-11-18 10:02
閱讀 2886·2021-09-04 16:41
閱讀 1177·2019-08-30 15:55
閱讀 1443·2019-08-29 17:27
閱讀 1160·2019-08-29 17:12
閱讀 2563·2019-08-29 15:38
閱讀 2886·2019-08-29 13:02
閱讀 2859·2019-08-29 12:29