描述app
PHP中把定義在函數、類以外的變量稱之爲全局變量,也就是定義在主腳本中的變量,這些變量能夠在函數、成員方法中經過global關鍵字引入使用。函數
1 function test() { 2 global $id; 3 $id++; 4 } 5 6 $id = 1; 7 test(); 8 echo $id;
存儲ui
全局變量在整個請求執行期間始終存在,它們保存在EG(symbol_table)
中,也就是全局變量符號表,與靜態變量的存儲同樣,這也是一個哈希表,主腳本(或include、require)在zend_execute_ex
執行開始以前會把當前做用域下的全部局部變量添加到EG(symbol_table)
中spa
zend_vm_execute.h中,i_init_execute_data()
這個函數中會把局部變量插入到EG(symbol_table):code
1 ZEND_API void zend_execute(zend_op_array *op_array, zval *return_value) 2 { 3 ... 4 i_init_execute_data(execute_data, op_array, return_value); 5 zend_execute_ex(execute_data); 6 ... 7 }
i_init_execute_data會把局部變量插入到EG(symbol_table),定義在zend_execute.c
1 static zend_always_inline void i_init_code_execute_data(zend_execute_data *execute_data, zend_op_array *op_array, zval *return_value) /* {{{ */ 2 { 3 ZEND_ASSERT(EX(func) == (zend_function*)op_array); 4 5 EX(opline) = op_array->opcodes; 6 EX(call) = NULL; 7 EX(return_value) = return_value; 8 9 zend_attach_symbol_table(execute_data); 10 11 if (!op_array->run_time_cache) { 12 op_array->run_time_cache = emalloc(op_array->cache_size); 13 memset(op_array->run_time_cache, 0, op_array->cache_size); 14 } 15 EX_LOAD_RUN_TIME_CACHE(op_array); 16 EX_LOAD_LITERALS(op_array); 17 18 EG(current_execute_data) = execute_data; 19 }
zend_attach_symbol_table 把局部變量插入到EG(symbol_table),定義在zend_execute_API.c中
1 ZEND_API void zend_attach_symbol_table(zend_execute_data *execute_data) /* {{{ */ 2 { 3 zend_op_array *op_array = &execute_data->func->op_array; 4 HashTable *ht = execute_data->symbol_table; //全局變量符號表 5 6 /* copy real values from symbol table into CV slots and create 7 INDIRECT references to CV in symbol table */ 8 if (EXPECTED(op_array->last_var)) { 9 zend_string **str = op_array->vars; //局部變量 10 zend_string **end = str + op_array->last_var;//最後一個局部變量的位置 11 zval *var = EX_VAR_NUM(0); 12 13 do { 14 zval *zv = zend_hash_find(ht, *str); 15 16 if (zv) { 17 if (Z_TYPE_P(zv) == IS_INDIRECT) { 18 zval *val = Z_INDIRECT_P(zv); 19 20 ZVAL_COPY_VALUE(var, val); 21 } else { 22 ZVAL_COPY_VALUE(var, zv); 23 } 24 } else { 25 ZVAL_UNDEF(var); 26 zv = zend_hash_add_new(ht, *str, var);//添加到全局變量符號表 27 } 28 ZVAL_INDIRECT(zv, var); 29 str++;//指向下一個局部變量 30 var++; 31 } while (str != end); 32 } 33 }
注意局部變量經過偏移量來訪問,而不是變量名blog
從上面的過程能夠很直觀的看到,在執行前遍歷局部變量,而後插入EG(symbol_table),EG(symbol_table)中的value直接指向局部變量的zval,示例通過這一步的處理以後(此時局部變量只是分配了zval,但還未初始化,因此是IS_UNDEF):element
訪問作用域
與靜態變量的訪問同樣,全局變量也是將原來的值轉換爲引用,而後在global導入的做用域內建立一個局部變量指向該引用:string
1 global $id; // 至關於:$id = & EG(symbol_table)["id"];
銷燬
局部變量若是沒有手動銷燬,那麼在函數執行結束時會將它們銷燬,而全局變量則是在整個請求結束時纔會銷燬,即便是咱們直接在PHP腳本中定義在函數外的那些變量。
1 void shutdown_destructors(void) 2 { 3 if (CG(unclean_shutdown)) { 4 EG(symbol_table).pDestructor = zend_unclean_zval_ptr_dtor; 5 } 6 zend_try { 7 uint32_t symbols; 8 do { 9 symbols = zend_hash_num_elements(&EG(symbol_table)); 10 //銷燬 11 zend_hash_reverse_apply(&EG(symbol_table), (apply_func_t) zval_call_destructor); 12 } while (symbols != zend_hash_num_elements(&EG(symbol_table))); 13 } 14 ... 15 }