看兩段幾乎相同的代碼,僅差在 in_array 第三個參數 $strict:
// a.php - strict = true
$y = "12";
$x = range(0, 49999); // 50000 個字符串 "0"..."49999"
for ($i = 0; i < 30000; $i++) {
in_array($y, $x, true);
}
// 耗時: 0.101s
// b.php - strict = false (默認)
$y = "1800";
$x = range(0, 49999);
for ($i = 0; i < 30000; $i++) {
in_array($y, $x); // 非嚴格模式
}
// 耗時: 9.517s —— 慢了 94 倍!
系統調用統計顯示 strtol 佔用 52.92% CPU 時間(78 萬次調用),這是字符串轉長整數的庫函數。
% time seconds usecs/call calls function 52.92 175.486683 224 780155 strtol 33.34 110.550182 55275091 2 __libc_start_main 6.33 20.999979 177 118479 memcpy ...
PHP_FUNCTION(in_array) {
php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}
static void php_search_array(INTERNAL_FUNCTION_PARAMETERS, int behavior) {
zval *value, *array, **entry, res;
HashPosition pos;
zend_bool strict = 0;
int (*is_equal_func)(zval *, zval *, zval * TSRMLS_DC) = is_equal_function;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "za|b", &value, &array, &strict) == FAILURE) return;
if (strict) {
is_equal_func = is_identical_function; // 嚴格模式:類型+值均相等
}
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
while (zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void**)&entry, &pos) == SUCCESS) {
is_equal_func(&res, value, *entry TSRMLS_CC);
if (Z_LVAL(res)) { RETURN_TRUE; }
zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
}
RETURN_FALSE;
}
is_identical_function (===):先比類型,類型不同直接返回 false,無轉換開銷。
is_equal_function (==):委託 compare_function,涉及複雜的類型轉換規則:
is_numeric_string_ex → strtol/zend_strtod)get 或 cast_object
// zendi_smart_strcmp -> is_numeric_string_ex
static inline zend_uchar is_numeric_string_ex(const char *str, int length, long *lval, double *dval, ...) {
// ... 跳過空白、符號、判斷進制 ...
if (type == IS_LONG) {
// 關鍵行:調用 strtol 完成字符串→長整數轉換
if (lval) *lval = strtol(str, NULL, base);
return IS_LONG;
}
}
PHP 的非嚴格比較規則:字符串與數字比較時,字符串會被轉換為數字。
"1800" == 1800 → true"abc" == 0 → true(非數字字符串轉為 0)"12abc" == 12 → true(前導數字部分)測試中 $y = "1800",數組含整數 0~49999。非嚴格模式下,"1800" 每次比較都要轉換為數字 1800,然後與數組元素比較。而 strtol 的重複調用導致性能崩塌。
in_array($needle, $haystack, true),避免隱式轉換陷阱並提升性能isset($map[$key]) 或 array_key_exists() 為 O(1),將值作為鍵存儲in_array 非嚴格模式看似方便,實則隱藏著 strtol/zend_strtod 等昂貴的類型轉換開銷。從內核層面看,is_equal_function 為了實現 PHP 的弱類型語義,付出了巨大的運行時代價。嚴格模式不僅語義更清晰,更是性能的保障。