PHP的一个坑--in_array

今天在找问题的时候发现了一个小坑。
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内部实现是一样的。
PHP的一个坑--in_array_第1张图片
其中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。

之后的几个可以自己看源码分析一下。

你可能感兴趣的:(PHP)