在PHP中,用戶函數的定義從function關鍵字開始。如下所示簡單示例:
function foo($var) {
[echo](http://www.php.net/echo) $var;
}
這是一個非常簡單的函數,它所實現的功能是定義一個函數,函數有一個參數,函數的內容是在標準輸出端輸出傳遞給它的參數變量的值。
函數的一切從function開始。我們從function開始函數定義的探索之旅。
**詞法分析**
在 Zend/zend_language_scanner.l中我們找到如下所示的代碼:
<ST_IN_SCRIPTING>"function" {
return T_FUNCTION;
}
它所表示的含義是function將會生成T_FUNCTION標記。在獲取這個標記后,我們開始語法分析。
**語法分析**
在 Zend/zend_language_parser.y文件中找到函數的聲明過程標記如下:
function:
T_FUNCTION { $$.u.opline_num = CG(zend_lineno); }
;
?
is_reference:
/* empty */ { $$.op_type = ZEND_RETURN_VAL; }
| '&' { $$.op_type = ZEND_RETURN_REF; }
;
?
unticked_function_declaration_statement:
function is_reference T_STRING {
zend_do_begin_function_declaration(&$1, &$3, 0, $2.op_type, NULL TSRMLS_CC); }
'(' parameter_list ')' '{' inner_statement_list '}' {
zend_do_end_function_declaration(&$1 TSRMLS_CC); }
;
> 關注點在 function is_reference T_STRING,表示function關鍵字,是否引用,函數名。
T_FUNCTION標記只是用來定位函數的聲明,表示這是一個函數,而更多的工作是與這個函數相關的東西,包括參數,返回值等。
**生成中間代碼**
語法解析后,我們看到所執行編譯函數為zend_do_begin_function_declaration。在 Zend/zend_complie.c文件中找到其實現如下:
void zend_do_begin_function_declaration(znode *function_token, znode *function_name,
int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC) /* {{{ */
{
...//省略
function_token->u.op_array = CG(active_op_array);
lcname = zend_str_tolower_dup(name, name_len);
?
orig_interactive = CG(interactive);
CG(interactive) = 0;
init_op_array(&op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC);
CG(interactive) = orig_interactive;
?
...//省略
?
if (is_method) {
...//省略 類方法 在后面的類章節介紹
} else {
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
?
?
opline->opcode = ZEND_DECLARE_FUNCTION;
opline->op1.op_type = IS_CONST;
build_runtime_defined_function_key(&opline->op1.u.constant, lcname,
name_len TSRMLS_CC);
opline->op2.op_type = IS_CONST;
opline->op2.u.constant.type = IS_STRING;
opline->op2.u.constant.value.str.val = lcname;
opline->op2.u.constant.value.str.len = name_len;
Z_SET_REFCOUNT(opline->op2.u.constant, 1);
opline->extended_value = ZEND_DECLARE_FUNCTION;
zend_hash_update(CG(function_table), opline->op1.u.constant.value.str.val,
opline->op1.u.constant.value.str.len, &op_array, sizeof(zend_op_array),
(void **) &CG(active_op_array));
}
?
}
/* }}} */
生成的中間代碼為 **ZEND_DECLARE_FUNCTION** ,根據這個中間代碼及操作數對應的op_type。我們可以找到中間代碼的執行函數為 **ZEND_DECLARE_FUNCTION_SPEC_HANDLER**。
> 在生成中間代碼時,可以看到已經統一了函數名全部為小寫,表示函數的名稱不是區分大小寫的。
為驗證這個實現,我們看一段代碼:
function T() {
[echo](http://www.php.net/echo) 1;
}
?
function t() {
[echo](http://www.php.net/echo) 2;
}
執行代碼,可以看到屏幕上輸出如下報錯信息:
Fatal error: Cannot redeclare t() (previously declared in ...)
表示對于PHP來說T和t是同一個函數名。檢驗函數名是否重復,這個過程是在哪進行的呢?下面將要介紹的函數聲明中間代碼的執行過程包含了這個檢查過程。
**執行中間代碼**
在 Zend/zend_vm_execute.h 文件中找到 ZEND_DECLARE_FUNCTION中間代碼對應的執行函數:ZEND_DECLARE_FUNCTION_SPEC_HANDLER。此函數只調用了函數do_bind_function。其調用代碼為:
do_bind_function(EX(opline), EG(function_table), 0);
在這個函數中將EX(opline)所指向的函數添加到EG(function_table)中,并判斷是否已經存在相同名字的函數,如果存在則報錯。EG(function_table)用來存放執行過程中全部的函數信息,相當于函數的注冊表。它的結構是一個HashTable,所以在do_bind_function函數中添加新的函數使用的是HashTable的操作函數**zend_hash_add**
- 第一章 準備工作和背景知識
- 第一節 環境搭建
- 第二節 源碼結構、閱讀代碼方法
- 第三節 常用代碼
- 第四節 小結
- 第二章 用戶代碼的執行
- 第一節 生命周期和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中文手冊