1. 程式人生 > >深入理解PHP原理之函式(Introspecting PHP Function)

深入理解PHP原理之函式(Introspecting PHP Function)



宣告:此內容轉載鳥哥,只是留作個人學習、記錄用。為表尊重,特此說明。

在PHP中,函式分為倆種,
•一種是zend_internal_function, 這種函式是由擴充套件或者Zend/PHP核心提供的,用’C/C++’編寫的,可以直接執行的函式。
•另外一種是zend_user_function, 這種函式呢,就是我們經常在見的,使用者在PHP指令碼中定義的函式,這種函式最終會被ZE翻譯成opcode array來執行

檢視zend_compile.h,我們可以找到如下的3個結構:
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;
 
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_uint *refcount;
 
    zend_op *opcodes;
    zend_uint last, size;
 
    zend_compiled_variable *vars;
    int last_var, size_var;
 
    zend_uint T;
 
    zend_brk_cont_element *brk_cont_array;
    zend_uint last_brk_cont;
    zend_uint current_brk_cont;
 
    zend_try_catch_element *try_catch_array;
    int last_try_catch;
 
    /* static variables support */
    HashTable *static_variables;
 
    zend_op *start_op;
    int backpatch_count;
 
    zend_bool done_pass_two;
    zend_bool uses_this;
 
    char *filename;
 
     zend_uint line_start;
    zend_uint line_end;
    char *doc_comment;
    zend_uint doc_comment_len;
 
    void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
 
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;
        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_internal_function, 當PHP啟動的時候 ,它會遍歷每個載入的擴充套件模組,然後將模組中function_entry中指明的每一個函式, 建立一個zend_internal_function結構, 並將type置為ZEND_INTERNAL_FUNCTION(見下表), 將這個結構填入全域性的函式表(一個HashTable);
#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
    


第二個結構,op_array, 這個結構很重要, 因為:
      extern ZEND_API zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
 


也就是說,我們編寫的PHP指令碼,都會被ZE翻譯成op_array, 最後交由zend_execute執行。

另外,在ZE中,使用者定義的函式(userland function), 也會被翻譯成一個op_array, 並填入全域性函式表中。這個時候scope,function_name都不為空。而對於在全域性作用域的直接程式碼來說,最後的op_array的scope為全域性,function_name為空。

第三個結構, 很有趣, 要理解這個結構,首先你要理解他的設計目標: zend_internal_function, zend_function,zend_op_array可以安全的互相轉換(The are not identical structs, but all the elements that are in “common” they hold in common, thus the can safely be casted to each other);

具體來說,當在op code中通過ZEND_DO_FCALL呼叫一個函式的時候,ZE會在函式表中,根據名字(其實是lowercase的函式名字,這也就是為什麼PHP的函式名是大小寫不敏感的)查詢函式, 如果找到,返回一個zend_function結構的指標(仔細看這個上面的zend_function結構), 然後判斷type,如果是ZEND_INTERNAL_FUNCTION, 那麼ZE就呼叫zend_execute_internal,通過zend_internal_function.handler來執行這個函式, 如果不是,就呼叫zend_execute來執行這個函式包含的zend_op_array.