深入理解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返回的原因之一。

PHP是一種服務器端腳本語言,用於動態網頁開發和服務器端應用程序。 1.PHP是一種解釋型語言,無需編譯,適合快速開發。 2.PHP代碼嵌入HTML中,易於網頁開發。 3.PHP處理服務器端邏輯,生成HTML輸出,支持用戶交互和數據處理。 4.PHP可與數據庫交互,處理表單提交,執行服務器端任務。

PHP在過去幾十年中塑造了網絡,並將繼續在Web開發中扮演重要角色。 1)PHP起源於1994年,因其易用性和與MySQL的無縫集成成為開發者首選。 2)其核心功能包括生成動態內容和與數據庫的集成,使得網站能夠實時更新和個性化展示。 3)PHP的廣泛應用和生態系統推動了其長期影響,但也面臨版本更新和安全性挑戰。 4)近年來的性能改進,如PHP7的發布,使其能與現代語言競爭。 5)未來,PHP需應對容器化、微服務等新挑戰,但其靈活性和活躍社區使其具備適應能力。

PHP的核心優勢包括易於學習、強大的web開發支持、豐富的庫和框架、高性能和可擴展性、跨平台兼容性以及成本效益高。 1)易於學習和使用,適合初學者;2)與web服務器集成好,支持多種數據庫;3)擁有如Laravel等強大框架;4)通過優化可實現高性能;5)支持多種操作系統;6)開源,降低開發成本。

PHP沒有死。 1)PHP社區積極解決性能和安全問題,PHP7.x提升了性能。 2)PHP適合現代Web開發,廣泛用於大型網站。 3)PHP易學且服務器表現出色,但類型系統不如靜態語言嚴格。 4)PHP在內容管理和電商領域仍重要,生態系統不斷進化。 5)通過OPcache和APC等優化性能,使用OOP和設計模式提升代碼質量。

PHP和Python各有優劣,選擇取決於項目需求。 1)PHP適合Web開發,易學,社區資源豐富,但語法不夠現代,性能和安全性需注意。 2)Python適用於數據科學和機器學習,語法簡潔,易學,但執行速度和內存管理有瓶頸。

PHP用於構建動態網站,其核心功能包括:1.生成動態內容,通過與數據庫對接實時生成網頁;2.處理用戶交互和表單提交,驗證輸入並響應操作;3.管理會話和用戶認證,提供個性化體驗;4.優化性能和遵循最佳實踐,提升網站效率和安全性。

PHP在數據庫操作和服務器端邏輯處理中使用MySQLi和PDO擴展進行數據庫交互,並通過會話管理等功能處理服務器端邏輯。 1)使用MySQLi或PDO連接數據庫,執行SQL查詢。 2)通過會話管理等功能處理HTTP請求和用戶狀態。 3)使用事務確保數據庫操作的原子性。 4)防止SQL注入,使用異常處理和關閉連接來調試。 5)通過索引和緩存優化性能,編寫可讀性高的代碼並進行錯誤處理。

在PHP中使用預處理語句和PDO可以有效防範SQL注入攻擊。 1)使用PDO連接數據庫並設置錯誤模式。 2)通過prepare方法創建預處理語句,使用佔位符和execute方法傳遞數據。 3)處理查詢結果並確保代碼的安全性和性能。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

SublimeText3漢化版
中文版,非常好用

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),