亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

characters

 靜態(tài)變量

PHP中局部變量分配在zend_execute_data結(jié)構(gòu)上,每次執(zhí)行zend_op_array都會(huì)生成一個(gè)新的zend_execute_data,局部變量在執(zhí)行之初分配,然后在執(zhí)行結(jié)束時(shí)釋放,這是局部變量的生命周期,而局部變量中有一種特殊的類型:靜態(tài)變量,它們不會(huì)在函數(shù)執(zhí)行完后釋放,當(dāng)程序執(zhí)行離開函數(shù)域時(shí)靜態(tài)變量的值被保留下來,下次執(zhí)行時(shí)仍然可以使用之前的值。

PHP中的靜態(tài)變量通過static關(guān)鍵詞創(chuàng)建:

function my_func(){
    static $count = 4;
    $count++;
    echo $count,"\n";
}
my_func();
my_func();
===========================
5
6

靜態(tài)變量的存儲(chǔ)

靜態(tài)變量既然不會(huì)隨執(zhí)行的結(jié)束而釋放,那么很容易想到它的保存位置:zend_op_array->static_variables,這是一個(gè)哈希表,所以PHP中的靜態(tài)變量與普通局部變量不同,它們沒有分配在執(zhí)行空間zend_execute_data上,而是以哈希表的形式保存在zend_op_array中。

靜態(tài)變量只會(huì)初始化一次,注意:它的初始化發(fā)生在編譯階段而不是執(zhí)行階段,上面這個(gè)例子中:static $count = 4;是在編譯階段發(fā)現(xiàn)定義了一個(gè)靜態(tài)變量,然后插進(jìn)了zend_op_array->static_variables中,并不是執(zhí)行的時(shí)候把static_variables中的值修改為4,所以上面執(zhí)行的時(shí)候會(huì)輸出5、6,再次執(zhí)行并沒有重置靜態(tài)變量的值。

這個(gè)特性也意味著靜態(tài)變量初始的值不能是變量,比如:static $count = $xxx;這樣定義將會(huì)報(bào)錯(cuò)。

靜態(tài)變量的訪問

局部變量通過編譯時(shí)確定的編號(hào)進(jìn)行讀寫操作,而靜態(tài)變量通過哈希表保存,這就使得其不能像普通變量那樣有一個(gè)固定的編號(hào),有一種可能是通過變量名索引的,那么究竟是否如此呢?我們分析下其編譯過程。

靜態(tài)變量編譯的語法規(guī)則:


statement:
    ...
    |   T_STATIC static_var_list ';'    { $$ = $2; }
    ...
;
static_var_list:
        static_var_list ',' static_var { $$ = zend_ast_list_add($1, $3); }
    |   static_var { $$ = zend_ast_create_list(1, ZEND_AST_STMT_LIST, $1); }
;
static_var:
        T_VARIABLE          { $$ = zend_ast_create(ZEND_AST_STATIC, $1, NULL); }
    |   T_VARIABLE '=' expr { $$ = zend_ast_create(ZEND_AST_STATIC, $1, $3); }
;
語法解析后生成了一個(gè)ZEND_AST_STATIC語法樹節(jié)點(diǎn),接著再看下這個(gè)節(jié)點(diǎn)編譯為opcode的過程:zend_compile_static_var。
void zend_compile_static_var(zend_ast *ast)
{
    zend_ast *var_ast = ast->child[0];
    zend_ast *value_ast = ast->child[1];
    zval value_zv;
    if (value_ast) {
        //定義了初始值
        zend_const_expr_to_zval(&value_zv, value_ast);
    } else {
        //無初始值
        ZVAL_NULL(&value_zv);
    }
    zend_compile_static_var_common(var_ast, &value_zv, 1);
}

這里首先對(duì)初始化值進(jìn)行編譯,最終得到一個(gè)固定值,然后調(diào)用:zend_compile_static_var_common()處理,首先判斷當(dāng)前編譯的zend_op_array->static_variables是否已創(chuàng)建,未創(chuàng)建則分配一個(gè)HashTable,接著將定義的靜態(tài)變量插入:

/

/zend_compile_static_var_common():
if (!CG(active_op_array)->static_variables) {
    ALLOC_HASHTABLE(CG(active_op_array)->static_variables);
    zend_hash_init(CG(active_op_array)->static_variables, 8, NULL, ZVAL_PTR_DTOR, 0);
}
//插入靜態(tài)變量
zend_hash_update(CG(active_op_array)->static_variables, Z_STR(var_node.u.constant), value);
插入靜態(tài)變量哈希表后并沒有完成,接下來還有一個(gè)重要操作:
//生成一條ZEND_FETCH_W的opcode
opline = zend_emit_op(&result, by_ref ? ZEND_FETCH_W : ZEND_FETCH_R, &var_node, NULL);
opline->extended_value = ZEND_FETCH_STATIC;
if (by_ref) {
    zend_ast *fetch_ast = zend_ast_create(ZEND_AST_VAR, var_ast);
    //生成一條ZEND_ASSIGN_REF的opcode
    zend_emit_assign_ref_znode(fetch_ast, &result);
}

后面生成了兩條opcode:

ZEND_FETCH_W: 這條opcode對(duì)應(yīng)的操作是創(chuàng)建一個(gè)IS_INDIRECT類型的zval,指向static_variables中對(duì)應(yīng)靜態(tài)變量的zval

ZEND_ASSIGN_REF: 它的操作是引用賦值,即將一個(gè)引用賦值給CV變量

通過上面兩條opcode可以確定靜態(tài)變量的讀寫過程:首先根據(jù)變量名在static_variables中取出對(duì)應(yīng)的zval,然后將它修改為引用類型并賦值給局部變量,也就是說static $count = 4;包含了兩個(gè)操作,嚴(yán)格的將$count并不是真正的靜態(tài)變量,它只是一個(gè)指向靜態(tài)變量的局部變量,執(zhí)行時(shí)實(shí)際操作是:$count = & static_variables["count"];。上面例子$count與static_variables["count"]間的關(guān)系如圖所示。


Previous article: Next article: