Unpack() дает разные результаты на разных машинах

У меня какое-то странное поведение с функцией распаковки. У меня есть упакованная строка, хранящаяся как longblob в базе данных mysql. Когда я читаю эту строку и распаковываю ее, она дает мне массив, пока все хорошо. Но когда я запускаю это на другой машине, некоторые значения в массиве отличаются.

Когда я выгружаю данные из mysql, они равны на обеих машинах.

Распаковка производится так:

$array = unpack("N*", $packed); 

$array тогда должно быть так (и это на одной машине)

Array
(
    [1]  => 179848175
    [2]  => -16214255
    [3]  => 179848175
    [4]  => -16214255
    [5]  => 179848175
    [6]  => -16214255
    [7]  => 179999949
    [8]  => -16152916
    [9]  => 179999277
    [10] => -16168574
...
)

А вот на другой машине так:

Array
(
    [1]  => 179848175
    [2]  => 427853622
    [3]  => 179848175
    [4]  => 427853622
    [5]  => 179848175
    [6]  => 427853622
    [7]  => 179999949
    [8]  => 427853423
    [9]  => 179999277
    [10] => 427853341
...
)

Каждое второе значение кажется другим.

Я проверил это на трех разных машинах, на двух все было нормально, но на этой машине я получаю странный результат.

На одной машине работает PHP 5.6.3 (тут все ок), на двух машинах работает PHP 5.5.14 (на одной нормально, на другой нет)


person Ben    schedule 13.07.2015    source источник
comment
На одной машине работает 32-битный PHP, а на другой — 64-битный PHP?   -  person Mark Baker    schedule 13.07.2015
comment
не уверен в этом, uname -a выводит x86_64 на обеих машинах. Есть ли способ, если одному 32, другому 64?   -  person Ben    schedule 13.07.2015
comment
Просто проверьте значение констант PHP_INT_MAX (значение 2147483647 указывает на 32-разрядную версию) или PHP_INT_SIZE (значение 4 указывает на 32-разрядную версию, 8 — на 64-разрядную версию) из PHP-скрипта... просто потому, что операционная система 64-битный, не означает, что PHP, на котором он работает, является 64-битным.   -  person Mark Baker    schedule 13.07.2015
comment
хорошо, только что сделал это на машинах, где он работает, там echo PHP_INT_SIZE показывает 4. К сожалению, я не могу проверить на нерабочей машине прямо сейчас, так как у меня нет к ней доступа до завтра. Если это 64-битный PHP, есть ли способ справиться с этим?   -  person Ben    schedule 13.07.2015
comment
Я думаю, что в этом есть ответ: [64-битный php с php_int_max = 2147483647][1] [1]: stackoverflow.com/questions/17837157/   -  person DTH    schedule 13.07.2015
comment
спасибо, попробую разобраться, хотя вроде бы наоборот. В противном случае я бы попытался удалить php и установить 32-битную версию (?)...   -  person Ben    schedule 13.07.2015


Ответы (1)


Формат pack N означает unsigned long, что означает, что он не может быть отрицательным. Однако вы сохраняете отрицательные значения, и именно они не распаковываются так, как вы хотите. PHP не имеет формата pack для машинно-независимых подписанных длинных слов; он поддерживает только их упаковку в машинном порядке байтов, что может быть несовместимо на разных машинах. Таким образом, вам придется подписывать значения самостоятельно.

Чтобы преобразовать элементы массива в значения со знаком:

for ($i = 1; $i <= count($array); $i++) {
    // Check for a greater-than-32-bit environment,
    // and check if the number should be negative
    // (i.e., if the high bit is set in 32-bit notation).
    if (PHP_INT_SIZE > 4 && $array[$i] & 0x80000000) {
        // A negative number was unpacked as an unsigned
        // long in a greater-than-32-bit environment.
        // Subtract the appropriate amount (max 32-bit
        // unsigned long + 1) to convert it to negative.
        $array[$i] = $array[$i] - 0x100000000;
    }
}

var_dump($array);
person Lithis    schedule 13.07.2015
comment
Спасибо! Я проверю это, как только снова получу доступ к машине! - person Ben; 13.07.2015