對於PHPer而言,咱們一般會把經常使用的功能封裝成一個函數來進行復用,以提高開發效率。可是php究竟是如何找到對應的函數的呢?今天,咱們來一塊兒尋找答案~php
首先,咱們先回顧一下php的函數分類,php函數包含用戶自定義函數、內部函數、匿名函數等多種類型。而對於用戶自定義函數和內部函數,他們分別存在對應本身的數據結構,可是Zend引擎爲了適配兩種函數類型,因此定義了一種新的數據結構:zend_function
聯合體數據結構
咱們仍是先看下zend_function
聯合體,瞭解下爲何針對用戶自定義函數和內部函數要作適配函數
typedef union _zend_function {
zend_uchar type; //函數類型,用來標記是用戶自定義函數仍是內部函數
struct {
zend_uchar type; /* never used */
char *function_name; //函數名稱
zend_class_entry *scope; //函數所在的類做用域,用來標記做爲成員方法時所屬的類
zend_uint fn_flags; // 標記其做爲類的成員方法時的訪問類型,是public、protected仍是private
union _zend_function *prototype; //函數原型,標記內部函數或者用戶自定義函數所屬的zend_function
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;
struct _zend_op_array {
/* 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 */
zend_bool done_pass_two;
....// 其它字段
}
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;
複製代碼
從上面介紹的內容,咱們能夠發現,無論是用戶自定義函數仍是內部函數,在底層存儲時都會存在共有字段type
和common
,因此他們以聯合體的方式共享內存,能夠節省內存空間和快速獲取函數的基本信息,而且若是有須要,能夠在一些結構間完美的進行強制類型轉換。即zend_function
能夠與zend_op_array
互換,zend_function
能夠與zend_internal_function
互換ui
聊完了用戶自定義函數和內部函數的數據結構存儲,咱們再來看下全局函數列表spa
全局函數列表,能夠理解成函數註冊表,其內部實現是一個哈希表。用戶自定義函數和內部函數編譯完成後會將函數名註冊到全局函數列表中。也就是此時會判斷是否全局函數列表中存在同名函數prototype
那麼用戶自定義函數和內部函數在存儲到全局函數列表時有什麼不一樣呢?指針
用戶自定義函數: 咱們寫的php函數在編譯階段會通過詞法分析->語法分析->生成中間代碼opcode->執行中間代碼的過程,執行中間代碼時,會將函數名註冊到全局函數列表rest
內部函數: php啓動時,會加載全部擴展模塊,併爲擴展模塊中每個函數建立一個zend_internal_function結構,並將這個函數名註冊到全局函數列表code
接下來,咱們再來看下調用函數時,是如何執行的呢?內存
函數調用時,首先會根據函數名去全局函數列表內查找是否存在該函數,若是不存在,則會直接報出「Call to undefined function xxx()"的錯誤信息;若是存在,則獲取該函數指針對應的函數結構中的type字段,判斷其函數類型,若是函數類型是自定義函數,則調用zend_execute來執行函數的zend_op_array內容,而若是函數類型是內部函數,則直接調用zend_internal_function的handle指針指向的擴展模塊的C函數
到這裏,你們應該能夠找到開頭咱們提出的問題的答案了。其實就是經過函數註冊到全局函數列表,而後函數調用時,再從全局函數列表中查找對應函數進行執行來實現的;只不過,對於用戶自定義函數和內部函數而言,其實現方式不一樣,固然這也就意味着執行效率的不一樣(固然php內部函數執行效率更高了,由於它沒有運行時的編譯階段,至關於直接執行c語言,因此能用php內部函數的儘可能使用內部函數)
今天咱們就聊到這裏了,歡迎你們的手動點贊~