国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

(PHP7內核剖析-5) PHP代碼的編譯

DevTTL / 544人閱讀

摘要:代碼的編譯的解析過程任務就是將代碼轉化為數組,代碼里的所有信息都保存在數組中,然后將數組交給引擎執行,就是內核具體執行的命令,比如賦值加減操作函數調用等,每一條都對應一個處理,這些是提前定義好的函數。

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

struct _zend_op_array {
    zend_op *opcodes; //opcode指令數組
    int last_var; //編譯前此值為0,然后發現一個新變量這個值就加1(op_type為IS_CV的變量)
    uint32_t T;//臨時變量數:op_type為IS_TMP_VAR、IS_VAR的變量
    int last_literal;  //字面量數量
    zval *literals; //字面量(常量)數組,這些都是在PHP代碼定義的一些值
    zend_string **vars; //PHP變量名數組,這個數組在ast編譯期間配合last_var用來確定各個變量的編號
    HashTable *static_variables;//靜態變量符號表:通過static聲明的
    int  cache_size; //運行時緩存數組大小
    void **run_time_cache; //運行時緩存,主要用于緩存一些znode_op以便于快速獲取數據
};
//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; //返回值類型
};

//操作數類型
#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函數的返回值
#define IS_UNUSED   (1<<3)  //8:表示操作數沒有用
#define IS_CV       (1<<4)  //16:PHP腳本變量,即腳本里通過$var_name定義的變量,這些變量是編譯階段確定的


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
實例:
$a = 123;
$b = "hi~";

echo $a,$b;
注意:這里變量的編號從0、1、2、3...依次遞增的,但是實際使用中并不是直接用的這個下標,而是轉化成了內存偏移量offset,這個是ZEND_CALL_VAR_NUM宏處理的,所以變量偏移量實際是96、112、128...遞增的

pass_two()主要有兩個重要操作:

1.將IS_CONST、IS_VAR、IS_TMP_VAR類型的操作數、返回值轉化為內存偏移量
2.另外一個重要操作就是設置各指令的處理handler

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28560.html

相關文章

  • (PHP7內核剖析-4) 局部變量,全局變量,常量

    摘要:局部變量中局部變量分配在結構上,每次執行都會生成一個新的,局部變量在執行之初分配,然后在執行結束時釋放,這是局部變量的生命周期。 1.局部變量 PHP中局部變量分配在zend_execute_data結構上,每次執行zend_op_array都會生成一個新的zend_execute_data,局部變量在執行之初分配,然后在執行結束時釋放,這是局部變量的生命周期。 讀寫操作:局部變量通過...

    yagami 評論0 收藏0
  • (PHP7內核剖析-1) CGI與FastCGI

    摘要:是與之間數據交換的一種協議。當收到這個請求后,會啟動對應的程序,這里就是的解析器。接下來解析器會解析文件,初始化執行環境,然后處理請求,再以規定規定的格式返回處理后的結果,退出進程,再把結果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數據交換的一種協議。FastCGI:同 CGI,是一種通信協議,但比 CGI 在效率上做了一些優化。PHP-...

    rubyshen 評論0 收藏0
  • (PHP7內核剖析-1) CGI與FastCGI

    摘要:是與之間數據交換的一種協議。當收到這個請求后,會啟動對應的程序,這里就是的解析器。接下來解析器會解析文件,初始化執行環境,然后處理請求,再以規定規定的格式返回處理后的結果,退出進程,再把結果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數據交換的一種協議。FastCGI:同 CGI,是一種通信協議,但比 CGI 在效率上做了一些優化。PHP-...

    Salamander 評論0 收藏0
  • (PHP7內核剖析-1) CGI與FastCGI

    摘要:是與之間數據交換的一種協議。當收到這個請求后,會啟動對應的程序,這里就是的解析器。接下來解析器會解析文件,初始化執行環境,然后處理請求,再以規定規定的格式返回處理后的結果,退出進程,再把結果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數據交換的一種協議。FastCGI:同 CGI,是一種通信協議,但比 CGI 在效率上做了一些優化。PHP-...

    abson 評論0 收藏0
  • (PHP7內核剖析-7) Zend引擎執行過程

    1.EG(executor_globals/zend_executor_globals) PHP整個生命周期中最主要的一個結構,是一個全局變量,在main執行前分配(非ZTS下),直到PHP退出,它記錄著當前請求全部的信息 showImg(https://segmentfault.com/img/bV8fW0?w=960&h=777); 2.EX(execute_data/zend_execut...

    elisa.yang 評論0 收藏0

發表評論

0條評論

DevTTL

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<