不同机器上的PHP in_array比较表现不同

11
请查看以下代码片段,它在不同的机器上给出了不同的结果:
$data = array(
    "28000000000000003" => 'ABC',
    "28000000000000001" => 'PQR'
);

echo "1.".in_array("28000000000000003",array_keys($data),true);

echo "2.".in_array("28000000000000003",array_keys($data));

echo "3.".in_array("28000000000000003",array("28000000000000003","28000000000000001"),true);

echo "4.".in_array("28000000000000003",array("28000000000000003","28000000000000001"));

如预期,在我们的本地服务器上,4种情况都为真,而在生产服务器上,第一种情况给出了错误结果,其余三个为真。

有人能帮我理解发生了什么事吗?是我在配置方面漏掉了什么吗?


4
你确定这些是字符串吗? - jeroenvisser101
1
请执行以下代码:array_map('var_dump', array_keys($data)); - nice ass
3
好的,您的答案在这里 :) 您是如何在生产环境中创建该数组的? - nice ass
1
生产机器使用的是64位版本的PHP吗? - Salman A
1
似乎与此相关,因此在array_keys(strict)上使用第三个参数。 - nice ass
显示剩余10条评论
4个回答

10

非常容易....让我猜测一下,你的开发系统是Windows,而你的生产服务器是Linux?

你遇到整数溢出问题,因为你的PHP Windows版本很可能是32位的,而Linux是64位的。

查看数组键转换的条件

  • 包含有效整数的字符串将被转换为整数类型。例如,键"8"实际上将存储在8下。另一方面,"08"不会被转换,因为它不是有效的十进制整数。
  • 浮点数也会被转换为整数,这意味着小数部分将被截断。例如,键8.7实际上将存储在8下。
  • 布尔值也会被转换为整数,即键true实际上将存储在1下,键false存储在0下。
  • null将被转换为空字符串,即键null实际上将存储在""下。不能使用数组和对象作为键。这样做会导致警告:非法的偏移类型。

所以发生的情况是:

因此,关键字2800000000000000364位系统上是有效的整数,但在32位系统上是一个字符串

我能够复制您的问题

echo "<pre>";
$data = array("28000000000000003" => 'ABC',"28000000000000001" => 'PQR');
$keys = array("28000000000000003","28000000000000001");
$keysDerived = array_keys($data);

var_dump(in_array("28000000000000003", $keysDerived, true));
var_dump(in_array("28000000000000003", $keysDerived));
var_dump(in_array("28000000000000003", $keys, true));
var_dump(in_array("28000000000000003", $keys));

输出

bool(false)    <----------------------- false instead of true 
bool(true)
bool(true)
bool(true)

这个问题与in_array无关,而是与array_keys示例有关。

示例代码

echo "<pre>"; 
$data = array("28000000000000003" => 'ABC',"28000000000000001" => 'PQR');
$keys = array("28000000000000003","28000000000000001");
$keysDerived = array_keys($data);
var_dump($keys,$keysDerived);

输出

array(2) {
  [0]=>
  string(17) "28000000000000003"    <------- Keys are String
  [1]=>
  string(17) "28000000000000001"
}
array(2) {
  [0]=>
  int(28000000000000003)           <------- They are converted to int on 64bits
  [1]=>
  int(28000000000000001)
}

查看在线演示

这意味着它们不是同一类型...

in_array bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

如果第三个参数strict设置为TRUE,则in_array()函数还将检查在haystack中的needle的类型

如果您运行此代码

foreach ( $keys as $key ) {
    echo gettype($key) . "\n";
}

foreach ( $keysDerived as $key ) {
    echo gettype($key) . "\n";
}

输出 64位

string
string
integer
integer

输出 32 位

string
string
string
string

简单解决方案

echo "<pre>";
$data = array("28000000000000003" => 'ABC',"28000000000000001" => 'PQR');
$keys = array("28000000000000003","28000000000000001");
$keysDerived = array_keys_string($data);
var_dump($keys,$keysDerived);

var_dump(in_array("28000000000000003", $keysDerived, true));
var_dump(in_array("28000000000000003", $keysDerived));
var_dump(in_array("28000000000000003", $keys, true));
var_dump(in_array("28000000000000003", $keys));

输出

array(2) {
  [0]=>
  string(17) "28000000000000003"
  [1]=>
  string(17) "28000000000000001"
}
array(2) {
  [0]=>
  string(17) "28000000000000003"
  [1]=>
  string(17) "28000000000000001"
}
bool(true)
bool(true)
bool(true)
bool(true)

查看原始代码 查看修改后的代码

使用的函数

function array_keys_string(array $input) {
    $list = array();
    foreach ( $input as $k => $v ) {
        $list[] = (string)$k;
    }
    return $list;
}

6

您的本地服务器是32位的,生产服务器是64位的。

PHP文档指出,在定义数组字面值时,键将被强制转换:

包含有效整数的字符串将被转换为整数类型。例如,键“8”实际上将存储在8下。

因此,如果您尝试以下代码片段:

var_export(array("5" => "test"));

您会看到结果是一个带有数字键 5 的数组,而不是字符串键 "5"
在您的情况下,您拥有大量数字字符串作为键。在32位计算机上,数字28000000000000003超过了可能的最大整数值(PHP_INT_MAX),因此数组键将保持为字符串,并且这就是在您的本地服务器上发生的事情。另一方面,在64位计算机上,最大整数要大得多,"28000000000000003"被转换为integer,这就是在您的生产服务器上发生的情况。
因此,在64位生产服务器上运行时,array_keys($data)返回整数数组。在您的第一个测试用例中,当您尝试使用严格比较在其中查找字符串时,您会得到FALSE

2

问题:如果你有长整数类型的键,例如'329462291595',在64位系统上它们将被视为长整数类型,但在32位系统上将被视为字符串类型

因此,函数array_keys($data)在64位系统中返回int,在32位系统中返回string。

解决方案:将所有array_keys($data)转换为字符串。 然后使用第三个参数true(用于严格检查)执行in_array("28000000000000003",array_keys($data),true);

function array_keys_string($arr){
    $arr_keys      = array_keys($arr);
    $res_arry      = array();
    foreach($arr_keys as $val){
       $res_arry[] = (string)$val;
    }
    return $res_arry;
}


echo "1.".in_array("28000000000000003",array_keys_string($data),true);

这将在两个服务器上产生相同的结果。
请参考以下链接:
ref: http://www.php.net/manual/en/function.array-keys.php#105578 来自php.net:http://www.php.net/manual/en/function.in-array.php
<?php
$a = array('1.10', 12.4, 1.13);

if (in_array('12.4', $a, true)) {
    echo "'12.4' found with strict check\n";
}

if (in_array(1.13, $a, true)) {
    echo "1.13 found with strict check\n";
}
?>

上面的示例将输出:使用严格检查找到1.13

1
不会。在32位平台上,array_keys($data)将包含字符串,而没有引号的28000000000000003 将被转换为浮点数,因此使用严格比较的in_array的结果将是FALSE - Denys Popov

2
您可以尝试使用array_key_exists()。关联数组键的长度有一定限制。详情请参见以下链接:PHP关联数组键(索引)的限制? 您可以增加键内存限制。使用ini_set('memory_limit', '1024M');

他的问题中没有描述任何表明达到内存限制的行为。此外,在大局观中,他的数组键相对较短。这个答案似乎没有回答他的问题。 - Corbin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接