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

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

Jun 13, 2016 pm 12:28 PM
function return zend

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 までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

C言語のreturnの使い方を詳しく解説 C言語のreturnの使い方を詳しく解説 Oct 07, 2023 am 10:58 AM

C 言語における return の使い方は、 1. 戻り値の型が void の関数については、return 文を使用して関数の実行を早期に終了することができます; 2. 戻り値の型が void ではない関数については、 return ステートメントは、関数の実行を終了するためのものです。結果は呼び出し元に返されます。 3. 関数の実行を早期に終了します。関数内で return ステートメントを使用して、関数の実行を早期に終了することもできます。関数が値を返さない場合。

機能とはどういう意味ですか? 機能とはどういう意味ですか? Aug 04, 2023 am 10:33 AM

ファンクションとは、関数を意味します。これは、特定の関数を備えた再利用可能なコード ブロックです。プログラムの基本コンポーネントの 1 つです。入力パラメータを受け取り、特定の操作を実行し、結果を返すことができます。その目的は、再利用可能なコード ブロックをカプセル化することです。コードの再利用性と保守性を向上させるコード。

Javaのreturn文とfinally文の実行順序は何ですか? Javaのreturn文とfinally文の実行順序は何ですか? Apr 25, 2023 pm 07:55 PM

ソースコード: publicclassReturnFinallyDemo{publicstaticvoidmain(String[]args){System.out.println(case1());}publicstaticintcase1(){intx;try{x=1;returnx;}finally{x=3;}}}#出力 上記のコードの出力は、単純に次のように結論付けることができます:finally の前に return が実行されます。バイトコード レベルで何が起こるかを見てみましょう。以下は、case1 メソッドのバイトコードの一部をインターセプトし、ソース コードを比較して、各命令の意味に注釈を付けます。

Zend Framework でのアクセス許可制御に ACL (アクセス コントロール リスト) を使用する方法 Zend Framework でのアクセス許可制御に ACL (アクセス コントロール リスト) を使用する方法 Jul 29, 2023 am 09:24 AM

Zend Framework でのアクセス許可制御に ACL (AccessControlList) を使用する方法 はじめに: Web アプリケーションでは、アクセス許可制御は重要な機能です。これにより、ユーザーはアクセスを許可されたページと機能にのみアクセスできるようになり、不正アクセスが防止されます。 Zend フレームワークは、ACL (AccessControlList) コンポーネントを使用してアクセス許可制御を実装する便利な方法を提供します。この記事では、Zend Framework で ACL を使用する方法を紹介します。

PHP 実装フレームワーク: Zend Framework 入門チュートリアル PHP 実装フレームワーク: Zend Framework 入門チュートリアル Jun 19, 2023 am 08:09 AM

PHP 実装フレームワーク: ZendFramework 入門チュートリアル ZendFramework は、PHP によって開発されたオープン ソースの Web サイト フレームワークであり、現在 ZendTechnologies によって保守されています。ZendFramework は、MVC デザイン パターンを採用し、Web2.0 アプリケーションと Web サーブの実装に役立つ一連の再利用可能なコード ライブラリを提供します。 。 ZendFramework は PHP 開発者に非常に人気があり、尊敬されており、幅広い機能を備えています。

Python の「enumerate()」関数の目的は何ですか? Python の「enumerate()」関数の目的は何ですか? Sep 01, 2023 am 11:29 AM

この記事では、Python の enumerate() 関数と「enumerate()」関数の目的について学びます。 enumerate() 関数とは何ですか? Python の enumerate() 関数は、データ コレクションをパラメータとして受け取り、列挙オブジェクトを返します。列挙オブジェクトはキーと値のペアとして返されます。キーは各項目に対応するインデックス、値は項目です。構文 enumerate(iterable,start) パラメータ iterable - 渡されたデータ コレクションは、iterablestart と呼ばれる列挙オブジェクトとして返すことができます。 - 名前が示すように、列挙オブジェクトの開始インデックスは start によって定義されます。無視したら

MySQL.procテーブルの役割と機能の詳しい説明 MySQL.procテーブルの役割と機能の詳しい説明 Mar 16, 2024 am 09:03 AM

MySQL.proc テーブルの役割と機能の詳細な説明。MySQL は人気のあるリレーショナル データベース管理システムです。開発者が MySQL を使用する場合、多くの場合、ストアド プロシージャ (StoredProcedure) の作成と管理が必要になります。 MySQL.proc テーブルは非常に重要なシステム テーブルであり、ストアド プロシージャの名前、定義、パラメータなど、データベース内のすべてのストアド プロシージャに関連する情報が保存されます。この記事では、MySQL.proc テーブルの役割と機能について詳しく説明します。

Vue3 はセットアップ構文シュガーをどのように使用して return の書き込みを拒否しますか Vue3 はセットアップ構文シュガーをどのように使用して return の書き込みを拒否しますか May 12, 2023 pm 06:34 PM

Vue3.2 セットアップ構文シュガーは、単一ファイル コンポーネント (SFC) で結合された API を使用して、Vue3.0 の面倒なセットアップを解決するコンパイル時構文シュガーです。宣言された変数、関数、インポートによって導入されたコンテンツは、インポートによって公開されます。使用上の問題点 1. 宣言した変数、関数、import で導入した内容を使用中に return する必要はなく、糖衣構文を使用することができます。 // 導入した内容をインポート import{getToday }from'./utils'//variable constmsg='Hello !'//function func

See all articles