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 を通じてこの関数を実行します。ユーザー定義関数は中間コードを生成し、その中間コードを呼び出される相対メソッドにマップする必要があります。
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. 匿名関数
匿名関数は、指定された識別子を必要とせず、他の関数にパラメータとして渡すことができる関数またはサブルーチンの一種です。