- URL: https://www.laruence.com/en/2010/05/26/1541.html
- Please include attribution when republishing.
To avoid confusion between numeric indexes and numeric-string indexes (see note 1), PHP introduced the zend_symtable_* family of functions and applied them to arrays.
As a result, a numeric-string index is also treated as a numeric index. But there are always some cases the PHP maintainers didn't think of...
For instance, type casting:
Given the many kind suggestions that json_decode's second parameter can give you an array directly: Let me clarify — the code below is deliberate. The point is not json_decode itself, but constructing a "problematic" array.
Under PHP 5.2.* (json version 1.2.1):
$data = array(
123 => 'laruence',
);
$value = json_encode($data);
$obj = json_decode($value);
$arr = (array)$obj;
var_dump($arr);
Also, shenxian provided an even simpler way to construct it:
$obj=new stdClass;
$obj->{'123'} = "laruence";
$arr = (array)$obj;
var_dump($arr);
At this point the problem appears. The output above is:
array(1) {
["123"]=>
string(8) "laruence"
}
Now you're stuck, because the array key is a string, while through the normal access path PHP automatically converts numeric strings to numbers. So:
print_r($arr[123]);
//PHP Notice: Undefined offset: 123 in ***
print_r($arr["123"]);
//PHP Notice: Undefined offset: 123 in ***
var_dump(array_key_exists("123", $arr));
//bool(false)
I've filed the bug. However, PHP itself doesn't guarantee consistency in type casting, so in the end the maintainers felt it hardly mattered whether it's a bug or not — just keep it in mind in everyday use: http://bugs.php.net/bug.php?id=51915
Note 1
The numeric strings discussed here differ slightly from the numeric strings in my earlier article PHP String Comparison. In the zend_symtable_* family of functions, only strings matching /^-?[^0][0-9]*$/ are treated as numeric strings. The relevant core logic is:
#define HANDLE_NUMERIC(key, length, func) {
register char *tmp=key;
if (*tmp=='-') {
tmp++;
}
if ((*tmp>='0' && *tmp<='9')) do {
char *end=key+length-1;
long idx;
if (*tmp++=='0' && length>2) {
break;
}
while (tmp<end) {
if (!(*tmp>='0' && *tmp<='9')) {
break;
}
tmp++;
}
if (tmp==end && *tmp=='0') {
if (*key=='-') {
idx = strtol(key, NULL, 10);
if (idx!=LONG_MIN) {
return func;
}
} else {
idx = strtol(key, NULL, 10);
if (idx!=LONG_MAX) {
return func;
}
}
}
} while (0);
}
PS: I only have versions 5.2.8 and 5.2.11 here. If any readers have other PHP versions, please help test whether this problem also exists in your version. Thanks.
Also: thanks to yuanhao for raising this issue; the original problem was related to Memcached.
Be First to Comment