ホームページ  >  記事  >  バックエンド開発  >  PHPカーネルの内部構造を深く理解する(5)関数

PHPカーネルの内部構造を深く理解する(5)関数

WBOY
WBOYオリジナル
2016-06-23 13:16:38766ブラウズ

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

1. ユーザー関数(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 も 2 つの部分で初期化されます:

typedef struct _zend_function_state {    zend_function *function;    void **arguments;} zend_function_state;

* 引数は関数へのポインタです。 *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 を 1 つずつ順番に実行し、最終結果を返します。関数の定義と実行は分離されており、関数は独立した実行単位として存在できます。

2. 内部関数 (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 構造体を作成し、その型を ZEND_INTERNAL_FUNCTION に設定します。グローバル関数テーブル (HashTable 構造) に入力されます。関数の設定と登録のプロセスについては、Zend/zene_API.c ファイルの zend_register_function 関数を参照してください。この関数は、関数ページの処理に加えて、クラス メソッドも処理します。魔法の方法。

内部関数の構造は基本的にユーザー定義関数の構造と似ていますが、いくつかの違いがあります:

呼び出しメソッド、ハンドラーフィールド、それが ZEND_INTERNAL_FUNCTION の場合、ZEND は zend_execute_internal を呼び出し、zend_internal_function.handler を通じてこの関数を実行します。ユーザー定義関数は中間コードを生成し、その中間コードを呼び出される相対メソッドにマップする必要があります。
  • 組み込み関数には、それがどのモジュールに属しているかを示す追加のモジュール フィールドが構造内にあります。拡張モジュールごとに異なる
  • type フィールドがあります。ユーザー定義関数では、type フィールドはほとんど役に立ちませんが、組み込み関数の type フィールドは、いくつかの内部関数間の区別として機能します。
  • 3. 変数関数

    変数名の後に括弧がある場合、PHP は変数の値と同じ名前の関数を探して実行しようとします。

    変数関数 $func

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

    コンパイル済み中間コード

    function name:  (null)number of ops:  9compiled vars:  !0 = $funcline     # *  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:  6compiled vars:  noneline     # *  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 および他の関数に反映されます。

    4. 匿名関数

    匿名関数は、指定された識別子を必要とせず、他の関数にパラメータとして渡すことができる関数またはサブルーチンの一種です。

    声明:
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。