描述
PHP中把定义在函数、类之外的变量称之为全局变量,也就是定义在主脚本中的变量,这些变量可以在函数、成员方法中通过global关键字引入使用。
1 functiontest() {2 global $id;3 $id++;4 }5
6 $id = 1;7 test();8 echo $id;
存储
全局变量在整个请求执行期间始终存在,它们保存在EG(symbol_table)中,也就是全局变量符号表,与静态变量的存储一样,这也是一个哈希表,主脚本(或include、require)在zend_execute_ex执行开始之前会把当前作用域下的所有局部变量添加到EG(symbol_table)中
zend_vm_execute.h中,i_init_execute_data()这个函数中会把局部变量插入到EG(symbol_table):
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 create7 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 }
注意局部变量通过偏移量来访问,而不是变量名
从上面的过程可以很直观的看到,在执行前遍历局部变量,然后插入EG(symbol_table),EG(symbol_table)中的value直接指向局部变量的zval,示例经过这一步的处理之后(此时局部变量只是分配了zval,但还未初始化,所以是IS_UNDEF):
访问
与静态变量的访问一样,全局变量也是将原来的值转换为引用,然后在global导入的作用域内创建一个局部变量指向该引用:
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 }