Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP in_array comparison working differently on different machine

Tags:

arrays

php

Please review following piece of code, it is giving different results on different machines:

$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"));

As expected, results true for all 4 cases on our local server, while on production server in 1st case it is giving false result and true in rest of three

Can anybody help me to understand what exactly happening? Do I missed from configuration point of view?

like image 337
HRK Avatar asked Feb 11 '13 12:02

HRK


2 Answers

Its very easy .... let me guess your Development system is windows and your production server is linux ?

You are having Integer overflow Issues because most likey your windows version of PHP is 32bit and linux is 64bit

See Condition for array key conversion

  • Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer.
  • Floats are also cast to integers, which means that the fractional part will be truncated. E.g. the key 8.7 will actually be stored under 8.
  • Bools are cast to integers, too, i.e. the key true will actually be stored under 1 and the key false under 0.
  • Null will be cast to the empty string, i.e. the key null will actually be stored under "". Arrays and objects can not be used as keys. Doing so will result in a warning: Illegal offset type.

So what happens is that :

So the key 28000000000000003 is a valid integer on a 64bit but a String on a 32bits system

I was able to replicate your issue

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

Output

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

This issues has nothing to do with in_array but rather array_keys example

Sample Code

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

Output

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

See Online Demo

This means that they are not the same type ...

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

If the third parameter strict is set to TRUE then the in_array() function will also check the types of the needle in the haystack.

If you run this code

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

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

Output 64Bits

string
string
integer
integer

Output 32Bits

string
string
string
string

Simple Workaround

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

Output

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)

See Original Code See Modified Code

Function Used

function array_keys_string(array $input) {
    $list = array();
    foreach ( $input as $k => $v ) {
        $list[] = (string)$k;
    }
    return $list;
}
like image 187
Baba Avatar answered Sep 28 '22 04:09

Baba


Your local server is 32-bit and your production server is 64-bit.

PHP documentation says that when defining array literals, keys will be cast:

Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8.

So, if you try the following piece of code:

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

You will see the result is an array with numerical key 5, not string key "5".

In your case, you have large numerical strings as keys. On a 32-bit machine, number 28000000000000003 exceeds the maximum possible integer value (PHP_INT_MAX), so the array key will stay string, and that is what happens on your local server. On a 64-bit machine, on the other hand, maximum integer is way greater, and "28000000000000003" is cast to integer, and that is what happens on your production server.

So, when run on the 64-bit production server, array_keys($data) returns array of integers. When in your first test case you try to find a string in it using strict comparison, you get FALSE.

like image 45
Denys Popov Avatar answered Sep 28 '22 04:09

Denys Popov