最近有人问我在PHP中使用空函数和isset函数时如何判断变量。起初,我很困惑,因为我只知道一点点。为了了解它的真实原理,我迅速打开源代码进行研究。经过分析,可以发现两个函数调用的是同一个函数,所以本文将这两个函数放在一起分析。
我对github中的PHP源代码有更详细的评论。感兴趣的人可以观看并赠送一颗星星。评论PHP5.4源代码。您可以通过提交记录查看添加的注释。
函数使用格式。
空的
Booempty(混合$ var)确定变量是否为空。
变量名
Booliset(混合$ var [,混合$.])判断变量是否设置,是否为空。
参数描述
对于empty,在PHP 5.5版本之前,empty只支持变量参数,其他类型的参数会导致解析错误,比如函数调用的结果不能作为参数。
对于isset,如果变量被函数(如unset)设置为空,该函数将返回false。如果将多个参数传递给isset函数,则isset函数只有在所有参数都已设置的情况下才会返回true。从左到右计算,一旦遇到尚未设置的变量,就会停止。
运行示例
$result=空(0);//true$result=空(null);//true$result=空(false);//true$result=空(array());//true$result=空(' 0 ');//true$result=空(1);//false$result=空(回调函数);//错误$ a=null$ result=isset($ a);//false;$ a=1;$ result=isset($ a);//true;$ a=1;$ b=2;$ c=3;$result=isset($a,$b,$ c);//true $ a=1;$ b=null$ c=3;$result=isset($a,$b,$ c);//false查找函数的定义位置。
其实空不是一个函数,而是一种语言结构。语言结构是在PHP程序运行之前编译的,所以不能像以前一样简单地搜索‘PHP _ FUNCTION empty’或‘ZEND _ FUNCTION empty’来查看其源代码。要看到空的和其他语言结构的源代码,首先要了解PHP代码执行的机制。
PHP执行代码有四个步骤,流程图如下:
在第一阶段,即Scanning阶段,程序扫描zend_language_scanner.l文件,将代码文件转换为语言片段。对于isset和empty函数,在zend_language_scanner.l文件中搜索empty和isset可以得到该文件中函数的宏定义如下:
ST _ IN _ SCRIPTING ' ISSET ' { return T _ ISSET;} ST _ IN _ SCRIPTING 'empty ' {返回T _ EMPTY}然后,进入解析阶段。在这个阶段,程序将T_ISSET和T_EMPTY等Tokens转换为有意义的表达式。这时,它会做语法分析。Tokens的yacc保存在zend_language_parser.y文件中,可以找到T_ISSET和T_EMPTY的定义。
internal _ functions _ in _ yacc :t _ ISSET '(' ISSET _ variables ')' { $ $=$ 3;}| T_EMPTY '('变量')' { ZEND _ do _ isset _ or _ ISEMPTY(ZEND _ ISEMPTY,$$,$ 3 TSRMLS _ CC);} | T _ INCLUDE expr { ZEND _ do _ INCLUDE _ or _ eval(ZEND _ INCLUDE,$$,2美元TSRMLS _ CC);} | T _ INCLUDE _ ONCE expr { ZEND _ do _ INCLUDE _ or _ eval(ZEND _ INCLUDE _ ONCE,$$,$ 2 TSRMLS _ CC);} | T _ EVAL '(' expr ')' { ZEND _ do _ include _ or _ eval(ZEND _ EVAL,$$,3美元TSRMLS _ CC);} | T _ REQUIRE expr { ZEND _ do _ include _ or _ eval(ZEND _ REQUIRE,$$,2美元TSRMLS _ CC);} | T _ REQUIRE _ ONCE expr { ZEND _ do _ include _ or _ eval(ZEND _ REQUIRE _ ONCE,$$,2美元TSRMLS _ CC);};Isset和empty函数最终执行zend_do_isset_or_isempty函数,并继续搜索。
grep-rn ' Zend _ do _ isset _ or _ isempty '
可以发现这个函数是在zend_compile.c文件中定义的。
功能执行步骤
1.分析参数。
2.检查它是否是可写变量。
3.如果变量的op_type是IS_CV(编译时的变量),将其操作码设置为Zend _ isset _ isempty _ var否则,从active_op_array获取下一个op值,并根据其op值设置last_op的操作码。
4.设置操作码后,将交给zend _ excute执行。
源代码解释
IS_CV是编译器使用的缓存机制,它保存被引用变量的地址。一个变量第一次被引用的时候会是CV,以后不需要再去查活动符号表来引用这个变量。
关于空函数,在操作码的步骤之后,参考操作码处理程序,我们可以知道isset和empty执行了ZEND_ISSET_ISEMPTY_VAR等一系列函数。以Zend _ isset _ isempty _ var _ spec _ cv _ var _ handler为例,我们在Zend _ VM _ execute中找到了这个函数的定义。查看函数,我们可以知道空函数的最终执行函数是i_zend_is_true(),i_zend_is_true函数是在zend_execute.h中定义的.i_zend_is_true函数的核心代码如下:
switch(Z _ TYPE _ P(op)){ case IS _ nulls :结果=0;打破;case is _ long : case is _ bool : case is _ resource ://如果参数为整数且不为0,则空参数为false。结果=(z _ lval _ p (op)?1:0);打破;案例IS_DOUBLE:结果=(Z_DVAL_P(op)?1 : 0);打破;case IS _ string : if(Z _ STRLEN _ P(op)==0 | |(Z _ STRLEN _ P(op)==1 Z _ STRVAL _ P(op)[0]==' 0 '){//空(' 0')==真结果=0;} else { result=1;} breakIS_ARRAY: //空(数组)的情况下,根据结果=(Zend _ hash _ num _ elements(z _ arrval _ p(op))的数组数来判断?1:0);打破;case IS _ OBJECT : if(IS _ ZEND _ STD _ OBJECT(* op)){ TSRMLS _ FETCH();if(Z _ OBJ _ HT _ P(op)-cast _ object){ zval tmp;if(Z _ OBJ _ HT _ P(op)-cast _ object(op,tmp,IS _ BOOL TSRMLS _ CC)==SUCCESS){ result=Z _ LVAL(tmp);打破;} } else if(Z _ OBJ _ HT _ P(op)-get){ zval * tmp=Z _ OBJ _ HT _ P(op)-get(op TSRMLS _ CC);if(Z_TYPE_P(tmp)!=IS_OBJECT) { /*为安全起见-避免循环*/convert _ to _ boolean(tmp);结果=Z _ LVAL _ P(tmp);zval _ ptr _ dtor(tmp);打破;} } }结果=1;打破;default:结果=0;打破;}这个代码比较直观,函数对检测值不做任何转换。通过这段代码,进一步分析了示例中的空函数:empty(null),to IS_NULL分支,result=0,i_zend_is_true()==0,I_zend_is_true()==1,所以返回true。
空(假),到IS_BOOL分支,结果=ZLVAL_P(假)=0,i_zend_is_true()==0,I_zend_is_true()==1,所以返回true。
空(array()),到IS_ARRAY分支,结果=Zend _ hash _ num _ elements(z _ arrival _ p(op))?1 : 0),zend_hash_num_elements返回数组元素个数,数组为空,所以结果为0,i_zend_is_true()==0!I_zend_is_true()==1,所以返回true。
空(' 0 '),到IS_STRING分支,因为Z_STRLENP(op)==1和Z_STRVAL_P(op)[0]=='0 ',结果为0,i_zend_is_true()==0!I_zend_is_true()==1,所以返回true。
空(1),到IS_LONG分支,结果=Z_LVAL_P(op)=1,i_zend_is_true==1!I_zend_is_true()==0,因此返回false。
对于isset函数,最终的判断代码是:
if (isset Z_TYPE_PP)(值)!=IS _ NULL){ ZVAL _ BOOL(EX _ T(opline-result . var)。tmp_var,1);} else { ZVAL _ BOOL(EX _ T)(op line-result . var)。tmp_var,0);}只要设置了值而不是NULL,isset函数就会返回true。
总结
在阅读了这两个函数的源代码后,我了解到:
1.编译过程中PHP代码的执行步骤。
2.如何找到PHP语言结构的源代码位置?
3.如何找到操作码处理程序的具体功能?
学无止境,每个人都有自己的缺点。只有通过不断的学习,他们才能弥补自己的不足。
原创文章写作受限,学习稀疏。如果文中有任何不规范之处,请告知。
如果这篇文章对你有帮助,请点击推荐,谢谢_。
以上对PHP中的empty和isset函数的深入了解,是边肖与大家分享的全部内容,希望能给大家一个参考和支持。