global語句的作用是定義全局變量,例如如果想在函數內訪問全局作用域內的變量則可以通過global聲明來定義。下面從語法解釋開始分析。
**1. 詞法解析**
查看 Zend/zend_language_scanner.l文件,搜索 global關鍵字。我們可以找到如下代碼:
<ST_IN_SCRIPTING>"global" {
return T_GLOBAL;
}
**2. 語法解析**
在詞法解析完后,獲得了token,此時通過這個token,我們去Zend/zend_language_parser.y文件中查找。找到相關代碼如下:
| T_GLOBAL global_var_list ';'
?
global_var_list:
global_var_list ',' global_var { zend_do_fetch_global_variable(&$3, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); }
| global_var { zend_do_fetch_global_variable(&$1, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); }
;
上面代碼中的**$3**是指global_var(如果不清楚yacc的語法,可以查閱yacc入門類的文章。)
從上面的代碼可以知道,對于全局變量的聲明調用的是zend_do_fetch_global_variable函數,查找此函數的實現在Zend/zend_compile.c文件。
void zend_do_fetch_global_variable(znode *varname, const znode *static_assignment, int fetch_type TSRMLS_DC)
{
...//省略
opline->opcode = ZEND_FETCH_W; /* the default mode must be Write, since fetch_simple_variable() is used to define function arguments */
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 = fetch_type;
result = opline->result;
?
... // 省略
fetch_simple_variable(&lval, varname, 0 TSRMLS_CC); /* Relies on the fact that the default fetch is BP_VAR_W */
?
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;
}
/* }}} */
上面的代碼確認了opcode為ZEND_FETCH_W外,還執行了zend_do_assign_ref函數。zend_do_assign_ref函數的實現如下:
void zend_do_assign_ref(znode *result, const znode *lvar, const znode *rvar TSRMLS_DC) /* {{{ */
{
zend_op *opline;
?
... //省略
?
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
opline->opcode = ZEND_ASSIGN_REF;
...//省略
if (result) {
opline->result.op_type = IS_VAR;
opline->result.u.EA.type = 0;
opline->result.u.var = get_temporary_variable(CG(active_op_array));
*result = opline->result;
} else {
/* SET_UNUSED(opline->result); */
opline->result.u.EA.type |= EXT_TYPE_UNUSED;
}
opline->op1 = *lvar;
opline->op2 = *rvar;
}
從上面的zend_do_fetch_global_variable函數和zend_do_assign_ref函數的實現可以看出,使用global聲明一個全局變量后,其執行了兩步操作,ZEND_FETCH_W和ZEND_ASSIGN_REF。
**3. 生成并執行中間代碼**
我們看下ZEND_FETCH_W的最后執行。從代碼中我們可以知道:
- ZEND_FETCH_W = 83
- op->op1.op_type = 4
- op->op2.op_type = 0
而計算最后調用的方法在代碼中的體現為:
zend_opcode_handlers[opcode * 25 + zend_vm_decode[op->op1.op_type] * 5 + zend_vm_decode[op->op2.op_type]];
計算,最后調用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);
}
在zend_fetch_var_address_helper_SPEC_CV中調用如下代碼獲取符號表
target_symbol_table = zend_get_target_symbol_table(opline, EX(Ts), type, varname TSRMLS_CC);
在zend_get_target_symbol_table函數的實現如下:
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_GLOBAL:
case ZEND_FETCH_GLOBAL_LOCK:
return &EG(symbol_table);
break;
... // 省略
}
return NULL;
}
在前面語法分析過程中,程序傳遞的參數是 ZEND_FETCH_GLOBAL_LOCK,于是如上所示。我們取&EG(symbol_table);的值。這也是全局變量的存放位置。
如上就是整個global的解析過程。
- 第一章 準備工作和背景知識
- 第一節 環境搭建
- 第二節 源碼結構、閱讀代碼方法
- 第三節 常用代碼
- 第四節 小結
- 第二章 用戶代碼的執行
- 第一節 生命周期和Zend引擎
- 第二節 SAPI概述
- Apache模塊
- 嵌入式
- FastCGI
- 第三節 PHP腳本的執行
- 詞法分析和語法分析
- opcode
- opcode處理函數查找
- 第四節 小結
- 第三章 變量及數據類型
- 第一節 變量的結構和類型
- 哈希表(HashTable)
- PHP的哈希表實現
- 鏈表簡介
- 第二節 常量
- 第三節 預定義變量
- 第四節 靜態變量
- 第五節 類型提示的實現
- 第六節 變量的生命周期
- 變量的賦值和銷毀
- 變量的作用域
- global語句
- 第七節 數據類型轉換
- 第八節 小結
- 第四章 函數的實現
- 第一節 函數的內部結構
- 函數的內部結構
- 函數間的轉換
- 第二節 函數的定義,傳參及返回值
- 函數的定義
- 函數的參數
- 函數的返回值
- 第三節 函數的調用和執行
- 第四節 匿名函數及閉包
- 第五節 小結
- 第五章 類和面向對象
- 第一節 類的結構和實現
- 第二節 類的成員變量及方法
- 第三節 訪問控制的實現
- 第四節 類的繼承,多態及抽象類
- 第五節 魔術方法,延遲綁定及靜態成員
- 第六節 PHP保留類及特殊類
- 第七節 對象
- 第八節 命名空間
- 第九節 標準類
- 第十節 小結
- 第六章 內存管理
- 第一節 內存管理概述
- 第二節 PHP中的內存管理
- 第三節 內存使用:申請和銷毀
- 第四節 垃圾回收
- 新的垃圾回收
- 第五節 內存管理中的緩存
- 第六節 寫時復制(Copy On Write)
- 第七節 內存泄漏
- 第八節 小結
- 第七章 Zend虛擬機
- 第一節 Zend虛擬機概述
- 第二節 語法的實現
- 詞法解析
- 語法分析
- 實現自己的語法
- 第三節 中間代碼的執行
- 第四節 PHP代碼的加密解密
- 第五節 小結
- 第八章 線程安全
- 第二節 線程,進程和并發
- 第三節 PHP中的線程安全
- 第九章 錯誤和異常處理
- 第十章 輸出緩沖
- 第十六章 PHP語言特性的實現
- 第一節 循環語句
- foreach的實現
- 第二十章 怎么樣系列(how to)
- 附錄
- 附錄A PHP及Zend API
- 附錄B PHP的歷史
- 附錄C VLD擴展使用指南
- 附錄D 怎樣為PHP貢獻
- 附錄E phpt測試文件說明
- 附錄F PHP5.4新功能升級解析
- 附錄G:re2c中文手冊