今天在找问题的时候发现了一个小坑。
in_array定义:
in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) : bool
着重说一下第三个参数。
strict
如果第三个参数 strict 的值为 TRUE 则 in_array() 函数还会检查 needle 的类型是否和 haystack 中的相同。
看一组代码
$a = in_array(0, ['a', 'b']);
$b = in_array(null, ['a', 'b']);
$c = in_array(false, ['a', 'b']);
$d = in_array('', ['a', 'b']);
$a1 = in_array(0, ['a', 'b'],true);
$b1 = in_array(null, ['a', 'b'],true);
$c1 = in_array(false, ['a', 'b'],true);
$d1 = in_array('', ['a', 'b'],true);
$e = in_array(0, [0]);
$f = in_array(null, [0]);
$g = in_array(false, [0]);
$h = in_array('', [0]);
$e1 = in_array(0, [0],TRUE);
$f1 = in_array(null, [0],true);
$g1 = in_array(false, [0],true);
$h1 = in_array('', [0],true);
你们觉得输出都会输出什么。可以先自己判断一下。
a--true
b--false
c--false
d--false
a1--false
b1--false
c1--false
d1--false
e--true
f--true
g--true
h--true
e1--true
f1--false
g1--false
h1--false
注意改变$strict而导致返回结果不同的代码。
没有强调类型前 $strict = false | 强调类型后 $strict = true |
---|---|
$a = in_array(0, [‘a’, ‘b’]); | $a1 = in_array(0, [‘a’, ‘b’],true); |
$f = in_array(null, [0]); | $f1 = in_array(null, [0],true); |
$g = in_array(false, [0]); | $g1 = in_array(false, [0],true); |
$h = in_array(’’, [0]); | $h1 = in_array(’’, [0],true); |
true | false |
看一下PHP中in_array源码
用第一个分析。->$a = in_array(0, [‘a’, ‘b’]);
php-src/ext/standard/array.c
in_array和array_search内部实现是一样的。
其中INTERNAL_FUNCTION_PARAM_PASSTHRU
#define INTERNAL_FUNCTION_PARAM_PASSTHRU ht, return_value, return_value_ptr, this_ptr, return_value_used TSRMLS_CC
/* void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior)
* 0 = return boolean
* 1 = return key
*/
static inline void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) /* {{{ */
{
zval *value, /* value to check for */
*array, /* array to check in */
*entry; /* pointer to array entry */
zend_ulong num_idx;
zend_string *str_idx;
zend_bool strict = 0; /* strict comparison or not */
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_ZVAL(value)
Z_PARAM_ARRAY(array)
Z_PARAM_OPTIONAL
Z_PARAM_BOOL(strict)
ZEND_PARSE_PARAMETERS_END();
if (strict) {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
ZVAL_DEREF(entry);
if (fast_is_identical_function(value, entry)) {
if (behavior == 0) {
RETURN_TRUE;
} else {
if (str_idx) {
RETVAL_STR_COPY(str_idx);
} else {
RETVAL_LONG(num_idx);
}
return;
}
}
} ZEND_HASH_FOREACH_END();
} else {
if (Z_TYPE_P(value) == IS_LONG) {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
if (fast_equal_check_long(value, entry)) {
if (behavior == 0) {
RETURN_TRUE;
} else {
if (str_idx) {
RETVAL_STR_COPY(str_idx);
} else {
RETVAL_LONG(num_idx);
}
return;
}
}
} ZEND_HASH_FOREACH_END();
} else if (Z_TYPE_P(value) == IS_STRING) {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
if (fast_equal_check_string(value, entry)) {
if (behavior == 0) {
RETURN_TRUE;
} else {
if (str_idx) {
RETVAL_STR_COPY(str_idx);
} else {
RETVAL_LONG(num_idx);
}
return;
}
}
} ZEND_HASH_FOREACH_END();
} else {
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) {
if (fast_equal_check_function(value, entry)) {
if (behavior == 0) {
RETURN_TRUE;
} else {
if (str_idx) {
RETVAL_STR_COPY(str_idx);
} else {
RETVAL_LONG(num_idx);
}
return;
}
}
} ZEND_HASH_FOREACH_END();
}
}
RETURN_FALSE;
}
当$a = in_array(0, [‘a’, ‘b’]),进入fast_equal_check_long
static zend_always_inline int fast_equal_check_function(zval *op1, zval *op2)
{
zval result;
if (EXPECTED(Z_TYPE_P(op1) == IS_LONG)) {
if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
return Z_LVAL_P(op1) == Z_LVAL_P(op2);
} else if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
return ((double)Z_LVAL_P(op1)) == Z_DVAL_P(op2);
}
} else if (EXPECTED(Z_TYPE_P(op1) == IS_DOUBLE)) {
if (EXPECTED(Z_TYPE_P(op2) == IS_DOUBLE)) {
return Z_DVAL_P(op1) == Z_DVAL_P(op2);
} else if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
return Z_DVAL_P(op1) == ((double)Z_LVAL_P(op2));
}
} else if (EXPECTED(Z_TYPE_P(op1) == IS_STRING)) {
if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) {
return zend_fast_equal_strings(Z_STR_P(op1), Z_STR_P(op2));
}
}
compare_function(&result, op1, op2);
return Z_LVAL(result) == 0;
}
这里因为Z_TYPE_P(op2) != IS_LONG 会进入compare_function中。
ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) /* {{{ */
{
int ret;
int converted = 0;
zval op1_copy, op2_copy;
zval *op_free, tmp_free;
while (1) {
switch (TYPE_PAIR(Z_TYPE_P(op1), Z_TYPE_P(op2))) {
case TYPE_PAIR(IS_LONG, IS_LONG):
ZVAL_LONG(result, Z_LVAL_P(op1)>Z_LVAL_P(op2)?1:(Z_LVAL_P(op1)get) {
zval rv;
op_free = Z_OBJ_HT_P(op1)->get(Z_OBJ_P(op1), &rv);
ret = compare_function(result, op_free, op2);
zend_free_obj_get_result(op_free);
return ret;
} else if (Z_TYPE_P(op2) != IS_OBJECT && Z_OBJ_HT_P(op1)->cast_object) {
ZVAL_UNDEF(&tmp_free);
if (Z_OBJ_HT_P(op1)->cast_object(Z_OBJ_P(op1), &tmp_free, ((Z_TYPE_P(op2) == IS_FALSE || Z_TYPE_P(op2) == IS_TRUE) ? _IS_BOOL : Z_TYPE_P(op2))) == FAILURE) {
ZVAL_LONG(result, 1);
zend_free_obj_get_result(&tmp_free);
return SUCCESS;
}
ret = compare_function(result, &tmp_free, op2);
zend_free_obj_get_result(&tmp_free);
return ret;
}
}
if (Z_TYPE_P(op2) == IS_OBJECT) {
if (Z_OBJ_HT_P(op2)->get) {
zval rv;
op_free = Z_OBJ_HT_P(op2)->get(Z_OBJ_P(op2), &rv);
ret = compare_function(result, op1, op_free);
zend_free_obj_get_result(op_free);
return ret;
} else if (Z_TYPE_P(op1) != IS_OBJECT && Z_OBJ_HT_P(op2)->cast_object) {
ZVAL_UNDEF(&tmp_free);
if (Z_OBJ_HT_P(op2)->cast_object(Z_OBJ_P(op2), &tmp_free, ((Z_TYPE_P(op1) == IS_FALSE || Z_TYPE_P(op1) == IS_TRUE) ? _IS_BOOL : Z_TYPE_P(op1))) == FAILURE) {
ZVAL_LONG(result, -1);
zend_free_obj_get_result(&tmp_free);
return SUCCESS;
}
ret = compare_function(result, op1, &tmp_free);
zend_free_obj_get_result(&tmp_free);
return ret;
} else if (Z_TYPE_P(op1) == IS_OBJECT) {
ZVAL_LONG(result, 1);
return SUCCESS;
}
}
if (!converted) {
if (Z_TYPE_P(op1) < IS_TRUE) {
ZVAL_LONG(result, zval_is_true(op2) ? -1 : 0);
return SUCCESS;
} else if (Z_TYPE_P(op1) == IS_TRUE) {
ZVAL_LONG(result, zval_is_true(op2) ? 0 : 1);
return SUCCESS;
} else if (Z_TYPE_P(op2) < IS_TRUE) {
ZVAL_LONG(result, zval_is_true(op1) ? 1 : 0);
return SUCCESS;
} else if (Z_TYPE_P(op2) == IS_TRUE) {
ZVAL_LONG(result, zval_is_true(op1) ? 0 : -1);
return SUCCESS;
} else {
op1 = zendi_convert_scalar_to_number(op1, &op1_copy, result, 1);
op2 = zendi_convert_scalar_to_number(op2, &op2_copy, result, 1);
if (EG(exception)) {
if (result != op1) {
ZVAL_UNDEF(result);
}
return FAILURE;
}
converted = 1;
}
} else if (Z_TYPE_P(op1)==IS_ARRAY) {
ZVAL_LONG(result, 1);
return SUCCESS;
} else if (Z_TYPE_P(op2)==IS_ARRAY) {
ZVAL_LONG(result, -1);
return SUCCESS;
} else {
ZEND_ASSERT(0);
zend_throw_error(NULL, "Unsupported operand types");
if (result != op1) {
ZVAL_UNDEF(result);
}
return FAILURE;
}
}
}
}
ZVAL_LONG相当于把-1给result。
进入default
取op1=0,op2=a。
op1 < IS_TRUE,op2也不是true。result返回0。
那么在fast_equal_check_long中返回1。
再退出至php_search_array中。又因为(strict)behavior=0,所以返回true。所以$a = in_array(0, [‘a’, ‘b’]);返回true。
至于加了true之后的,php_search_array中behavior变为1,返回false。
之后的几个可以自己看源码分析一下。