php原始碼之路第四章第一節( 函式的內部結構)
函式是一種可以在任何被需要的時候執行的程式碼塊。它不僅僅包括使用者自定義的函式,還包括程式語言實現的庫函式。
使用者定義的函式
如下所示手冊中的展示函式用途的虛擬碼
function foo($arg_1, $arg_2, ..., $arg_n) {
echo "Example function.\n";
return $retval;
}
任何有效的 PHP 程式碼都可以編寫在函式內部,甚至包括其它函式和類定義。 內部函式 PHP 有很多標準的函式和結構。如我們常見的count、strpos、implode等函式,這些都是標準函式,它們都是由標準擴充套件提供的;如我們經常用到的isset、empty、eval等函式,這些結構被稱之為語言結構。還有一些函式需要和特定的PHP擴充套件模組一起編譯並開啟,否則無法使用。也就是有些擴充套件是可選的。 標準函式的實現存放在ext/standard擴充套件目錄中。 匿名函式 有時我們的一代程式碼並不需要為它指定一個名稱,而只需要它完成特定的工作,匿名函式的作用是為了擴大函式的使用功能,在PHP 5.3以前,傳遞函式回撥的方式,我們只有兩種選擇: 字串的函式名 使用create_function建立的返回 在PHP5.3以後,我們多了一個選擇--Closure。在實現上PHP 5.3中對匿名函式的支援,採用的是把要保持的外部變數,做為Closure物件的”Static屬性”來實現的。 變數函式 PHP 支援變數函式的概念。這意味著如果一個變數名後有圓括號,PHP 將尋找與變數的值同名的函式,並且將嘗試執行它。除此之外,這個可以被用於實現回撥函式,函式表等。一個變數函式的簡單例子:
$func = 'print_r';
$func('i am print_r function.');
變數函式不能用於語言結構(echo等)
下面我們將開始關注函式在PHP中具體實現,函式的內部結構,函式的呼叫,引數傳遞以及函式返回值等。
函式的內部結構
在PHP中,函式有自己的作用域,同時在其內部可以實現各種語句的執行,最後返回最終結果值。在PHP的原始碼中可以發現,Zend引擎將函式分為以下型別:
#define ZEND_INTERNAL_FUNCTION 1
#define ZEND_USER_FUNCTION 2
#define ZEND_OVERLOADED_FUNCTION 3
#define ZEND_EVAL_CODE 4
#define ZEND_OVERLOADED_FUNCTION_TEMPORARY 5
其中的ZEND_USER_FUNCTION是使用者函式,ZEND_INTERNAL_FUNCTION是內建的函式。也就是說PHP將內建的函式和使用者定義的函式分別儲存。 1.使用者函式(ZEND_USER_FUNCTION) 使用者自定義函式是非常常用的函式種類,如下面的程式碼,定義了一個使用者自定義的函式:
<?php
function tipi( $name ){
$return = "Hi! " . $name;
echo $return;
return $return;
}
?>
這個示例中,對自定義函式傳入了一個引數,並將其與Hi! 一起輸出並做為返回值返回。從這個例子可以看出函式的基本特點:執行時宣告、可以傳引數、有值返回。當然,有些函式只是進行一些操作,並不一定顯式的有返回值,在PHP的實現中,即使沒有顯式的返回, Zend引擎也會“幫你“返回NULL。
ZE在執行過程中,會將執行時資訊儲存於_zend_execute_data中:
struct _zend_execute_data {
//...省略部分程式碼
zend_function_state function_state;
zend_function *fbc; /* Function Being Called */
//...省略部分程式碼
};
在程式初始化的過程中,function_state也會進行初始化,function_state由兩個部分組成:
typedef struct _zend_function_state {
zend_function *function;
void **arguments;
} zend_function_state;
**arguments是一個指向函式引數的指標,而函式體本身則儲存於*function中, *function是一個zend_function結構體,它最終儲存了使用者自定義函式的一切資訊,它的具體結構是這樣的:
typedef union _zend_function {
zend_uchar type; /* 如使用者自定義則為 #define ZEND_USER_FUNCTION 2
MUST be the first element of this struct! */
struct {
zend_uchar type; /* never used */
char *function_name; //函式名稱
zend_class_entry *scope; //函式所在的類作用域
zend_uint fn_flags; // 作為方法時的訪問型別等,如ZEND_ACC_STATIC等
union _zend_function *prototype; //函式原型
zend_uint num_args; //引數數目
zend_uint required_num_args; //需要的引數數目
zend_arg_info *arg_info; //引數資訊指標
zend_bool pass_rest_by_reference;
unsigned char return_reference; //返回值
} common;
zend_op_array op_array; //函式中的操作
zend_internal_function internal_function;
} zend_function;
zend_function的結構中的op_array儲存了該函式中所有的操作,當函式被呼叫時,ZE就會將這個op_array中的opline一條條順次執行,並將最後的返回值返回。從VLD擴充套件中檢視的關於函式的資訊可以看出,函式的定義和執行是分開的,一個函式可以作為一個獨立的執行單元而存在。
2.內部函式(ZEND_INTERNAL_FUNCTION)
ZEND_INTERNAL_FUNCTION函式是由擴充套件、PHP核心、Zend引擎提供的內部函式,一般用“C/C++”編寫,可以直接在使用者指令碼中呼叫的函式。如下為內部函式的結構:
typedef struct _zend_internal_function {
/* Common elements */
zend_uchar type;
char * function_name;
zend_class_entry *scope;
zend_uint fn_flags;
union _zend_function *prototype;
zend_uint num_args;
zend_uint required_num_args;
zend_arg_info *arg_info;
zend_bool pass_rest_by_reference;
unsigned char return_reference;
/* END of common elements */
void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
struct _zend_module_entry *module;
} zend_internal_function;
最常見的操作是在模組初始化時,ZE會遍歷每個載入的擴充套件模組,然後將模組中function_entry中指明的每一個函式(module->functions),建立一個zend_internal_function結構, 並將其type設定為ZEND_INTERNAL_FUNCTION,將這個結構填入全域性的函式表(HashTable結構); 函式設定及註冊過程見 Zend/zend_API.c檔案中的 zend_register_functions函式。這個函式除了處理函式,也處理類的方法,包括那些魔術方法。
內部函式的結構與使用者自定義的函式結構基本類似,有一些不同,
呼叫方法,handler欄位. 如果是ZEND_INTERNAL_FUNCTION, 那麼ZE就呼叫zend_execute_internal,通過zend_internal_function.handler來執行這個函式。而使用者自定義的函式需要生成中間程式碼,然後通過中間程式碼對映到相對就把方法呼叫。
內建函式在結構中多了一個module欄位,表示屬於哪個模組。不同的擴充套件其模組不同。
type欄位,在使用者自定義的函式中,type欄位幾乎無用,而內建函式中的type欄位作為幾種內部函式的區分。
3.變數函式
PHP 支援變數函式的概念。這意味著如果一個變數名後有圓括號,PHP 將尋找與變數的值同名的函式,並且將嘗試執行它。除此之外,這個可以被用於實現回撥函式,函式表等。對比使用變數函式和內部函式的呼叫:
變數函式$func
$func = 'print_r';
$func('i am print_r function.');
通過VLD來檢視這段程式碼編譯後的中間程式碼:
內部函式print_r
print_r('i am print_r function.');
通過VLD來檢視這段程式碼編譯後的中間程式碼:
對比發現,二者在呼叫的中間程式碼上存在一些區別。變數函式是DO_FCALL_BY_NAME,而內部函式是DO_FCALL。這在語法解析時就已經決定了,見Zend/zend_complie.c檔案的zend_do_end_function_call函式中部分程式碼:
if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) {
opline->opcode = ZEND_DO_FCALL;
opline->op1 = *function_name;
ZVAL_LONG(&opline->op2.u.constant, zend_hash_func(Z_STRVAL(function_name->u.constant), Z_STRLEN(function_name->u.constant) + 1));
} else {
opline->opcode = ZEND_DO_FCALL_BY_NAME;
SET_UNUSED(opline->op1);
}
如果不是方法,並且不是動態呼叫,並且函式名為字串常量,則其生成的中間程式碼為ZEND_DO_FCALL。其它情況則為ZEND_DO_FCALL_BY_NAME。另外將變數函式作為回撥函式,其處理過程在Zend/zend_complie.c檔案的zend_do_pass_param函式中。最終會體現在中間程式碼執行過程中的 ZEND_SEND_VAL_SPEC_CONST_HANDLER 等函式中。
4.匿名函式
匿名函式是一類不需要指定表示符,而又可以被呼叫的函式或子例程,匿名函式可以方便的作為引數傳遞給其他函式。
相關推薦
php原始碼之路第四章第一節( 函式的內部結構)
函式是一種可以在任何被需要的時候執行的程式碼塊。它不僅僅包括使用者自定義的函式,還包括程式語言實現的庫函式。 使用者定義的函式 如下所示手冊中的展示函式用途的虛擬碼 funct
php原始碼之路第六章第一節 (記憶體管理概述)
記憶體是計算機非常關鍵的部件之一,是暫時儲存程式以及資料的空間,CPU只有有限的暫存器可以用於儲存計算資料,而大部分的資料都是儲存在記憶體中的,程式執行都是在記憶體中進行的。和CPU計算能力一樣,記憶體也是決定計算效率的一個關鍵部分。 計算中的資源中主要包含:
php原始碼之路第七章第一節 ( Zend虛擬機器)
在前面的章節中,我們瞭解到一個PHP檔案在伺服器端的執行過程包括以下兩個大的過程: 遞給php程式需要執行的檔案, php程式完成基本的準備工作後啟動PHP及Zend引擎, 載入註冊的擴充套件
php原始碼之路第七章第三節 (PHP程式碼的加密解密)
PHP語言作為指令碼語言的一種,由於不需要進行編譯,所以通常PHP程式的分發都是直接釋出原始碼。對於一些開源軟體來說,這並沒有什麼問題,因為它本來就希望有更多的人閱讀程式碼,希望有更多的人蔘與進來,而對於商業程式碼來說,這卻是一個不太好的訊息,不管是從商
【軟件構造】第四章第一節 面向可理解性的構造
javadoc 搜索 顯示 div accounts nbsp 避免 模糊 質量 第四章第一節 面向可理解性的構造 Outline 代碼可理解性 編碼規範 Notes ## 代碼的可理解性 代碼的可理解性可以理解為代碼的可讀性。具體來說,可從以下幾個方面來看: 是否遵
Pthon學習之路 第四篇 Python基礎(二)
pri bsp programs -s alt 如果 lex class 算數運算 1.運算符:+ - *(乘法) /(除法) %(求余) //(求商) **(求冪) 2.成員運算:in not in:判斷單個字符或者子序列在不在字符串中。(n
Java學習之路 第五章 面向物件(1)
面向物件(1) 1、認識物件 (1)萬物皆物件。 (2)物件=特點或特徵(屬性)+行為或(方法)。 (3)物件由屬性和方法組成,一定要具體到個體上。 2、認識類 (1)類是一些具有共同屬性和方法的物件的一個抽象。 (2)類是一個概念,不是具體的一個物件。 (3)
ORACLE官方SQL語言參考筆記之運算子(第四章-第二節-算術運算子)
算術運算子可以使用一個或兩個引數的算術運算子來否定、新增、減去、相乘和分割數值。其中一些運算子也用於日期時間和間隔演算法。運算子的引數必須解析為數值資料型別或可隱式轉換為數值資料型別的任何資料型別。一元算術運算子返回與引數的數值資料型別相同的資料型別。對於二進位制算術運算子,
前端:CSS第四章第一節
第一部分:樣式規則
第四章 操作列表(遍歷列表)
遍歷列表: nums = ['1','2','3','4'] for num in nums: print(num) >for後面,沒有縮排的程式碼,只執行一次,不
讀構建之法 第四章:兩人合作
應用 結對編程 使用 一對一 測試 一個 比較 以及 領域 程序員寫的代碼最終是人在看,所以代碼規範很重要,原則是:簡明,易讀,無二義性。 不光是程序書寫的格式問題,還牽涉到程序設計、模塊之間的關系、設計模式等方方面面。 代碼復審的正確定義看代碼是否在代碼規範的框架內正確的
構建之法第四章讀書心得
算法 邏輯錯誤 規範 審核 領域 之間 心得 使用 部分 代碼風格規範——主要是文字上的規定,看似表面文章,實際上非常重要 代碼風格的原則是:簡明,易讀,無二義性 代碼設計規範——牽涉到程序設計、模塊之間的關系、設計模式等方方面面的通用原則 代碼設計規範不光是程序書寫的格
構建之法第四章讀後感
可維護 編程思想 可維護性 有著 人在 經歷 項目 能夠 疑惑 第四章讀後感 在經過對第四章的閱讀後,我更加清晰地認識到了在項目開發中,規範二字的重要性,也新學到了除開代碼規範以外,其他對於團隊協作也很重要的東西,比如說構造函數的使用,模塊化的編程思想,當然自己也對一些問題
構建之法第四章第十七章
height 多文檔 缺失 後來 更強 最大的 fun 影響 手機 一、關於goto函數:濫用goto語句會使程序變得很難理解,而不是所有人能正確的使用goto函數,我的問題是:是不是因為這樣所以很多文檔規定禁用或少用goto函數?但其實如果可以正確的使用goto語句就不能
讀構建之法第四章第十七章有感
限制 選擇 class blog 了解 什麽 靈活 多重循環 價值 第四章 1、原文;“函數最好有單一的出口,為了達到這個目的,可以使用goto.只要有助於程序邏輯的清晰體現,什麽方法都可以使用。——P69” 問題:關於goto,我記得老師講過,這個在編程中是盡力避
Python之旅.第四章.模塊與包 4.02
ack 包含 sql mod 名稱空間 app mysql 一次 true 一、模塊的使用之import 1 什麽是模塊?模塊就一系統功能的集合體,在python中,一個py文件就是一個模塊,比如module.py,其中模塊名module2 使用模塊2.1 import 導
Python之旅.第四章.模塊與包.總結(未完待遇)
standard 後綴 att 擔心 lse 綁定 做的 業務 搜索 一、模塊 模塊: 一系列功能的集合體,在python中一個py文件就是一個模塊,模塊名就是py文件的文件名; 模塊的好處: 1.減少重復的代碼 2.拿來主義 定義模塊: 就是創建一個py文件;
Python之旅.第四章.模塊與包4.09
port 換行符 earch re模塊 shel bytes fda count sub 一、shelve模塊 Shelve(了解),是更高程度的封裝。使用時只針對之前設計生成的文件,可以無視不同平臺自動生成的其他文件。 Json的中間格式為字符串,用w寫入文件 Pic
Windows核心編程之核心總結(第四章 進程(一))(2018.6.8)
Windows核心編程之核心總結學習目標 第四章進程的學習可謂是任重而道遠,雖然不難,但知識量很多,也比較零散,需要多總結,腦海裏才有進程的框架。所以,我把本章分為幾個小節來講完。我還是一如既往的添加輔助性內容,希望對於小白有所幫助。而比我流弊的大有人在,大神們可以跳過輔助性內容。本小節的學習目標如下:1.C
現代軟件工程-構建之法---第四章 練習與討論
方法 人的 工作效率 isf 強調 一是 成本 不能 時代 1 、結對項目的案例與論文 論文已閱讀。 2、性格對合作的影響 我的MBTI為:ISFJ 照顧者型(內向實感情感判斷)——值得信賴和依靠。 在團隊合作中,外傾型的人一般會較為熱情對工作積極性比較大,內傾