先来看一段代码
<?php
$array = ["a", "b", "c"];
var_dump(in_array(0, $array));
这东西的输出是true, 虽然数组里并没有0
$array = ["a", "b", "c"];
var_dump(in_array(0, $array, true));
这东西的输出是false!!!
文档
http://php.net/manual/en/function.in-array.php
很简单没啥可说的
in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ) : bool
源码
https://github.com/php/php-src/blob/master/ext/standard/array.c
/* {{{ proto bool in_array(mixed needle, array haystack [, bool strict])
Checks if the given value exists in the array */
PHP_FUNCTION(in_array)
{
php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
/* }}} */
/* {{{ proto mixed array_search(mixed needle, array haystack [, bool strict])
Searches the array for a given value and returns the corresponding key if successful */
PHP_FUNCTION(array_search)
{
php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}
/* }}} */
没错,in_array和array_search只是最后一个参数不一样而已。in_array需要自己制定最后一个参数,array_search最后一个参数是true。
再来看看php_search_array如何实现的
/* 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;
}
/* }}} */
重点
if (Z_TYPE_P(value) == IS_LONG) {
// ZEND_HASH_FOREACH_KEY_VAL(hashtable, 数值索引, 字符串索引, 值)
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();
}
相关函数实现
/*
op1: php输入的查找项,
op2: array遍历当前项
*/
static zend_always_inline int fast_equal_check_long(zval *op1, zval *op2)
{
zval result;
// 明显op2是字符串,不是long,跳过这里
if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) {
return Z_LVAL_P(op1) == Z_LVAL_P(op2);
}
// 重点!!
// 参数1:执行结果
// 参数2: php输入的查找项: 0
// 参数3: php数组当前项: "a" 或者 "b" 或者 "c"
compare_function(&result, op1, op2);
return Z_LVAL(result) == 0;
}
来继续看源码
https://github.com/php/php-src/blob/db0079023421b8048f090ee04adb992e09132553/Zend/zend_operators.c
// 参数1:执行结果
// 参数2: php输入的查找项: 0
// 参数3: php数组当前项: "a" 或者 "b" 或者 "c"
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)<Z_LVAL_P(op2)?-1:0));
return SUCCESS;
case TYPE_PAIR(IS_DOUBLE, IS_LONG):
Z_DVAL_P(result) = Z_DVAL_P(op1) - (double)Z_LVAL_P(op2);
ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));
return SUCCESS;
case TYPE_PAIR(IS_LONG, IS_DOUBLE):
Z_DVAL_P(result) = (double)Z_LVAL_P(op1) - Z_DVAL_P(op2);
ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));
return SUCCESS;
case TYPE_PAIR(IS_DOUBLE, IS_DOUBLE):
if (Z_DVAL_P(op1) == Z_DVAL_P(op2)) {
ZVAL_LONG(result, 0);
} else {
Z_DVAL_P(result) = Z_DVAL_P(op1) - Z_DVAL_P(op2);
ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));
}
return SUCCESS;
case TYPE_PAIR(IS_ARRAY, IS_ARRAY):
ZVAL_LONG(result, zend_compare_arrays(op1, op2));
return SUCCESS;
case TYPE_PAIR(IS_NULL, IS_NULL):
case TYPE_PAIR(IS_NULL, IS_FALSE):
case TYPE_PAIR(IS_FALSE, IS_NULL):
case TYPE_PAIR(IS_FALSE, IS_FALSE):
case TYPE_PAIR(IS_TRUE, IS_TRUE):
ZVAL_LONG(result, 0);
return SUCCESS;
case TYPE_PAIR(IS_NULL, IS_TRUE):
ZVAL_LONG(result, -1);
return SUCCESS;
case TYPE_PAIR(IS_TRUE, IS_NULL):
ZVAL_LONG(result, 1);
return SUCCESS;
case TYPE_PAIR(IS_STRING, IS_STRING):
if (Z_STR_P(op1) == Z_STR_P(op2)) {
ZVAL_LONG(result, 0);
return SUCCESS;
}
ZVAL_LONG(result, zendi_smart_strcmp(Z_STR_P(op1), Z_STR_P(op2)));
return SUCCESS;
case TYPE_PAIR(IS_NULL, IS_STRING):
ZVAL_LONG(result, Z_STRLEN_P(op2) == 0 ? 0 : -1);
return SUCCESS;
case TYPE_PAIR(IS_STRING, IS_NULL):
ZVAL_LONG(result, Z_STRLEN_P(op1) == 0 ? 0 : 1);
return SUCCESS;
case TYPE_PAIR(IS_OBJECT, IS_NULL):
ZVAL_LONG(result, 1);
return SUCCESS;
case TYPE_PAIR(IS_NULL, IS_OBJECT):
ZVAL_LONG(result, -1);
return SUCCESS;
default: // 重点:IS_LONG, IS_STRING处理
if (Z_ISREF_P(op1)) { // 解引用
op1 = Z_REFVAL_P(op1);
continue;
} else if (Z_ISREF_P(op2)) { // 解引用
op2 = Z_REFVAL_P(op2);
continue;
}
if (Z_TYPE_P(op1) == IS_OBJECT && Z_OBJ_HANDLER_P(op1, compare)) {
ret = Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2);
if (UNEXPECTED(Z_TYPE_P(result) != IS_LONG)) {
convert_compare_result_to_long(result);
}
return ret;
} else if (Z_TYPE_P(op2) == IS_OBJECT && Z_OBJ_HANDLER_P(op2, compare)) {
ret = Z_OBJ_HANDLER_P(op2, compare)(result, op1, op2);
if (UNEXPECTED(Z_TYPE_P(result) != IS_LONG)) {
convert_compare_result_to_long(result);
}
return ret;
}
if (Z_TYPE_P(op1) == IS_OBJECT && Z_TYPE_P(op2) == IS_OBJECT) {
if (Z_OBJ_P(op1) == Z_OBJ_P(op2)) {
/* object handles are identical, apparently this is the same object */
ZVAL_LONG(result, 0);
return SUCCESS;
}
if (Z_OBJ_HANDLER_P(op1, compare_objects) == Z_OBJ_HANDLER_P(op2, compare_objects)) {
ZVAL_LONG(result, Z_OBJ_HANDLER_P(op1, compare_objects)(op1, op2));
return SUCCESS;
}
}
if (Z_TYPE_P(op1) == IS_OBJECT) {
if (Z_OBJ_HT_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;
}
}
/*
继续追宏定义
Zend/zend_types.h源码定义了
#define IS_UNDEF 0
#define IS_NULL 1
#define IS_FALSE 2
#define IS_TRUE 3
#define IS_LONG 4 // op1
#define IS_DOUBLE 5
#define IS_STRING 6 // op2
#define IS_ARRAY 7
#define IS_OBJECT 8
#define IS_RESOURCE 9
#define IS_REFERENCE 10
*/
if (!converted) {
if (Z_TYPE_P(op1) < IS_TRUE) { // no
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;
}
}
}
}
/* }}} */
最后的最后
https://github.com/php/php-src/blob/db0079023421b8048f090ee04adb992e09132553/Zend/zend_operators.c
#define zendi_convert_scalar_to_number(op, holder, result, silent) \
((Z_TYPE_P(op) == IS_LONG || Z_TYPE_P(op) == IS_DOUBLE) ? (op) : \
(((op) == result) ? (_convert_scalar_to_number((op), silent, 1), (op)) : \
(silent ? _zendi_convert_scalar_to_number((op), holder) : \
_zendi_convert_scalar_to_number_noisy((op), holder))))