ホームページ >バックエンド開発 >PHPチュートリアル >PHPカーネル(6)関数の定義、パラメータ転送、戻り値を深く理解する

PHPカーネル(6)関数の定義、パラメータ転送、戻り値を深く理解する

WBOY
WBOYオリジナル
2016-06-13 12:28:37883ブラウズ

PHP カーネルの定義、パラメータ転送、戻り値を深く理解する (6) 関数

1. 関数の定義

ユーザー関数の定義は次のように関数キーワードから始まります。

<span style="color: #0000ff;">function</span> foo(<span style="color: #800080;">$var</span><span style="color: #000000;">) {    </span><span style="color: #0000ff;">echo</span> <span style="color: #800080;">$var</span><span style="color: #000000;">;}</span>

1. 字句解析

Zend/zend_ language_scanner .l 次のコードが見つかります:

<ST_IN_SCRIPTING><span style="color: #800000;">"</span><span style="color: #800000;">function</span><span style="color: #800000;">"</span><span style="color: #000000;"> {    </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> T_FUNCTION;}</span>

これは、関数が T_FUNCTION タグを生成することを意味します。このトークンを取得したら、構文解析を開始します。

2. 構文分析

次のように、Zend/zend_ language_parser.y ファイル内の関数の宣言プロセス マークを見つけます。

関数 is_reference T_STRING に注目します。これは、関数のキーワード、参照するかどうか、および関数名を示します。
<span style="color: #000000;">function:    T_FUNCTION { $$.u.opline_num </span>=<span style="color: #000000;"> CG(zend_lineno); }; is_reference:        </span><span style="color: #008000;">/*</span><span style="color: #008000;"> empty </span><span style="color: #008000;">*/</span> { $$.op_type =<span style="color: #000000;"> ZEND_RETURN_VAL; }    </span>|   <span style="color: #800000;">'</span><span style="color: #800000;">&</span><span style="color: #800000;">'</span>         { $$.op_type =<span style="color: #000000;"> ZEND_RETURN_REF; }; unticked_function_declaration_statement:        function is_reference T_STRING {zend_do_begin_function_declaration(</span>&$<span style="color: #800080;">1</span>, &$<span style="color: #800080;">3</span>, <span style="color: #800080;">0</span>, $<span style="color: #800080;">2</span><span style="color: #000000;">.op_type, NULL TSRMLS_CC); }            </span><span style="color: #800000;">'</span><span style="color: #800000;">(</span><span style="color: #800000;">'</span> parameter_list <span style="color: #800000;">'</span><span style="color: #800000;">)</span><span style="color: #800000;">'</span> <span style="color: #800000;">'</span><span style="color: #800000;">{</span><span style="color: #800000;">'</span> inner_statement_list <span style="color: #800000;">'</span><span style="color: #800000;">}</span><span style="color: #800000;">'</span><span style="color: #000000;"> {                zend_do_end_function_declaration(</span>&$<span style="color: #800080;">1</span><span style="color: #000000;"> TSRMLS_CC); };</span>
T_FUNCTION マークは、関数宣言を見つけるためにのみ使用され、これが関数宣言であることを示します。関数であり、パラメーターや戻り値など、この関数に関連する作業がさらに多くなります。

3. 中間コードの生成

構文解析後、実行されたコンパイル済み関数は zend_do_begin_function_declaration であることがわかります。次のように Zend/zend_complie.c ファイル内の実装を見つけます。

