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

WBOY
リリース: 2016-06-13 12:28:38
オリジナル
788 人が閲覧しました

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

PHP 関数には、ユーザー定義関数、内部関数 (print_r count...)、匿名関数、変数関数 ($func = 'print_r) が含まれます。 '; $ func(array('a','b'));)

PHP カーネルのソースコード内の関数は次の種類に分類されます

<span style="color: #0000ff;">#define</span> ZEND_INTERNAL_FUNCTION              1<span style="color: #0000ff;">#define</span> ZEND_USER_FUNCTION                  2  <span style="color: #0000ff;">#define</span> ZEND_OVERLOADED_FUNCTION            3<span style="color: #0000ff;">#define</span> ZEND_EVAL_CODE                      4<span style="color: #0000ff;">#define</span> ZEND_OVERLOADED_FUNCTION_TEMPORARY  5
ログイン後にコピー

1. ユーザー関数 (ZEND_USER_FUNCTION)

PHP 実装では、明示的な戻り値がない場合でも、関数は必ずしも明示的な戻り値を持ちません。 PHP カーネルは NULL を返します。

実行プロセス中、ZEND はランタイム情報を _zend_execute_data に保存します。

<span style="color: #0000ff;">struct</span><span style="color: #000000;"> _zend_execute_data {    </span><span style="color: #008000;">//</span><span style="color: #008000;">...省略部分代码</span><span style="color: #000000;">    zend_function_state function_state;    zend_function </span>*fbc; <span style="color: #008000;">/*</span><span style="color: #008000;"> Function Being Called </span><span style="color: #008000;">*/</span>    <span style="color: #008000;">//</span><span style="color: #008000;">...省略部分代码</span>};
ログイン後にコピー

プログラムの初期化プロセス中に、 function_state も初期化されます。 function_state は次のもので構成されます。 2 つの部分:

typedef <span style="color: #0000ff;">struct</span><span style="color: #000000;"> _zend_function_state {    zend_function </span>*<span style="color: #000000;">function;    </span><span style="color: #0000ff;">void</span> **<span style="color: #000000;">arguments;} zend_function_state;</span>
ログイン後にコピー

*arguments は関数パラメーターへのポインターであり、関数本体は *function に格納されます。これは zend_function 構造体であり、最終的にすべての情報が格納されます。ユーザー定義関数について 具体的な構造は次のとおりです:

<span style="color: #000000;">typedef union _zend_function {    zend_uchar type;    </span><span style="color: #008000;">/*</span><span style="color: #008000;"> MUST be the first element of this struct! </span><span style="color: #008000;">*/</span>     <span style="color: #0000ff;">struct</span><span style="color: #000000;"> {        zend_uchar type;  </span><span style="color: #008000;">/*</span><span style="color: #008000;"> never used </span><span style="color: #008000;">*/</span>        <span style="color: #0000ff;">char</span> *function_name;    <span style="color: #008000;">//</span><span style="color: #008000;">函数名称</span>        zend_class_entry *scope; <span style="color: #008000;">//</span><span style="color: #008000;">函数所在的类作用域</span>        zend_uint fn_flags;     <span style="color: #008000;">//</span><span style="color: #008000;">函数类型,如用户自定义则为 #define </span>ZEND_USER_FUNCTION <span style="color: #800080;">2</span><span style="color: #000000;">          union _zend_function </span>*prototype; <span style="color: #008000;">//</span><span style="color: #008000;">函数原型</span>        zend_uint num_args;     <span style="color: #008000;">//</span><span style="color: #008000;">参数数目</span>        zend_uint required_num_args; <span style="color: #008000;">//</span><span style="color: #008000;">需要的参数数目</span>        zend_arg_info *arg_info;  <span style="color: #008000;">//</span><span style="color: #008000;">参数信息指针</span><span style="color: #000000;">        zend_bool pass_rest_by_reference;        unsigned </span><span style="color: #0000ff;">char</span> return_reference;  <span style="color: #008000;">//</span><span style="color: #008000;">返回值</span><span style="color: #000000;">    } common;     zend_op_array op_array;   </span><span style="color: #008000;">//</span><span style="color: #008000;">函数中的操作</span><span style="color: #000000;">?    zend_internal_function internal_function;  } zend_function;</span>
ログイン後にコピー

zend_function の構造内の op_array は、関数が呼び出されるときに、関数内のすべての操作を格納します。 ZEND は、この op_array 内の opline を 1 つずつ順番に実行し、最終結果を返します。関数の定義と実行は分離されており、関数は独立した実行単位として存在できます。

2. 内部関数 (ZEND_INTERNAL_FUNCTION)

ZEND_INTERNAL_FUNCTION 関数は、c/c で記述された拡張機能または Zend/PHP カーネルによって提供されます。直接実行できる関数。内部関数の構造は次のとおりです。

typedef <span style="color: #0000ff;">struct</span><span style="color: #000000;"> _zend_internal_function {    </span><span style="color: #008000;">/*</span><span style="color: #008000;"> Common elements </span><span style="color: #008000;">*/</span><span style="color: #000000;">    zend_uchar type;    </span><span style="color: #0000ff;">char</span> *<span style="color: #000000;"> function_name;    zend_class_entry </span>*<span style="color: #000000;">scope;    zend_uint fn_flags;    union _zend_function </span>*<span style="color: #000000;">prototype;    zend_uint num_args;    zend_uint required_num_args;    zend_arg_info </span>*<span style="color: #000000;">arg_info;    zend_bool pass_rest_by_reference;    unsigned </span><span style="color: #0000ff;">char</span><span style="color: #000000;"> return_reference;    </span><span style="color: #008000;">/*</span><span style="color: #008000;"> END of common elements </span><span style="color: #008000;">*/</span>     <span style="color: #0000ff;">void</span> (*<span style="color: #000000;">handler)(INTERNAL_FUNCTION_PARAMETERS);    </span><span style="color: #0000ff;">struct</span> _zend_module_entry *<span style="color: #000000;">module;} zend_internal_function;</span>
ログイン後にコピー

モジュールが初期化されると、ZE はロードされた各拡張モジュールを走査し、function_entry を追加します。モジュール内 指定された関数 (モジュール>関数) ごとに、zend_internal_function 構造体を作成し、そのタイプを ZEND_INTERNAL_FUNCTION に設定し、この構造体をグローバル関数テーブル (HashTable 構造体) に入力します。関数の設定と登録については、Zend/zene_API を参照してください。 c ファイルの zend_register_function 関数は、関数ページの処理に加えて、これらのマジック メソッドを含むクラス メソッドも処理します。

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

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

3. 変数関数

変数名の後に括弧がある場合、PHP は同じ名前の関数を検索します。を変数の値として指定し、実行してみます。

変数関数 $func

$func = <span style="color: #800000;">'</span><span style="color: #800000;">print_r</span><span style="color: #800000;">'</span><span style="color: #000000;">;$func(</span><span style="color: #800000;">'</span><span style="color: #800000;">i am print_r function.</span><span style="color: #800000;">'</span>);
ログイン後にコピー

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

function name:  (<span style="color: #0000ff;">null</span><span style="color: #000000;">)number of ops:  </span><span style="color: #800080;">9</span><span style="color: #000000;">compiled vars:  </span>!<span style="color: #800080;">0</span> =<span style="color: #000000;"> $funcline     # </span>*  op                           fetch          ext  <span style="color: #0000ff;">return</span><span style="color: #000000;"> operands</span>--------------------------------------------------------------------------------   <span style="color: #800080;">2</span>     <span style="color: #800080;">0</span>  ><span style="color: #000000;">   EXT_STMT         </span><span style="color: #800080;">1</span>      ASSIGN                                                   !<span style="color: #800080;">0</span><span style="color: #000000;">, </span><span style="color: #800000;">'</span><span style="color: #800000;">print_r</span><span style="color: #800000;">'</span>   <span style="color: #800080;">3</span>     <span style="color: #800080;">2</span><span style="color: #000000;">      EXT_STMT         </span><span style="color: #800080;">3</span>      INIT_FCALL_BY_NAME                                       !<span style="color: #800080;">0</span>         <span style="color: #800080;">4</span><span style="color: #000000;">      EXT_FCALL_BEGIN         </span><span style="color: #800080;">5</span><span style="color: #000000;">      SEND_VAL                                                 </span><span style="color: #800000;">'</span><span style="color: #800000;">i+am+print_r+function.</span><span style="color: #800000;">'</span>         <span style="color: #800080;">6</span>      DO_FCALL_BY_NAME                              <span style="color: #800080;">1</span>         <span style="color: #800080;">7</span><span style="color: #000000;">      EXT_FCALL_END         </span><span style="color: #800080;">8</span>    > RETURN                                  1
ログイン後にコピー

内部関数

print_r(<span style="color: #800000;">'</span><span style="color: #800000;">i am print_r function.</span><span style="color: #800000;">'</span>);
ログイン後にコピー

コンパイルされた中間コード

function name:  (<span style="color: #0000ff;">null</span><span style="color: #000000;">)number of ops:  </span><span style="color: #800080;">6</span><span style="color: #000000;">compiled vars:  noneline     # </span>*  op                           fetch          ext  <span style="color: #0000ff;">return</span><span style="color: #000000;">  operands</span>---------------------------------------------------------------------------------   <span style="color: #800080;">2</span>     <span style="color: #800080;">0</span>  ><span style="color: #000000;">   EXT_STMT         </span><span style="color: #800080;">1</span><span style="color: #000000;">      EXT_FCALL_BEGIN         </span><span style="color: #800080;">2</span><span style="color: #000000;">      SEND_VAL                                                 </span><span style="color: #800000;">'</span><span style="color: #800000;">i+am+print_r+function.</span><span style="color: #800000;">'</span>         <span style="color: #800080;">3</span>      DO_FCALL                                      <span style="color: #800080;">1</span>          <span style="color: #800000;">'</span><span style="color: #800000;">print_r</span><span style="color: #800000;">'</span>         <span style="color: #800080;">4</span><span style="color: #000000;">      EXT_FCALL_END         </span><span style="color: #800080;">5</span>    > RETURN                                                   <span style="color: #800080;">1</span>
ログイン後にコピー

比較すると、この 2 つの中間コードの呼び出しにはいくつかの違いがあることがわかります。変数関数は DO_FCALL_BY_NAME です。 while 内部関数は DO_FCALL です。これは構文解析中に決定されます。Zend/zend_complie.c ファイルの zend_do_end_function_call 関数のコードの一部を参照してください。

<span style="color: #0000ff;">if</span> (!is_method && !is_dynamic_fcall && function_name->op_type==<span style="color: #000000;">IS_CONST) {        opline</span>->opcode =<span style="color: #000000;"> ZEND_DO_FCALL;        opline</span>->op1 = *<span style="color: #000000;">function_name;        ZVAL_LONG(</span>&opline-><span style="color: #000000;">op2.u.constant, zend_hash_func(Z_STRVAL(function_name</span>->u.constant), Z_STRLEN(function_name->u.constant) + <span style="color: #800080;">1</span><span style="color: #000000;">));    } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {        opline</span>->opcode =<span style="color: #000000;"> ZEND_DO_FCALL_BY_NAME;        SET_UNUSED(opline</span>-><span style="color: #000000;">op1);    }</span>
ログイン後にコピー

メソッドではなく、これは動的呼び出しではなく、関数の名前は文字列変数であり、生成される中間コードは ZEND_DO_FCALL です。それ以外の場合は、ZEND_DO_FCALL_BY_NAME です。また、変数関数はコールバック関数として使用され、その処理プロセスは Zend/zend_complie.c ファイルの zend_do_pass_param 関数内にあり、最終的に中間コードの実行時に ZEND_SEND_VAL_SPEC_CONST_HADNLER などの関数に反映されます。

4. 匿名関数

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

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!