天天看點

(PHP7核心剖析-6) 函數

1.函數的存儲結構

typedef union  _zend_function        zend_function;

union _zend_function {
    zend_uchar type;    
    struct {
        zend_uchar type; 
        zend_uchar arg_flags[3];
        uint32_t fn_flags;
        zend_string *function_name;
        zend_class_entry *scope; //成員方法所屬類,面向對象實作中用到
        union _zend_function *prototype;
        uint32_t num_args; //參數數量
        uint32_t required_num_args; //必傳參數數量
        zend_arg_info *arg_info; //參數資訊
    } common;
    zend_op_array op_array; //自定義函數(函數實際編譯為普通的zend_op_array)
    zend_internal_function internal_function; //内部函數(通過擴充或者核心提供的C函數)
};
           
zend_function.common.xx快速通路到zend_function.zend_op_array.xx及zend_function.zend_internal_function.xx
zend_function.type取到zend_function.op_array.type及zend_function.internal_function.type
           
(PHP7核心剖析-6) 函數
PHP在編譯階段将使用者自定義的函數編譯為獨立的opcodes,儲存在EG(function_table)中,調用時重新配置設定新的zend_execute_data(相當于運作棧),然後執行函數的opcodes,調用完再還原到舊的zend_execute_data,繼續執行,EG(function_table)是一個哈希表,記錄的就是PHP中所有的函數

2.函數參數

函數參數在核心實作上與函數内的局部變量實際是一樣的,提供一個單獨的編号,參數名稱也在zend_op_array.vars中,編号首先是從參數開始的,是以按照參數順序其編号依次為0、1、2...(轉化為相對記憶體偏移量就是96、112、128...),然後函數調用時首先會在調用位置将參數的value複制到各參數各自的位置
//參數的額外資訊
typedef struct _zend_arg_info {
    zend_string *name; //參數名
    zend_string *class_name;
    zend_uchar type_hint; //顯式聲明的參數類型,比如(array $param_1)
    zend_uchar pass_by_reference; //是否引用傳參,參數前加&的這個值就是1
    zend_bool allow_null; //是否允許為NULL
    zend_bool is_variadic; //是否為可變參數,即...用法,function my_func($a, ...$b){...}
} zend_arg_info;
           
每個參數都有一個上面的結構,所有參數的結構儲存在zend_op_array.arg_info數組中,這裡有一個地方需要注意:zend_op_array->arg_info數組儲存的并不全是輸入參數,如果函數聲明了傳回值類型則也會為它建立一個zend_arg_info,這個結構在arg_info數組的第一個位置,這種情況下zend_op_array->arg_info指向的實際是數組的第二個位置,傳回值的結構通過zend_op_array->arg_info[-1]讀取

3.内部函數

内部函數指的是由核心、擴充提供的C語言編寫的function,這類函數不需要經曆opcode的編譯過程,是以效率上要高于PHP使用者自定義的函數,調用時與普通的C程式沒有差異。Zend引擎中定義了很多内部函數供使用者在PHP中使用,比如:define、defined、strlen、method_exists、class_exists、function_exists......等等,除了Zend引擎中定義的内部函數,PHP擴充中也提供了大量内部函數,我們也可以靈活的通過擴充自行定制。
//zend_internal_function頭部是一個與zend_op_array完全相同的common結構
typedef struct _zend_internal_function {
    /* Common elements */
    zend_uchar type;
    zend_uchar arg_flags[3]; /* bitset of arg_info.pass_by_reference */
    uint32_t fn_flags;
    zend_string* function_name;
    zend_class_entry *scope;
    zend_function *prototype;
    uint32_t num_args;
    uint32_t required_num_args;
    zend_internal_arg_info *arg_info;
    /* END of common elements */

    void (*handler)(INTERNAL_FUNCTION_PARAMETERS); //函數指針,展開:void (*handler)(zend_execute_data *execute_data, zval *return_value)
    struct _zend_module_entry *module;
    void *reserved[ZEND_MAX_RESERVED_RESOURCES];
} zend_internal_function;