首頁 後端開發 php教程 深入懂得PHP内核(六)函数的定义、传参及返回值

深入懂得PHP内核(六)函数的定义、传参及返回值

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

深入理解PHP内核(六)函数的定义、传参及返回值

一、函数的定义

  用户函数的定义从function 关键字开始,如下

<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></st_in_scripting>
登入後複製

  它所表示的含义是function将会生成T_FUNCTION标记。在获取这个标记后,我们开始语法分析。

  2、语法分析

  在Zend/zend_language_parser.y文件中找到函数的声明过程标记如下:

<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>
登入後複製

    关注点在function is_reference T_STRING,表示function关键字,是否引用,函数名

  T_FUNCTION标记只是用来定位函数的声明,表示这是一个函数,而更多的工作是与这个函数相关的东西,包括参数,返回值。

  3、生成中间代码

  语法解析后,我们看到所执行编译函数为zend_do_begin_function_declaration。在Zend/zend_complie.c文件找到其实现如下:

<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>
登入後複製

  生成的代码为ZEND_DECLARE_FUNCTION,根据这个中间的代码及操作数对应的op_type。我们可以找到中间代码的执行函数为ZEND_DECLARE_FUNCTION_SPEC_HANDLER。

    在生成中间代码的时候,可以看到已经统一了函数名全部为小写,表示函数的名称不是区  分大小写的。

  为验证这个实现,我们看一段代码

<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>
登入後複製

  执行代码会报错Fatal error: Cannot redeclare t() (previously declared in ...)

  表示对于PHP来说T和t是同一个函数名,校验函数名是否重复,这个过程是在哪进行的呢?

  4、执行中间代码

  在Zend/zend_vm_execute.h文件中找到ZEND_DECLARE_FUNCTION中间代码对应的执行函数:ZEND_DECLARE_FUNCTION_SPEC_HANDLER。此函数只调用了函数do_bind_function。其调用代码为:

do_bind_function(EX(opline), EG(function_table), <span style="color: #800080;">0</span>);
登入後複製

  在这个函数中将EX(opline)所指向的函数添加到EG(function_table)中,并判断是否已经存在相同名字的函数,如果存在则报错,EG(function_table)用来存放执行过程中全部的函数信息,相当于函数的注册表。它的结构是一个HashTable,所以在do_bind_function函数中添加新的函数使用的是HashTable的操作函数zend_hash_add

 

二、函数的参数

  函数的定义只是一个将函数名注册到函数列表的过程。

  1、用户自定义函数的参数

  我们知道对于函数的参数检查是通过zend_do_receive_arg函数来实现的,在此函数中对于参数的关键代码如下:

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>;
登入後複製

  整个参数的传递是通过给中间代码的arg_info字段执行赋值操作完成。关键点是在arg_info字段,arg_info字段的结构如下:

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>
登入後複製

  参数的值传递和参数传递的区别是通过pass_by_reference参数在生成中间代码时实现的。

  对于参数的个数,中间代码中包含的arg_nums字段在每次执行**zend_do_receive_argxx时都会加1.如下代码:

CG(active_op_array)->num_args++;
登入後複製

  并且当前参数的索引为?CG(active_op_array)->num_args-1.如下代码:

cur_arg_info = &CG(active_op_array)->arg_info[CG(active_op_array)->num_args-<span style="color: #800080;">1</span>];
登入後複製

  以上的分析是针对函数定义时的参数设置,这些参数是固定的。而在实际编写程序时可能我们会用到可变参数。此时我们会用到函数func_num_args和func_get_args。它们是以内部函数存在。于是在Zend\zend_builtin_functions.c文件中找到这两个函数的实现。我们首先来看func_num_args函数的实现,其代码如下:

<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>
登入後複製

  在存在ex->function_state.arguments的情况下,及函数调用时,返回ex->function_state.arguments转化后的值,否则显示错误并返回-1。这里最关键的一点是EG(current_execute_data)。这个变量存放的是当前执行程序或函数的数据,此时我们需要取前一个执行程序的数据,为什么呢?因为这个函数的调用是在进入函数后执行的。函数的相关数据等都在之前执行过程中,于是调用的是:

zend_execute_data *ex = EG(current_execute_data)->prev_execute_data;
登入後複製

 

  2、内部函数的参数

  以常见的count函数为例,其参数处理部分的代码如下:

<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 Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1665
14
CakePHP 教程
1424
52
Laravel 教程
1321
25
PHP教程
1269
29
C# 教程
1249
24
C語言return的用法詳解 C語言return的用法詳解 Oct 07, 2023 am 10:58 AM

C語言return的用法有:1、對於傳回值類型為void的函數,可以使用return語句來提前結束函數的執行;2、對於傳回值型別不為void的函數,return語句的作用是將函數的執行結果傳回給呼叫者;3、提前結束函數的執行,在函數內部,我們可以使用return語句來提前結束函數的執行,即使函數並沒有回傳值。

function是什麼意思 function是什麼意思 Aug 04, 2023 am 10:33 AM

function是函數的意思,是一段具有特定功能的可重複使用的程式碼區塊,是程式的基本組成單元之一,可以接受輸入參數,執行特定的操作,並傳回結果,其目的是封裝一段可重複使用的程式碼,提高程式碼的可重複使用性和可維護性。

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;}}#輸出上述程式碼的輸出可以簡單地得出結論:return在finally之前執行,我們來看下字節碼層面上發生了什麼事情。下面截取case1方法的部分字節碼,並且對照源碼,將每個指令的含義註釋在

PHP實作框架:Zend Framework入門教程 PHP實作框架:Zend Framework入門教程 Jun 19, 2023 am 08:09 AM

PHP實作框架:ZendFramework入門教學ZendFramework是PHP開發的開源網站框架,目前由ZendTechnologies維護,ZendFramework採用了MVC設計模式,提供了一系列可重複使用的程式碼庫,服務於實作Web2.0應用程式和Web服務。 ZendFramework深受PHP開發者的歡迎與推崇,擁有廣泛

如何在Zend框架中使用ACL(Access Control List)進行權限控制 如何在Zend框架中使用ACL(Access Control List)進行權限控制 Jul 29, 2023 am 09:24 AM

如何在Zend框架中使用ACL(AccessControlList)進行權限控制導言:在一個Web應用程式中,權限控制是至關重要的功能。它可以確保使用者只能存取其有權存取的頁面和功能,並防止未經授權的存取。 Zend框架提供了一種方便的方法來實現權限控制,即使用ACL(AccessControlList)元件。本文將介紹如何在Zend框架中使用ACL

'enumerate()'函數在Python中的用途是什麼? 'enumerate()'函數在Python中的用途是什麼? Sep 01, 2023 am 11:29 AM

在本文中,我們將了解enumerate()函數以及Python中「enumerate()」函數的用途。什麼是enumerate()函數? Python的enumerate()函數接受資料集合作為參數並傳回一個枚舉物件。枚舉物件以鍵值對的形式傳回。 key是每個item對應的索引,value是items。語法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怎麼使用setup語法糖拒絕寫return Vue3怎麼使用setup語法糖拒絕寫return May 12, 2023 pm 06:34 PM

Vue3.2setup語法糖是在單文件組件(SFC)中使用組合式API的編譯時語法糖解決Vue3.0中setup需要繁瑣將聲明的變量、函數以及import引入的內容通過return向外暴露,才能在使用的問題1.在使用中無需return宣告的變數、函數以及import引入的內容,即可在使用語法糖//import引入的內容import{getToday}from'./utils'//變數constmsg='Hello !'//函數func

See all articles