深入理解PHP內核(五)函數的內部結構

来源:http://www.cnblogs.com/orlion/archive/2016/02/16/5192940.html
-Advertisement-
Play Games

php的函數包括用戶定義的函數、內部函數(print_r count...)、匿名函數、變數函數($func = 'print_r'; $func(array('a','b'));) PHP內核源碼中將函數分為以下類型 #define ZEND_INTERNAL_FUNCTION 1 #define


php的函數包括用戶定義的函數、內部函數(print_r count...)、匿名函數、變數函數($func = 'print_r'; $func(array('a','b'));)

PHP內核源碼中將函數分為以下類型

#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)

  函數不一定顯式的有返回值,在PHP的實現中即使沒有顯式的返回,PHP內核也會幫我們返回NULL。

  ZEND在執行過程中,會將運行時信息存儲於_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;    /* 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;     //函數類型,如用戶自定義則為 #define 
ZEND_USER_FUNCTION 2  
        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存儲了該函數中的所有操作,當函數被調用時,ZEND就會將這個op_array中的opline一條條順序執行,並將最後的結果返回。函數的定義和執行是分開的,一個函數可以作為一個獨立的運行單元存在。

二、內部函數(ZEND_INTERNAL_FUNCTION)

  ZEND_INTERNAL_FUNCTION函數是由擴展或者Zend/PHP內核提供的,用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/zene_API.c文件中的zend_register_function函數,這個函數除了處理函數頁也處理類的方法,包括那些魔術方法。

  內部函數的結構與用戶自定義函數結構基本類似,有一些不同:

  •   調用方法,handler欄位,如果是ZEND_INTERNAL_FUNCTION,那麼ZEND就會調用zend_execute_internal,通過zend_internal_function.handler來執行這個函數。而用戶自定義函數需要生成中間代碼,然後通過中間代碼映射到相對就把方法調用。
  • 內置函數在結構中多了一個module欄位,表示屬於哪個模塊。不同的擴展模塊不同
  • type欄位,在用戶自定義函數中,type欄位幾乎無用,而內置函數中的type欄位作為幾種內部函數的區分。

三、變數函數

  如果一個變數名後邊有圓括弧,php將尋找與變數的值同名的函數,並且嘗試執行。

  變數函數$func

$func = 'print_r';
$func('i am print_r function.');

  編譯後中間代碼

function name:  (null)
number of ops:  9
compiled vars:  !0 = $func
line     # *  op                           fetch          ext  return operands
------------------------------------------------------------------------------
-
-
   2     0  >   EXT_STMT
         1      ASSIGN                                                   !0, 
'print_r'
   3     2      EXT_STMT
         3      INIT_FCALL_BY_NAME                                       !0
         4      EXT_FCALL_BEGIN
         5      SEND_VAL                                                 
'i+am+print_r+function.'
         6      DO_FCALL_BY_NAME                              1
         7      EXT_FCALL_END
         8    > RETURN                                  1

  內部函數

print_r('i am print_r function.');

  編譯後中間代碼

function name:  (null)
number of ops:  6
compiled vars:  none
line     # *  op                           fetch          ext  return  operands
-------------------------------------------------------------------------------
-
-
   2     0  >   EXT_STMT
         1      EXT_FCALL_BEGIN
         2      SEND_VAL                                                 
'i+am+print_r+function.'
         3      DO_FCALL                                      1          
'print_r'
         4      EXT_FCALL_END
         5    > RETURN                                                   1

  對比發現,二者在調用中間代碼上存在一些區別,變數函數是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_HADNLER等函數中。

 

四、匿名函數

  匿名函數是一類不需要指定表示符,而又可以被調用的函數或子常式,匿名函數可以方便的作為參數傳遞給其他函數。

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • I/O類使用 由於在IO操作中,需要使用的數據源有很多,作為一個IO技術的初學者,從讀寫文件開始學習IO技術是一個比較好的選擇。因為文件是一種常見的數據源,而且讀寫文件也是程式員進行IO編程的一個基本能力。本章IO類的使用就從讀寫文件開始。 1 文件操作 文件(File)是 最常見的數據源之一,在程
  • Java學習筆記 類型1.1:類的相關 Tags: 編程,Java 類之間關係 代理例子查看《Java編程思想》中的P131。簡單來說,代理就是組合之後,對組合進來的類的方法進行封裝(不是重寫,而是在函數體內調用組合進來類的對應的"同名"方法。)。 類訪問許可權 |訪問修飾符| 同類 | 同包 |不同
  • 所有的外部輸入參數都應該檢查合法性。 未正確處理輸入數據將可能導致sql註入等漏洞。 框架提供系列函數來取$_REQUEST中的值 requestInt requestString requestFloat requestBool ps:註意$_REQUEST中變數類型可能會是數組 如請求為 ?i[
  • 一、函數的定義 用戶函數的定義從function 關鍵字開始,如下 function foo($var) { echo $var; } 1、詞法分析 在Zend/zend_language_scanner.l中我們找到如下所示的代碼: <ST_IN_SCRIPTING>"function" { re
  • 首先上一張出現問題的圖片: 出現這個問題一般是你的jdk和eclipse不是同一位的,比如你的jdk是32位的但是eclipse下載的是64位的就會導致這種問題。 解決方案:把兩者都換為32或者64位的即可。 如何查看自己的jdk版本?大家可以下載到自己設備運行(註意:一定要以管理員許可權運行),網址
  • 最近碰到一些 SSL 的小問題,特記錄下。 我們有個 Java 實現的 SSL TCP 服務端,為客戶端(PC、Android 和 iOS)提供 SSL 接入連接服務。最近有用戶反饋其手機上 App 不能正常連接登錄,別人手機上都可以。經過單獨回訪調查該用戶使用的手機操作系統是 Android 6.
  • 一、什麼是CocoaPods CocoaPods是iOS項目的依賴管理工具,該項目源碼在Github上管理。開發iOS項目不可避免地要使用第三方開源庫,CocoaPods的出現使得我們可以節省設置和第三方開源庫的時間。 在使用CocoaPods之前,開發項目需要用到第三方開源庫的時候,我們需要 1...
  • 問題沒有解決,屬於菜鳥級別的孩子~~~~ 求助啊,求助!!!!!! 報告如下: Warning:An unhandled exception occurred. Please report the problemusing the error reporting dialog or via emai
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...