<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 4.2 選擇結構 程序并不都是順序執行的,選擇結構用于判斷給定的條件,根據判斷的結果來控制程序的流程。PHP中通過if、elseif、else和switch語句實現條件控制。這一節我們就分析下PHP中兩種條件語句的具體實現。 ### 4.2.1 if語句 If語句用法: ```php if(Condition1){ Statement1; }elseif(Condition2){ Statement2; }else{ Statement3; } ``` IF語句有兩部分組成:condition(條件)、statement(聲明),每個條件分支對應一組這樣的組合,其中最后的else比較特殊,它沒有條件,編譯時也是按照這個邏輯編譯為一組組的condition和statement,其具體的語法規則如下: ```c if_stmt: if_stmt_without_else %prec T_NOELSE { $$ = $1; } | if_stmt_without_else T_ELSE statement { $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_IF_ELEM, NULL, $3)); } ; if_stmt_without_else: T_IF '(' expr ')' statement { $$ = zend_ast_create_list(1, ZEND_AST_IF, zend_ast_create(ZEND_AST_IF_ELEM, $3, $5)); } | if_stmt_without_else T_ELSEIF '(' expr ')' statement { $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_IF_ELEM, $4, $6)); } ; ``` 從上面的語法規則可以看出,編譯if語句時首先會創建一個`ZEND_AST_IF`的節點,這個節點是一個list,用于保存各個分支的condition、statement,編譯每個分支時將創建一個`ZEND_AST_IF_ELEM`的節點,它有兩個子節點,分別用來記錄:condition、statement,然后把這個節點插入到`ZEND_AST_IF`下,最終生成的AST: ![](https://box.kancloud.cn/bef9ce29a3362a0a1d39cb10affebb5c_906x375.png) 編譯opcode時順序編譯每個分支的condition、statement即可,編譯過程大致如下: * __(1)__ 編譯當前分支的condition語句,這里可能會有多個條件,但最終會歸并為一個true/false的結果; * __(2)__ 編譯完condition后編譯一條ZEND_JMPZ的opcode,這條opcode用來判斷當前condition最終為true還是false,如果當前condition成立直接繼續執行本組statement即可,無需進行跳轉,但是如果不成立就需要跳過本組的statement,所以這條opcode還需要知道該往下跳過多少條opcode,而跳過的這些opcode就是本組的statement,因此這個值需要在編譯完本組statement后才能確定,現在還無法確定; * __(3)__ 編譯當前分支的statement列表,其節點類型ZEND_AST_STMT_LIST,就是普通語句的編譯; * __(4)__ 編譯完statement后編譯一條ZEND_JMP的opcode,這條opcode是當condition成立執行完本組statement時跳出if的,因為當前分支既然條件成立就不需要再跳到其他分支,執行完當前分支的statement后將直接跳出if,所以ZEND_JMP需要知道該往下跳過多少opcode,而跳過的這些opcode是后面所有分支的opcode數,只有編譯完全部分支后才能確定; * __(5)__ 編譯完statement后再設置步驟(2)中條件不成立時ZEND_JMPZ應該跳過的opcode數; * __(6)__ 重復上面的過程依次編譯后面的condition、statement,編譯完全部分支后再設置各分支在步驟(4)中ZEND_JMP跳出if的opcode位置。 具體的編譯過程在`zend_compile_if()`中,過程比較清晰: ```c void zend_compile_if(zend_ast *ast) { zend_ast_list *list = zend_ast_get_list(ast); uint32_t i; uint32_t *jmp_opnums = NULL; //用來保存每個分支在步驟(4)中的ZEND_JMP opcode if (list->children > 1) { jmp_opnums = safe_emalloc(sizeof(uint32_t), list->children - 1, 0); } //依次編譯各個分支 for (i = 0; i < list->children; ++i) { zend_ast *elem_ast = list->child[i]; zend_ast *cond_ast = elem_ast->child[0]; //條件 zend_ast *stmt_ast = elem_ast->child[1]; //聲明 znode cond_node; uint32_t opnum_jmpz; if (cond_ast) { //編譯condition zend_compile_expr(&cond_node, cond_ast); //編譯condition跳轉opcode:ZEND_JMPZ opnum_jmpz = zend_emit_cond_jump(ZEND_JMPZ, &cond_node, 0); } //編譯statement zend_compile_stmt(stmt_ast); //編譯statement執行完后跳出if的opcode:ZEND_JMP(最后一個分支無需這條opcode) if (i != list->children - 1) { jmp_opnums[i] = zend_emit_jump(0); } if (cond_ast) { //設置ZEND_JMPZ跳過opcode數 zend_update_jump_target_to_next(opnum_jmpz); } } if (list->children > 1) { //設置前面各分支statement執行完后應跳轉的位置 for (i = 0; i < list->children - 1; ++i) { zend_update_jump_target_to_next(jmp_opnums[i]); //設置每組stmt最后一條jmp跳轉為if外 } efree(jmp_opnums); } } ``` 最終if語句編譯后基本是這樣的結構: ![](https://box.kancloud.cn/3b07d8cff4d1f199fb91e0913a3b781e_422x490.png) 執行時依次判斷各分支條件是否成立,成立則執行當前分支statement,執行完后跳到if外語句;不成立則調到下一分支繼續判斷是否成立,以此類推。不管各分支條件有幾個,其最終都會歸并為一個結果,也就是每個分支只需要判斷最終的條件值是否為true即可,而多個條件計算得到最終值的過程就是普通的邏輯運算。 > __Note:__ 注意elseif與else if,上面介紹的是elseif的編譯,而else if則實際相當于嵌套了一個if,也就是說一個if的分支中包含了另外一個if,在編譯、執行的過程中這兩個是有差別的。 ### 4.2.2 switch語句 switch語句與if類似,都是條件語句,很多時候需要將一個變量或者表達式與不同的值進行比較,根據不同的值執行不同的代碼,這種場景下用if、switch都可以實現,但switch相對更加直觀。 switch語法: ```php switch(expression){ case value1: statement1; case value2: statement2; ... default: statementn; } ``` 這里并沒有將break加入到switch的語法中,因為嚴格意義上break并不是switch的一部分,break屬于另外一類單獨的語法:中斷語法,PHP中如果沒有在switch中加break則執行時會從命中的那個case開始一直執行到結束,這與很多其它的語言不同(比如:golang)。 從switch的語法可以看出,switch主要包含兩部分:expression、case list,case list包含多個case,每個case包含value、statement兩部分。expression是一個表達式,但它將在case對比前執行,所以switch最終執行時就是拿expression的值逐個與case的value比較,如果相等則從命中case的statement開始向下執行。 下面看下switch的語法規則: ```c statement: ... | T_SWITCH '(' expr ')' switch_case_list { $$ = zend_ast_create(ZEND_AST_SWITCH, $3, $5); } ... ; switch_case_list: '{' case_list '}' { $$ = $2; } | '{' ';' case_list '}' { $$ = $3; } | ':' case_list T_ENDSWITCH ';' { $$ = $2; } | ':' ';' case_list T_ENDSWITCH ';' { $$ = $3; } ; case_list: /* empty */ { $$ = zend_ast_create_list(0, ZEND_AST_SWITCH_LIST); } | case_list T_CASE expr case_separator inner_statement_list { $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_SWITCH_CASE, $3, $5)); } | case_list T_DEFAULT case_separator inner_statement_list { $$ = zend_ast_list_add($1, zend_ast_create(ZEND_AST_SWITCH_CASE, NULL, $4)); } ; case_separator: ':' | ';' ; ``` 從語法解析規則可以看出,switch最終被解析為一個`ZEND_AST_SWITCH`節點,這個節點主要包含兩個子節點:expression、case list,其中expression節點比較簡單,case list節點對應一個`ZEND_AST_SWITCH_LIST`節點,這個節點是一個list,有多個case子節點,每個case節點對應一個`ZEND_AST_SWITCH_CASE`節點,包括value(或expr)、statement兩個子節點,生成的AST如下: ![](https://box.kancloud.cn/3bd3e6cc2ec6fbcc67d87c2ed044ea67_700x437.png) 與if不同,switch不會像if那樣依次把每個分支編譯為一組組的condition、statement,而是會先編譯全部case的value表達式,再編譯全部case的statement,編譯過程大致如下: * (1)首先編譯expression,其最終將得到一個固定的value; * (2)依次編譯每個case的value,如果value是一個表達式則編譯expression,與(1)相同,執行時其最終也是一個固定的value,每個case編譯一條ZEND_CASE的opcode,除了這條opcode還會編譯出一條ZEND_JMPNZ的opcode,這條opcode用來跳到當前case的statement的開始位置,但是statement在這時還未編譯,所以ZEND_JMPNZ的跳轉值暫不確定; * (3)編譯完全部case的value后接著從頭開始編譯每個case的statement,編譯前首先設置步驟(2)中ZEND_JMPNZ的跳轉值為當前statement起始位置。 具體編譯過程在`zend_compile_switch()`中,這里不再展開,編譯后的基本結構如下: ![](https://box.kancloud.cn/e178688393551b894462fedbda14f191_399x411.png) 執行時首先如果switch的是一個表達式則會首先執行表達式的語句,然后再拿最終的結果逐個與case的值比較,如果case也是一個表達式則也先執行表達式,執行完再與switch的值比較,比較結果如果為true則跳到當前case的statement位置開始順序執行,如果結果為false則繼續向下執行,與下一個case比較,以此類推。 > __Note:__ > > __(1)__ case不管是表達式還是固定的值其最終比較時是一樣的,如果是表達式則將其執行完以后再作比較,也就是說switch并不支持case多個值的用法,比如:case value1 || value2 : statement,這么寫首先是會執行(value1 || value2),然后把結果與switch的值比較,并不是指switch的值等于value1或value2,這個地方一定要注意,如果想命中多個value只能寫到不同case下 > > __(2)__ switch的value與case的value比較用的是"==",而不是"==="
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看