欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

PHP empty和isset源码分析

程序员文章站 2022-06-14 08:21:47
...
被问到php的empty(0)、empty(0.0)、empty('0')、empty('0.0')和empty('00')分别返回什么,对于正常的empty()手册里描述自己也是非常清楚了:

""、0、"0"、NULL、FALSE、array()、var $var; 以及没有任何属性的对象都将被认为是空的,如果 var 为空,则返回 TRUE。

但就在empty('00')的时候疑惑了一下,因为在我的理解体系里,empty()把'0'转换成数字判断了,所以会返回true,那'00'也会转换吗?在没有实践的情况下,当时就机智的自己思考了一下觉得'00'返回是false,理由是'00'不会转换成数字,又是非空字符串,所以为false。

因为不确定,搜了一下google也没人研究这个简单的问题,只能自己扒了。先写了一个php

发现opcode:

2 0 E > ISSET_ISEMPTY_VAR 293601280 RES[ IS_TMP_VAR ~0 ] OP1[ IS_CV !0 ] OP2[ IS_UNUSED ]

1 FREE OP1[ IS_TMP_VAR ~0 ]

3 2 ISSET_ISEMPTY_VAR 310378496 RES[ IS_TMP_VAR ~1 ] OP1[ IS_CV !0 ] OP2[ IS_UNUSED ]

3 FREE OP1[ IS_TMP_VAR ~1 ]

4 4 > RETURN OP1[ IS_CONST (128414056) 1 ]

均调用了:ISSET_ISEMPTY_VAR , 然后去zend源码顺藤摸瓜的找到这个op执行的是ZEND_ISSET_ISEMPTY_VAR(zend_vm_opcodes.c),

然后找到ZEND_ISSET_ISEMPTY_VAR实际到execute的时候执行的是ZEND_ISSET_ISEMPTY_VAR_xxx(zend_vm_execute.h)等一系列函数,通过源码阅读发现判断变量是否为空的最终执行函数是:i_zend_is_true(),

最终找到定义的地方:zend_execute.h:static zend_always_inline int i_zend_is_true(zval *op);

具体代码如下:

static zend_always_inline int i_zend_is_true(zval *op){        int result;           switch (Z_TYPE_P(op)) {                case IS_NULL:                        result = 0;                        break;                case IS_LONG:                case IS_BOOL:                case IS_RESOURCE:                        result = (Z_LVAL_P(op)?1:0);                        break;                case IS_DOUBLE:                        result = (Z_DVAL_P(op) ? 1 : 0);                        break;                case IS_STRING:                        if (Z_STRLEN_P(op) == 0                                || (Z_STRLEN_P(op)==1 && Z_STRVAL_P(op)[0]=='0')) {                                result = 0;                        } else {                                result = 1;                        }                        break;                case IS_ARRAY:                        result = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0);                        break;                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);                                                break;                                        }                                } 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) {                                                /* for safety - avoid loop */                                                convert_to_boolean(tmp);                                                result = Z_LVAL_P(tmp);                                                zval_ptr_dtor(&tmp);                                                break;                                        }                                }                        }                        result = 1;                        break;                default:                        result = 0;                        break;        }        return result;}

很简单的代码,这下清楚了,empty()对检测值没有进行任何转换,只是在检测string的时候多了一个“补丁”操作(我理解的):

empty()在判断字符串的时候首先判断长度是否为0,是则返回0,否则判断是否长度为1且该字符为'0',是则返回0;其他情况返回1;

至于isset(),在判断出变量未赋值时就返回了,也没执行到i_zend_is_true()函数。