<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>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                通常意義上靜態變量是靜態分配的,他們的生命周期和程序的生命周期一樣,只有在程序退出時才結束期生命周期,這和局部變量相反,有的語言中全局變量也是靜態分配的。例如PHP和Javascript中的全局變量。 靜態變量可以分為: - 靜態全局變量,PHP中的全局變量也可以理解為靜態全局變量,因為除非明確unset釋放,在程序運行過程中始終存在。 - 靜態局部變量,也就是在函數內定義的靜態變量,函數在執行時對變量的操作會保持到下一次函數被調用。 - 靜態成員變量,這是在類中定義的靜態變量,和實例變量相對應,靜態成員變量可以在所有實例中共享。 最常見的是靜態局部變量及靜態成員變量。局部變量只有在函數執行時才會存在。通常,當一個函數執行完畢,它的局部變量的值就已經不存在,而且變量所占據的內存也被釋放。當下一次執行該過程時,它的所有局部變量將重新初始化。如果某個局部變量定義為靜態的,則它的值不會在函數調用結束后釋放,而是繼續保留變量的值。 在本小節將介紹靜態局部變量,有關靜態成員變量的內容將在類與對象章節進行介紹。 先看看如下局部變量的使用: function t() { static $i = 0; $i++; echo $i, ' '; } ? t(); t(); t(); 上面的程序會輸出1 2 3。從這個示例可以看出,$i變量的值在改變后函數繼續執行還能訪問到,$i變量就像是只有函數t()才能訪問到的一個全局變量。那PHP是怎么實現的呢? static是PHP的關鍵字,我們需要從詞法分析,語法分析,中間代碼生成到執行中間代碼這幾個部分探討整個實現過程。 ## 1. 詞法分析 首先查看 Zend/zend_language_scanner.l文件,搜索 static關鍵字。我們可以找到如下代碼: <ST_IN_SCRIPTING>"static" { return T_STATIC; } ## 2. 語法分析 在詞法分析找到token后,通過這個token,在Zend/zend_language_parser.y文件中查找。找到相關代碼如下: | T_STATIC static_var_list ';' ? static_var_list: static_var_list ',' T_VARIABLE { zend_do_fetch_static_variable(&$3, NULL, ZEND_FETCH_STATIC TSRMLS_CC); } | static_var_list ',' T_VARIABLE '=' static_scalar { zend_do_fetch_static_variable(&$3, &$5, ZEND_FETCH_STATIC TSRMLS_CC); } | T_VARIABLE { zend_do_fetch_static_variable(&$1, NULL, ZEND_FETCH_STATIC TSRMLS_CC); } | T_VARIABLE '=' static_scalar { zend_do_fetch_static_variable(&$1, &$3, ZEND_FETCH_STATIC TSRMLS_CC); } ? ; 語法分析的過程中如果匹配到相應的模式則會進行相應的處理動作,通常是進行opcode的編譯。在本例中的static關鍵字匹配中,是由函數zend_do_fetch_static_variable處理的。 ## 3. 生成opcode中間代碼 zend_do_fetch_static_variable函數的作用就是生成opcode,定義如下: void zend_do_fetch_static_variable(znode *varname, const znode *static_assignment, int fetch_type TSRMLS_DC) { zval *tmp; zend_op *opline; znode lval; znode result; ? ALLOC_ZVAL(tmp); ? if (static_assignment) { *tmp = static_assignment->u.constant; } else { INIT_ZVAL(*tmp); } if (!CG(active_op_array)->static_variables) { /* 初始化此時的靜態變量存放位置 */ ALLOC_HASHTABLE(CG(active_op_array)->static_variables); zend_hash_init(CG(active_op_array)->static_variables, 2, NULL, ZVAL_PTR_DTOR, 0); } // 將新的靜態變量放進來 zend_hash_update(CG(active_op_array)->static_variables, varname->u.constant.value.str.val, varname->u.constant.value.str.len+1, &tmp, sizeof(zval *), NULL); ? ...//省略 opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = (fetch_type == ZEND_FETCH_LEXICAL) ? ZEND_FETCH_R : ZEND_FETCH_W; /* 由于fetch_type=ZEND_FETCH_STATIC,程序會選擇ZEND_FETCH_W*/ opline->result.op_type = IS_VAR; opline->result.u.EA.type = 0; opline->result.u.var = get_temporary_variable(CG(active_op_array)); opline->op1 = *varname; SET_UNUSED(opline->op2); opline->op2.u.EA.type = ZEND_FETCH_STATIC; /* 這在中間代碼執行時會有大用 */ result = opline->result; ? if (varname->op_type == IS_CONST) { zval_copy_ctor(&varname->u.constant); } fetch_simple_variable(&lval, varname, 0 TSRMLS_CC); /* Relies on the fact that the default fetch is BP_VAR_W */ ? if (fetch_type == ZEND_FETCH_LEXICAL) { ...//省略 } else { zend_do_assign_ref(NULL, &lval, &result TSRMLS_CC); // 賦值操作中間代碼生成 } CG(active_op_array)->opcodes[CG(active_op_array)->last-1].result.u.EA.type |= EXT_TYPE_UNUSED; ? } 從上面的代碼我們可知,在解釋成中間代碼時,靜態變量是存放在CG(active_op_array)->static_variables中的。并且生成的中間代碼為:**ZEND_FETCH_W** 和 **ZEND_ASSIGN_REF** 。其中ZEND_FETCH_W中間代碼是在zend_do_fetch_static_variable中直接賦值,而ZEND_ASSIGN_REF中間代碼是在zend_do_fetch_static_variable中調用zend_do_assign_ref生成的。 ## 4. 執行中間代碼 opcode的編譯階段完成后就開始opcode的執行了。在Zend/zend_vm_opcodes.h文件中包含所有opcode的宏定義,這些宏并沒有特殊含義,只是作為opcode的唯一標示,包含本例中相關的如下兩個宏的定義: #define ZEND_FETCH_W 83 #define ZEND_ASSIGN_REF 39 前面第二章 [腳本的執行一節](#)介紹了根據opcode查找到相應處理函數的方法。通過中間代碼調用映射方法計算得此時ZEND_FETCH_W 對應的操作為ZEND_FETCH_W_SPEC_CV_HANDLER。其代碼如下: static int ZEND_FASTCALL ZEND_FETCH_W_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { return zend_fetch_var_address_helper_SPEC_CV(BP_VAR_W, ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); } ? static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CV(int type, ZEND_OPCODE_HANDLER_ARGS) { ...//省略 ? if (opline->op2.u.EA.type == ZEND_FETCH_STATIC_MEMBER) { retval = zend_std_get_static_property(EX_T(opline->op2.u.var).class_entry, Z_STRVAL_P(varname), Z_STRLEN_P(varname), 0 TSRMLS_CC); } else { // 取符號表,這里我們取的是EG(active_op_array)->static_variables target_symbol_table = zend_get_target_symbol_table(opline, EX(Ts), type, varname TSRMLS_CC); ...// 省略 if (zend_hash_find(target_symbol_table, varname->value.str.val, varname->value.str.len+1, (void **) &retval) == FAILURE) { switch (type) { ...//省略 // 在前面的調用中我們知道type = case BP_VAR_W,于是程序會走按case BP_VAR_W的流程走。 case BP_VAR_W: { zval *new_zval = &EG(uninitialized_zval); ? Z_ADDREF_P(new_zval); zend_hash_update(target_symbol_table, varname->value.str.val, varname->value.str.len+1, &new_zval, sizeof(zval *), (void **) &retval); // 更新符號表,執行賦值操作 } break; EMPTY_SWITCH_DEFAULT_CASE() } } switch (opline->op2.u.EA.type) { ...//省略 case ZEND_FETCH_STATIC: zval_update_constant(retval, (void*) 1 TSRMLS_CC); break; case ZEND_FETCH_GLOBAL_LOCK: if (IS_CV == IS_VAR && !free_op1.var) { PZVAL_LOCK(*EX_T(opline->op1.u.var).var.ptr_ptr); } break; } } ? ...//省略 } 在上面的代碼中有一個關鍵的函數zend_get_target_symbol_table。它的作用是獲取當前正在執行的目標符號表,而在函數執行時當前的op_array則是函數體本身,先看看zend_op_array的結構。 struct _zend_op_array { /* Common elements */ zend_uchar type; char *function_name; zend_uint num_args; zend_uint required_num_args; zend_arg_info *arg_info; zend_bool pass_rest_by_reference; unsigned char return_reference; /* END of common elements */ ? zend_bool done_pass_two; ? zend_uint *refcount; ? zend_op *opcodes; zend_uint last, size; ? /* static variables support */ HashTable *static_variables; ? zend_op *start_op; int backpatch_count; ? zend_uint this_var; // ... } 由上可以看到zend_op_array中包含function_name字段,也就是當前函數的名稱。再看看獲取當前符號表的函數: static inline HashTable *zend_get_target_symbol_table(const zend_op *opline, const temp_variable *Ts, int type, const zval *variable TSRMLS_DC) { switch (opline->op2.u.EA.type) { ...// 省略 case ZEND_FETCH_STATIC: if (!EG(active_op_array)->static_variables) { ALLOC_HASHTABLE(EG(active_op_array)->static_variables); zend_hash_init(EG(active_op_array)->static_variables, 2, NULL, ZVAL_PTR_DTOR, 0); } return EG(active_op_array)->static_variables; break; EMPTY_SWITCH_DEFAULT_CASE() } return NULL; } 在前面的zend_do_fetch_static_variable執行時,op2.u.EA.type的值為ZEND_FETCH_STATIC,從而這zend_get_target_symbol_table函數中我們返回的是EG(active_op_array)->static_variables。也就是當前函數的的靜態變量哈希表。每次執行時都會從該符號表中查找相應的值,由于op_array在程序執行時始終存在。所有對靜態符號表中數值的修改會繼續保留,下次函數執行時繼續從該符號表獲取信息。也就是說Zend為每個函數(準確的說是zend_op_array)分配了一個私有的符號表來保存該函數的靜態變量。
                  <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>

                              哎呀哎呀视频在线观看