Подтвердить что ты не робот

Сравнение PHP in_array, работающее по-разному на разных машинах

Прочитайте следующий фрагмент кода, он дает разные результаты на разных машинах:

$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 случаев на нашем локальном сервере, тогда как на производственном сервере в 1-м случае он дает ложный результат и true в остальных трех

Может кто-нибудь помочь мне понять, что именно происходит? Пропустить ли я с точки зрения конфигурации?

4b9b3361

Ответ 1

Его очень легко.... позвольте мне угадать, что ваша система разработки - это окна, а ваш производственный сервер - Linux?

У вас есть проблемы с переполнением Integer, потому что большинство пользователей вашей версии Windows PHP 32bit, а linux - 64bit

См. Условие для преобразования ключа массива

  • Строки, содержащие действительные целые числа, будут отлиты от целочисленного типа. Например. ключ "8" будет фактически сохранен под 8. С другой стороны, "08" не будет выбрано, так как оно не является допустимым десятичным целым.
  • Поплавки также отливаются от целых чисел, что означает, что дробная часть будет усечена. Например. ключ 8.7 будет фактически сохранен под 8.
  • Bools также передаются в целые числа, т.е. ключ true будет фактически сохранен под 1 и ключ false под 0.
  • Null будет передан в пустую строку, т.е. нулевой ключ будет фактически сохранен под "". Массивы и объекты не могут использоваться в качестве ключей. Это приведет к предупреждению: тип недопустимого смещения.

Итак, что происходит, это:

Таким образом, ключ 28000000000000003 является допустимым integer в 64bit, но a String в системе 32bits

Я смог реплицировать вашу проблему

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 example

Пример кода

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 (смешанная $игла, массив $haystack [, bool $strict = FALSE])

Если для третьего параметра Строго установлено значение ИСТИНА, то функция in_array() также проверит типы иглы в стоге сена.

Если вы запустите этот код

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

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

Выход 64Bits

string
string
integer
integer

Вывод 32Bits

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;
}

Ответ 2

Ваш локальный сервер 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.

Ответ 4

Проблема. Если у вас есть ключи с длинным целым числом, например "329462291595", они будут считаться как таковые в 64-битной системе, но будут иметь тип строка в 32-битной системе.

Таким образом, функция array_keys($data) вернет int в 64-битную систему и строку в 32-битной системе

Решение: преобразовать все array_keys($data) в строку.          а затем in_array("28000000000000003",array_keys($data),true); с третьим параметром 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, найденный со строгой проверкой