5

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

4 回答 4

7

PHP 将数字(so、float 或 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 之下。

于 2012-07-08T12:15:27.037 回答
1

您是否在 PHP 文档中找到了说明数组键最大长度的内容?

PHP 手册对其进行了如下定义:“键可以是整数或字符串。” 来源

与每种数据类型一样,整数也是有限的。这在计算中很常见。根据软件或底层处理器处理整数的方式,它们可以翻转、地板或顶部。

在您的情况下,当越过边界时,它会变为最小的负数。我称之为翻转,其他人可能称之为溢出环绕

PHP_INI_Min您可以使用和找出边界PHP_INI_MAX

注意字符串键,也有限制,通常不能大于 2GB。只是说。

于 2012-07-08T12:24:08.403 回答
0

请阅读 php 文档中关于整数溢出的部分。http://www.php.net/manual/en/language.types.integer.php#language.types.integer.overflow

“如果 PHP 遇到超出整数类型边界的数字,它将被解释为浮点数。此外,导致数字超出整数类型边界的操作将返回浮点数。”

于 2012-07-08T12:09:05.920 回答
0

好的,一些研究表明作为数组键的浮点数被截断为整数。所以我的大浮点数被截断为整数会导致溢出。仍在寻找官方文档链接。

请参阅此(已关闭)错误数组键从浮点数转换为整数以及本文和此拉取请求

编辑:在PHP 文档中找到:

键可以是整数或字符串。该值可以是任何类型。

此外,还会发生以下关键转换:

浮点数也被转换为整数,这意味着小数部分将被截断。例如,密钥 8.7 实际上将存储在 8 之下。

于 2012-07-08T12:14:16.023 回答