摘要:但是到底是如何找到對應的函數的呢今天,我們來一起尋找答案函數分類首先,我們先回顧一下的函數分類,函數包含用戶自定義函數內部函數匿名函數等多種類型。用戶自定義函數和內部函數編譯完成后會將函數名注冊到全局函數列表中。
對于PHPer而言,我們通常會把常用的功能封裝成一個函數來進行復用,以提升開發效率。但是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互換
函數注冊聊完了用戶自定義函數和內部函數的數據結構存儲,我們再來看下全局函數列表
全局函數列表,可以理解成函數注冊表,其內部實現是一個哈希表。用戶自定義函數和內部函數編譯完成后會將函數名注冊到全局函數列表中。也就是此時會判斷是否全局函數列表中存在同名函數
那么用戶自定義函數和內部函數在存儲到全局函數列表時有什么不同呢?
用戶自定義函數:
我們寫的php函數在編譯階段會經過詞法分析->語法分析->生成中間代碼opcode->執行中間代碼的過程,執行中間代碼時,會將函數名注冊到全局函數列表
內部函數:
php啟動時,會加載所有擴展模塊,并為擴展模塊中每一個函數創建一個zend_internal_function結構,并將這個函數名注冊到全局函數列表
接下來,我們再來看下調用函數時,是如何執行的呢?
函數調用時,首先會根據函數名去全局函數列表內查找是否存在該函數,如果不存在,則會直接報出“Call to undefined function xxx()"的錯誤信息;如果存在,則獲取該函數指針對應的函數結構中的type字段,判斷其函數類型,如果函數類型是自定義函數,則調用zend_execute來執行函數的zend_op_array內容,而如果函數類型是內部函數,則直接調用zend_internal_function的handle指針指向的擴展模塊的C函數
總結到這里,大家應該可以找到開頭我們提出的問題的答案了。其實就是通過函數注冊到全局函數列表,然后函數調用時,再從全局函數列表中查找對應函數進行執行來實現的;只不過,對于用戶自定義函數和內部函數而言,其實現方式不同,當然這也就意味著執行效率的不同(當然php內部函數執行效率更高了,因為它沒有運行時的編譯階段,相當于直接執行c語言,所以能用php內部函數的盡量使用內部函數)
今天我們就聊到這里了,歡迎大家的手動點贊~
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/31237.html
摘要:對于來說,變量有全局變量和局部變量之分那么,他們都是存儲到一個哈希表內了么其實不是的,變量存儲也有作用域的概念。 上次跟大家講了垃圾回收機制后,有些小伙伴對底層原理比較感興趣,私信問我了一些關于變量的相關知識,既然大家對變量比較感興趣,那么這次我們來系統的講一下變量的底層原理 變量結構 首先,我們還是先擺上我們的zval結構體,即php所有變量都會以zval結構體的形式實現 struc...
摘要:數組是最常用的數據類型,同時容易上手也得益于其強大的數組,但是數組在中是如何實現的呢首先,我們還是先了解下相關的數據結構,為下面的內容打好基礎哈希表哈希表,顧名思義,即將不同的關鍵字映射到不同單元的一種數據結構。 數組是PHPer最常用的數據類型,同時php容易上手也得益于其強大的數組,但是數組在php中是如何實現的呢? 首先,我們還是先了解下相關的數據結構,為下面的內容打好基礎 哈希...
摘要:但是對于結構體中的和字段我們一直都沒有詳細介紹過,而這兩個字段其實是和變量之間賦值的原理有著密切的關系的。 上周我們從底層的角度介紹了php變量從生成->常量賦值->銷毀的完整生命周期(不了解的同學可以翻看一下前面的文章php底層原理之變量(一)),但是我們留了一個思考,不知道大家有答案了沒,變量之間的賦值在底層又是如何實現的呢? 變量之間賦值 php變量的zval結構,我們已經介紹了...
摘要:總結垃圾回收機制以的引用計數機制為基礎以前只有該機制同時使用根緩沖區機制,當發現有存在循環引用的時,就會把其投入到根緩沖區,當根緩沖區達到配置文件中的指定數量后,就會進行垃圾回收,以此解決循環引用導致的內存泄漏問題開始引入該機制 php垃圾回收機制,對于PHPer來說是一個不陌生但是又不是很熟悉的內容。那么php是怎么實現對不需要的內存進行回收的呢? php變量的內部存儲結構 首先還是...
閱讀 2215·2021-11-22 11:56
閱讀 2657·2021-10-08 10:05
閱讀 7840·2021-09-22 15:53
閱讀 1927·2021-09-22 15:29
閱讀 2247·2021-09-08 09:35
閱讀 3370·2021-09-07 10:12
閱讀 1390·2019-08-30 13:11
閱讀 1993·2019-08-28 17:54