Heim >Backend-Entwicklung >PHP-Tutorial >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()函数。