生成されるコードは、この中間コードとオペランドに対応する op_type に基づいた ZEND_DECLARE_FUNCTION です。中間コードの実行関数は ZEND_DECLARE_FUNCTION_SPEC_HANDLER であることがわかります。
<span style="color: #0000ff;">void</span> zend_do_begin_function_declaration(znode *<span style="color: #000000;">function_token, znode </span>*<span style="color: #000000;">function_name, </span><span style="color: #0000ff;">int</span> is_method, <span style="color: #0000ff;">int</span> return_reference, znode *fn_flags_znode TSRMLS_DC) <span style="color: #008000;">/*</span><span style="color: #008000;"> {{{ </span><span style="color: #008000;">*/</span><span style="color: #000000;">{    ...</span><span style="color: #008000;">//</span><span style="color: #008000;">省略</span>    function_token->u.op_array =<span style="color: #000000;"> CG(active_op_array);    lcname </span>=<span style="color: #000000;"> zend_str_tolower_dup(name, name_len);     orig_interactive </span>=<span style="color: #000000;"> CG(interactive);    CG(interactive) </span>= <span style="color: #800080;">0</span><span style="color: #000000;">;    init_op_array(</span>&<span style="color: #000000;">op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC);    CG(interactive) </span>=<span style="color: #000000;"> orig_interactive;      ...</span><span style="color: #008000;">//</span><span style="color: #008000;">省略</span>     <span style="color: #0000ff;">if</span><span style="color: #000000;"> (is_method) {        ...</span><span style="color: #008000;">//</span><span style="color: #008000;">省略,类方法 在后面的章节介绍</span>?!<span style="color: #000000;">?GH    } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {        zend_op </span>*opline =<span style="color: #000000;"> get_next_op(CG(active_op_array) TSRMLS_CC);          opline</span>->opcode =<span style="color: #000000;"> ZEND_DECLARE_FUNCTION;        opline</span>->op1.op_type =<span style="color: #000000;"> IS_CONST;        build_runtime_defined_function_key(</span>&opline-><span style="color: #000000;">op1.u.constant, lcname,            name_len TSRMLS_CC);        opline</span>->op2.op_type =<span style="color: #000000;"> IS_CONST;        opline</span>->op2.u.constant.type =<span style="color: #000000;"> IS_STRING;        opline</span>->op2.u.constant.value.str.val =<span style="color: #000000;"> lcname;        opline</span>->op2.u.constant.value.str.len =<span style="color: #000000;"> name_len;        Z_SET_REFCOUNT(opline</span>->op2.u.constant, <span style="color: #800080;">1</span><span style="color: #000000;">);        opline</span>->extended_value =<span style="color: #000000;"> ZEND_DECLARE_FUNCTION;        zend_hash_update(CG(function_table), opline</span>-><span style="color: #000000;">op1.u.constant.value.str.val,            opline</span>->op1.u.constant.value.str.len, &<span style="color: #000000;">op_array, </span><span style="color: #0000ff;">sizeof</span><span style="color: #000000;">(zend_op_array),             (</span><span style="color: #0000ff;">void</span> **) &<span style="color: #000000;">CG(active_op_array));    } }</span><span style="color: #008000;">/*</span><span style="color: #008000;"> }}} </span><span style="color: #008000;">*/</span><span style="color: #000000;"><br></span>
中間コードを生成すると、関数名がすべて小文字に統一されており、関数名の大文字と小文字が区別されていないことがわかります。

この実装を検証するために、コードの一部を見てみましょう。

コードを実行すると、エラーが報告されます。 致命的なエラー: t( を再宣言できません) ) (以前に ... で宣言)
<span style="color: #000000;">function T() {    echo </span><span style="color: #800080;">1</span><span style="color: #000000;">;} function t() {    echo </span><span style="color: #800080;">2</span><span style="color: #000000;">;}</span>
は、PHP の場合、T と t が同じ関数名であることを意味します。この処理はどこで行われますか?

4. 中間コードを実行します

Zend/zend_vm_execute.h ファイル内の ZEND_DECLARE_FUNCTION 中間コードに対応する実行関数: ZEND_DECLARE_FUNCTION_SPEC_HANDLER を見つけます。この関数は関数 do_bind_function のみを呼び出します。呼び出しコードは次のとおりです:

この関数では、EX (opline) が指す関数を EG (function_table) に追加し、同じ名前の関数が既に存在するかどうかを判断します。存在する場合、EG (function_table) は実行中にすべての関数情報を保存するために使用されます。これは関数のレジストリに相当します。その構造は HashTable であるため、do_bind_function 関数に新しい関数を追加するには、HashTable 操作関数 zend_hash_add
do_bind_function(EX(opline), EG(function_table), <span style="color: #800080;">0</span>);

を使用します。 2. 関数パラメータ

関数の定義は、関数リストに関数名を登録するだけの処理です。

1. ユーザー定義関数のパラメーター

この関数では、関数のパラメーター チェックが zend_do_receive_arg 関数によって実装されていることがわかります。パラメータは次のとおりです:

