December
18th, 2008
['yahoo', 'google', 0, 'amazon', 'ebay'].search(’facebook’) === true … in PHP
A coworker of mine found some peculiar behavior in some code we had. We had an array with several values like you see in the title. We checked values in that array using PHP’s array_search function. While the string being searched for wasn’t present, the function was returning true. Initially perplexed by this we realized quickly after looking at the source of PHP’s array searching function we noticed the problem. There’s a second parameter to the array searching functions which tell PHP to do strict comparisons. This is in their documentation at http://php.net/array_search. It makes complete sense since PHP is loosely typed. But it’s not at all intuitive at first glance.
So what’s the problem?
No problem. PHP does implicit type conversion when comparing two values. The string facebook when converted to an integer becomes 0. So when PHP does the comparison it’s true.
var_dump((0 == 'facebook')); // true var_dump((1=='1 is the loneliest number')); // true
Generally speaking you’ll probably want to do a strict comparison when using PHP’s array search functions. Unfortunately, the default behavior is to do loose comparisons. Make sure you pass in the extra argument in order to force a strict check.
May the source be with you
The C source for the function that winds up being called helped us location the problem. Note the last argument behavior.
static 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 */
res; /* comparison result */
HashPosition pos; /* hash iterator */
zend_bool strict = 0; /* strict comparison or not */
ulong num_key;
uint str_key_len;
char *string_key;
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)) {
if (behavior == 0) {
RETURN_TRUE;
} else {
/* Return current key */
switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &str_key_len, &num_key, 0, &pos)) {
case HASH_KEY_IS_STRING:
RETURN_STRINGL(string_key, str_key_len - 1, 1);
break;
case HASH_KEY_IS_LONG:
RETURN_LONG(num_key);
break;
}
}
}
zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos);
}
RETURN_FALSE;
}
