PHP数组的问题,其中键是大整数


Issue with PHP array where keys are large integers

我在这里要做的基本上是从$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_MinPHP_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下。