パラメータ転送全体は、中間コードの arg_info フィールドに値を代入することで完了します。重要な点は arg_info フィールドにあります。 arg_info フィールドの構造は次のとおりです。
CG(active_op_array)->arg_info = erealloc(CG(active_op_array)-><span style="color: #000000;">arg_info,        </span><span style="color: #0000ff;">sizeof</span>(zend_arg_info)*(CG(active_op_array)-><span style="color: #000000;">num_args));cur_arg_info </span>= &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-<span style="color: #800080;">1</span><span style="color: #000000;">];cur_arg_info</span>->name = estrndup(varname-><span style="color: #000000;">u.constant.value.str.val,        varname</span>-><span style="color: #000000;">u.constant.value.str.len);cur_arg_info</span>->name_len = varname-><span style="color: #000000;">u.constant.value.str.len;cur_arg_info</span>->array_type_hint = <span style="color: #800080;">0</span><span style="color: #000000;">;cur_arg_info</span>->allow_null = <span style="color: #800080;">1</span><span style="color: #000000;">;cur_arg_info</span>->pass_by_reference =<span style="color: #000000;"> pass_by_reference;cur_arg_info</span>->class_name =<span style="color: #000000;"> NULL;cur_arg_info</span>->class_name_len = <span style="color: #800080;">0</span>;

パラメーター値の受け渡しとパラメーターの受け渡しの違いは、次の場合に pass_by_reference パラメーターによって実現されます。中間コードを生成しています。
typedef <span style="color: #0000ff;">struct</span><span style="color: #000000;"> _zend_arg_info {    </span><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> *name;   <span style="color: #008000;">/*</span><span style="color: #008000;">参数的名称</span><span style="color: #008000;">*/</span><span style="color: #000000;">    zend_uint name_len;     </span><span style="color: #008000;">/*</span><span style="color: #008000;">参数名称的长度</span><span style="color: #008000;">*/</span>    <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> *class_name; <span style="color: #008000;">/*</span><span style="color: #008000;"> 类名</span><span style="color: #008000;">*/</span><span style="color: #000000;">     zend_uint class_name_len;   </span><span style="color: #008000;">/*</span><span style="color: #008000;">类名长度</span><span style="color: #008000;">*/</span><span style="color: #000000;">    zend_bool array_type_hint;  </span><span style="color: #008000;">/*</span><span style="color: #008000;">数组类型提示</span><span style="color: #008000;">*/</span><span style="color: #000000;">    zend_bool allow_null;   </span><span style="color: #008000;">/*</span><span style="color: #008000;">是否允许为NULL?</span><span style="color: #008000;">*/</span><span style="color: #000000;">    zend_bool pass_by_reference;    </span><span style="color: #008000;">/*</span><span style="color: #008000;">是否引用传递</span><span style="color: #008000;">*/</span><span style="color: #000000;">    zend_bool return_reference;     </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> required_num_args;  } zend_arg_info;</span>
中間コードに含まれる arg_nums フィールドは、**zend_do_receive_argxx が実行されるたびに 1 ずつ増加します。

現在のパラメータのインデックスは?CG(active_op_array)->num_args-1 です。次のコード:
CG(active_op_array)->num_args++;

上記の分析は、次の場合のパラメータ設定です。関数が定義されています。これらのパラメータは固定です。実際にプログラムを書く際には、変数パラメータを使用することがあります。このとき、関数 func_num_args と func_get_args を使用します。これらは内部関数として存在します。したがって、Zendzend_builtin_functions.c ファイルでこれら 2 つの関数の実装を見つけてください。まず func_num_args 関数の実装を見てみましょう。コードは次のとおりです。
cur_arg_info = &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-<span style="color: #800080;">1</span>];

