热搜:NVER node 开发 php

PHP empty和isset源码分析

2024-07-22 19:00:01
PHP empty和isset源码分析

被问到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

<?phpempty($a);isset($a);

发现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()函数。