我在这里要做的基本上是从$stats
开始,得到一个包含四个数组的数组$counts
,其中每个数组都是一对:键是以毫秒为单位的时间戳而值是计数。我在Windows 7 x64上使用的是PHP 5.3.14版本。
问题是:为什么我得到负数组键值,以及如何避免这种情况?参见下面的var_dump()
:
$stats = array();
$stats[] = array(
'subtype' => 'small_text_message_user',
'count' => '6',
'date' => '2012-06-03'
);
$stats[] = array(
'subtype' => 'small_text_message_auto',
'count' => '3',
'date' => '2012-07-03',
);
$stats = array(
'subtype' => 'newsletter_auto',
'count' => '11',
'date' => '2012-07-16',
);
$counts = array();
$counts['small_text_message_user'] = array();
$counts['small_text_message_auto'] = array();
$counts['newsletter_user'] = array();
$counts['newsletter_auto'] = array();
foreach($data as $stat) :
$millisecs = 1000 * strtotime($stat['date']);
$count = intval($stat['count']);
$counts[$stat['subtype']][$millisecs] = $count;
var_dump($millisecs, $count);
endforeach;
var_dump($counts);
结果是错误的:
float 1338674400000
int 6
float 1341266400000
int 3
float 1342389600000
int 11
array (size=4)
'small_text_message_user' =>
array (size=1)
-1355396352 => int 6
'small_text_message_auto' =>
array (size=1)
1236603648 => int 3
'newsletter_user' =>
array (size=0)
empty
'newsletter_auto' =>
array (size=1)
-1935163648 => int 11
一个例子(实际上是有效的)是一小段代码,其中我生成一个";伪";阵列:
public function createTimestampRangeFromDates('DateTime $from,
'DateTime $to = null)
{
$start = strtotime($from->format('Y-m-d 00:00:00'));
$limit = strtotime(sprintf('%s 00:00:00', $to ? $to->format('Y-m-d')
: date('Y-m-d') . ' 00:00:00'));
return range($start, $limit, 86400);
}
// Test
$start = new 'DateTime('2012-06-27');
$end = new 'DateTime('2012-07-07'); // 10 days
$timestamps = createTimestampRangeFromDates($start, $end);
$millisecs = array_combine($timestamps, array_fill(0, count($timestamps), 0));
var_dump($millisecs);
这个工作正常,我得到大整数作为密钥:
array (size=11)
'1340748000000' => int 0
'1340834400000' => int 0
'1340920800000' => int 0
'1341007200000' => int 0
'1341093600000' => int 0
'1341180000000' => int 0
'1341266400000' => int 0
'1341352800000' => int 0
'1341439200000' => int 0
'1341525600000' => int 0
'1341612000000' => int 0
哦,如果这很重要的话,这个代码的目的是做一些类似的事情:
$values = array_merge($millisecs, $counts['newsletter_auto']);
这就是用实值覆盖伪数组。
按照@hakre的建议更新小型测试用例
$result1 = array();
$result2 = array();
$test = array('2012-06-03', '2012-07-03', '2012-07-16');
foreach($test as $date) :
$result1[1000 * strtotime($date)] = 0;
$result2[] = 1000 * strtotime($date);
endforeach;
var_dump($result1); // Not working
var_dump($result2); // Works
结果显示$result1
的负密钥。问题仅针对密钥,不针对值(因为值转换为float
):
array (size=3)
-1355396352 => int 0
1236603648 => int 0
-1935163648 => int 0
array (size=3)
0 => float 1338674400000
1 => float 1341266400000
2 => float 1342389600000
PHP将nummeric(so,float或integer)数组索引转换为integer:
ckruse@lin ~ $ php -r 'var_dump(array(1.2 => "a"));'
array(1) {
[1]=>
string(1) "a"
}
因此,如果您有一个非常大的数组索引,它在计算时会被PHP转换为浮点,然后在用作索引时返回为整数。这意味着您将获得一个整数溢出,其中包含到大的索引。
解决方案可以是使用字符串索引:
ckruse@lin ~ $ php -r 'var_dump(array(((string)1.2) => "a"));'
array(1) {
["1.2"]=>
string(1) "a"
}
编辑:手动打开http://php.net/manual/en/language.types.array.php说:
浮点值也被强制转换为整数,这意味着小数部分将被截断。例如,密钥8.7实际上将存储在8下。
您在PHP文档中发现了一些关于数组键最大长度的内容吗?
PHP手册将其定义如下:"密钥可以是整数或字符串。"(来源)
与每种数据类型一样,整数也是有限的。这在计算中很常见。根据软件或底层处理器处理整数的方式,它们可以翻转、翻转或翻转。
在你的情况下,当越过边界时,它会变成最小的负数。我称之为翻转,其他人可能称之为溢出或环绕。
你可以用PHP_INI_Min
和PHP_INI_MAX
找到边界。
注意字符串键,还有一个限制,它通常不能超过2GB。只是说说而已。
请阅读php文档中关于整数溢出的部分。http://www.php.net/manual/en/language.types.integer.php#language.types.integer.overflow
如果PHP遇到超出整数类型界限的数字,它将被解释为浮点值。此外,导致数字超出整数类型边界的操作将返回浮点值。
好的,一点研究表明,浮动作为数组键被截断为整数。因此,我的大浮点值被截断为整数会导致溢出。仍在寻找官方文档链接。
请参阅这个(关闭的)bug数组键从浮点转换为整数,以及本文和这个pull请求。
EDIT:在PHP文档中找到:
键可以是整数,也可以是字符串。该值可以是任意值类型
此外,还会发生以下密钥转换:
漂浮物也被抛到整数,这意味着小数部分将被截断。例如。密钥8.7实际上将存储在8下。