ex->function_state.arguments が存在し、関数が呼び出されるとき、ex- > ; function_state.arguments の変換された値が返されます。それ以外の場合は、エラーが表示され、-1 が返されます。ここで最も重要なポイントは EG (current_execute_data) です。この変数には、現在実行中のプログラムまたは関数のデータが格納されています。なぜなら、この関数の呼び出しは関数に入った後に実行されるからです。関数の関連データはすべて前の実行プロセスにあるため、呼び出しは次のようになります:
<span style="color: #008000;">/*</span><span style="color: #008000;"> {{{ proto int func_num_args(void)   Get the number of arguments that were passed to the function </span><span style="color: #008000;">*/</span><span style="color: #000000;">ZEND_FUNCTION(func_num_args){    zend_execute_data </span>*ex = EG(current_execute_data)-><span style="color: #000000;">prev_execute_data;     </span><span style="color: #0000ff;">if</span> (ex && ex-><span style="color: #000000;">function_state.arguments) {        RETURN_LONG((</span><span style="color: #0000ff;">long</span>)(zend_uintptr_t)*(ex-><span style="color: #000000;">function_state.arguments));    } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {        zend_error(E_WARNING,</span><span style="color: #800000;">"</span><span style="color: #800000;">func_num_args():  Called from the global scope - no function context</span><span style="color: #800000;">"</span><span style="color: #000000;">);        RETURN_LONG(</span>-<span style="color: #800080;">1</span><span style="color: #000000;">);    }}</span><span style="color: #008000;">/*</span><span style="color: #008000;"> }}} </span><span style="color: #008000;">*/</span>

zend_execute_data *ex = EG(current_execute_data)->prev_execute_data;

2. 内部関数のパラメーター

共通カウント関数を例にとると、パラメーター処理部分のコードは次のとおりです。

これには 2 つの操作が含まれます。個々のパラメータ番号を取得することです。1 つは解析パラメータ リストです。
<span style="color: #008000;">/*</span><span style="color: #008000;"> {{{ proto int count(mixed var [, int mode])   Count the number of elements in a variable (usually an array) </span><span style="color: #008000;">*/</span><span style="color: #000000;">PHP_FUNCTION(count){    zval </span>*<span style="color: #000000;">array;    </span><span style="color: #0000ff;">long</span> mode =<span style="color: #000000;"> COUNT_NORMAL;     </span><span style="color: #0000ff;">if</span> (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, <span style="color: #800000;">"</span><span style="color: #800000;">z|l</span><span style="color: #800000;">"</span><span style="color: #000000;">,         </span>&array, &mode) ==<span style="color: #000000;"> FAILURE) {        </span><span style="color: #0000ff;">return</span><span style="color: #000000;">;    }    ... </span><span style="color: #008000;">//</span><span style="color: #008000;">省略</span>}

  (1)取参数的个数

  取参数的个数是通过ZEND_NUM_ARGS()宏来实现的,其定义如下:

<span style="color: #0000ff;">#define</span> ZEND_NUM_ARGS()     (ht)

  ht是在Zend/zend.h文件中定义的宏INTERNAL_FUNCTION_PARAMETERS中的ht,如下

<span style="color: #0000ff;">#define</span> INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value,<span style="color: #000000;">zval </span>**return_value_ptr, zval *this_ptr, <span style="color: #0000ff;">int</span> return_value_used TSRMLS_DC

  (2)解析参数列表

  PHP内部函数在解析参数时使用的是zend_parse_parameters。它可以大大简化参数的接收处理工作,虽然它在处理可变参数时还有点弱。

  其声明如下:

ZEND_API <span style="color: #0000ff;">int</span> zend_parse_parameters(<span style="color: #0000ff;">int</span> num_args TSRMLS_DC, <span style="color: #0000ff;">char</span> *<span style="color: #000000;">type_spec, ...)</span>
  • 第一个参数num_args表明表示想要接收的参数个数,我们经常使用ZEND_NUM_ARGS()来表示对传入的参数“有多少要多少”
  • 第二个参数应该是宏TSRMLS_CC。
  • 第三个参数type_spec是一个字符串,用来指定我们所期待接收的各个参数的类型,有点类似于printf中指定输出格式的那个格式化字符串。
  • 剩下的参数就是我们用来接收PHP参数值的变量的指针。

  zend_parse_parameters()在解析参数的同时户尽可能的转换参数类型,这样就可以确保我们总是能得到所期望的类型的变量

 

  3、函数的返回值

  PHP中函数都有返回值,没return返回null

  (1)return语句

  从Zend/zend_language_parser.y文件中可以确认其生成中间代码调用的是zend_do_return函数。

<span style="color: #0000ff;">void</span> zend_do_return(znode *expr, <span style="color: #0000ff;">int</span> do_end_vparse TSRMLS_DC) <span style="color: #008000;">/*</span><span style="color: #008000;"> {{{ </span><span style="color: #008000;">*/</span><span style="color: #000000;">{    zend_op </span>*<span style="color: #000000;">opline;    </span><span style="color: #0000ff;">int</span><span style="color: #000000;"> start_op_number, end_op_number; </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (do_end_vparse) {        </span><span style="color: #0000ff;">if</span> (CG(active_op_array)-><span style="color: #000000;">return_reference                </span>&& !<span style="color: #000000;">zend_is_function_or_method_call(expr)) {            zend_do_end_variable_parse(expr, BP_VAR_W, </span><span style="color: #800080;">0</span> TSRMLS_CC);<span style="color: #008000;">/*</span><span style="color: #008000;"> 处理返回引用 </span><span style="color: #008000;">*/</span><span style="color: #000000;">        } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {            zend_do_end_variable_parse(expr, BP_VAR_R, </span><span style="color: #800080;">0</span> TSRMLS_CC);<span style="color: #008000;">/*</span><span style="color: #008000;"> 处理常规变量返回 </span><span style="color: #008000;">*/</span><span style="color: #000000;">        }    }    ...</span><span style="color: #008000;">//</span><span style="color: #008000;"> 省略,取其他中间代码操作</span><span style="color: #000000;">     opline</span>->opcode =<span style="color: #000000;"> ZEND_RETURN;     </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (expr) {        opline</span>->op1 = *<span style="color: #000000;">expr;         </span><span style="color: #0000ff;">if</span> (do_end_vparse &&<span style="color: #000000;"> zend_is_function_or_method_call(expr)) {            opline</span>->extended_value =<span style="color: #000000;"> ZEND_RETURNS_FUNCTION;        }    } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {        opline</span>->op1.op_type =<span style="color: #000000;"> IS_CONST;        INIT_ZVAL(opline</span>-><span style="color: #000000;">op1.u.constant);    }     SET_UNUSED(opline</span>-><span style="color: #000000;">op2);}</span><span style="color: #008000;">/*</span><span style="color: #008000;"> }}} </span><span style="color: #008000;">*/</span>

  生成中间代码为ZEND_RETURN。第一个操作数的类型在返回值为可用的表达式时,其类型为表达式的操作类型,否则类型为IS_CONST。这在后续计算执行中间代码函数时有用到。根据操作数的不同,ZEND_RETURN中间代码会执行ZEND_RETURN_SPEC_CONST_HANDLER,ZEND_RETURN_SPEC_TMP_HANDLER或ZEND_RETURN_SPEC_TMP_HANDLER。这三个函数的执行流程基本类似,包括对一些错误的处理。这里我们以ZEND_RETURN_SPEC_CONST_HANDLER为例说明函数返回值的执行过程:

<span style="color: #0000ff;">static</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> ZEND_FASTCALL  ZEND_RETURN_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS){    zend_op </span>*opline =<span style="color: #000000;"> EX(opline);    zval </span>*<span style="color: #000000;">retval_ptr;    zval </span>**<span style="color: #000000;">retval_ptr_ptr;      </span><span style="color: #0000ff;">if</span> (EG(active_op_array)->return_reference ==<span style="color: #000000;"> ZEND_RETURN_REF) {         </span><span style="color: #008000;">//</span><span style="color: #008000;">  ?ǔ?sÁ\[email protected]Á??</span>        <span style="color: #0000ff;">if</span> (IS_CONST == IS_CONST || IS_CONST ==<span style="color: #000000;"> IS_TMP_VAR) {               </span><span style="color: #008000;">/*</span><span style="color: #008000;"> Not supposed to happen, but we'll allow it </span><span style="color: #008000;">*/</span><span style="color: #000000;">            zend_error(E_NOTICE, </span><span style="color: #800000;">"</span><span style="color: #800000;">Only variable references \</span>                should be returned by reference<span style="color: #800000;">"</span><span style="color: #800000;">);</span>            <span style="color: #0000ff;">goto</span><span style="color: #000000;"> return_by_value;        }         retval_ptr_ptr </span>= NULL;  <span style="color: #008000;">//</span><span style="color: #008000;">  ?ǔ?</span>         <span style="color: #0000ff;">if</span> (IS_CONST == IS_VAR && !<span style="color: #000000;">retval_ptr_ptr) {            zend_error_noreturn(E_ERROR, </span><span style="color: #800000;">"</span><span style="color: #800000;">Cannot return string offsets by </span>reference<span style="color: #800000;">"</span><span style="color: #800000;">);</span><span style="color: #000000;">        } </span><span style="color: #0000ff;">if</span> (IS_CONST == IS_VAR && !<span style="color: #000000;">Z_ISREF_PP(retval_ptr_ptr)) {            </span><span style="color: #0000ff;">if</span> (opline->extended_value == ZEND_RETURNS_FUNCTION &&<span style="color: #000000;">                EX_T(opline</span>->op1.u.<span style="color: #0000ff;">var</span>).<span style="color: #0000ff;">var</span><span style="color: #000000;">.fcall_returned_reference) {            } </span><span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (EX_T(opline->op1.u.<span style="color: #0000ff;">var</span>).<span style="color: #0000ff;">var</span>.ptr_ptr ==                    &EX_T(opline->op1.u.<span style="color: #0000ff;">var</span>).<span style="color: #0000ff;">var</span><span style="color: #000000;">.ptr) {                </span><span style="color: #0000ff;">if</span> (IS_CONST == IS_VAR && !<span style="color: #800080;">0</span><span style="color: #000000;">) {                      </span><span style="color: #008000;">/*</span><span style="color: #008000;"> undo the effect of get_zval_ptr_ptr() </span><span style="color: #008000;">*/</span><span style="color: #000000;">                    PZVAL_LOCK(</span>*<span style="color: #000000;">retval_ptr_ptr);                }                zend_error(E_NOTICE, </span><span style="color: #800000;">"</span><span style="color: #800000;">Only variable references \</span>                 should be returned by reference<span style="color: #800000;">"</span><span style="color: #800000;">);</span>                <span style="color: #0000ff;">goto</span><span style="color: #000000;"> return_by_value;            }        }         </span><span style="color: #0000ff;">if</span> (EG(return_value_ptr_ptr)) { <span style="color: #008000;">//</span><span style="color: #008000;">  ?ǔ?s</span>            SEPARATE_ZVAL_TO_MAKE_IS_REF(retval_ptr_ptr);   <span style="color: #008000;">//</span><span style="color: #008000;">  is_ref__gc???</span><span style="color: #800080;">1</span><span style="color: #000000;">            Z_ADDREF_PP(retval_ptr_ptr);    </span><span style="color: #008000;">//</span><span style="color: #008000;">  refcount__gcŒ?×1</span><span style="color: #000000;">             (</span>*EG(return_value_ptr_ptr)) = (*<span style="color: #000000;">retval_ptr_ptr);        }    } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {return_by_value:         retval_ptr </span>= &opline-><span style="color: #000000;">op1.u.constant;         </span><span style="color: #0000ff;">if</span> (!<span style="color: #000000;">EG(return_value_ptr_ptr)) {            </span><span style="color: #0000ff;">if</span> (IS_CONST ==<span style="color: #000000;"> IS_TMP_VAR) {             }        } </span><span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (!<span style="color: #800080;">0</span>) { <span style="color: #008000;">/*</span><span style="color: #008000;"> Not a temp var </span><span style="color: #008000;">*/</span>            <span style="color: #0000ff;">if</span> (IS_CONST == IS_CONST ||<span style="color: #000000;">                EG(active_op_array)</span>->return_reference == ZEND_RETURN_REF ||<span style="color: #000000;">                (PZVAL_IS_REF(retval_ptr) </span>&& Z_REFCOUNT_P(retval_ptr) > <span style="color: #800080;">0</span><span style="color: #000000;">)) {                zval </span>*<span style="color: #000000;">ret;                 ALLOC_ZVAL(ret);                INIT_PZVAL_COPY(ret, retval_ptr);   </span><span style="color: #008000;">//</span><span style="color: #008000;">  ?????ǔ? </span><span style="color: #000000;">                zval_copy_ctor(ret);                </span>*EG(return_value_ptr_ptr) =<span style="color: #000000;"> ret;            } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {                </span>*EG(return_value_ptr_ptr) = retval_ptr; <span style="color: #008000;">//</span><span style="color: #008000;">  ?6??</span><span style="color: #000000;">                Z_ADDREF_P(retval_ptr);            }        } </span><span style="color: #0000ff;">else</span><span style="color: #000000;"> {            zval </span>*<span style="color: #000000;">ret;             ALLOC_ZVAL(ret);            INIT_PZVAL_COPY(ret, retval_ptr);    </span><span style="color: #008000;">//</span><span style="color: #008000;">  ?????ǔ? </span>            *EG(return_value_ptr_ptr) =<span style="color: #000000;"> ret;            }    }     </span><span style="color: #0000ff;">return</span> zend_leave_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);   <span style="color: #008000;">//</span><span style="color: #008000;">  ?</span><span style="color: #000000;">ǔ????}</span>

  函数的返回值在程序执行时存储在*EG(return_value_ptr_ptr)。ZEND内核对值返回和引用返回作了区别,并且在此基础上对常量,临时变量和其他类型的变量在返回时作了不同的处理。在return执行完之后,ZEND内核通过调用zend_leave_helper_SPEC函数,清除函数内部使用的变量等。这也是ZEND内核自动给函数加上NULL返回的原因之一。

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