PHP несериализованное целое число от 64-разрядного до 32-разрядного

#php #serialization #architecture #integer-overflow

#php #сериализация #архитектура #целое число-переполнение

Вопрос:

Я пытался отменить сериализацию на 32-разрядном сервере объекта, который был сериализован на 64-разрядном сервере. Я выделил свою проблему в целое число в объекте. Вот небольшое воспроизведение проблемы.

На 64-разрядной машине:

 $i = serialize('20110510134021'); //i:20110510134021;
 

На 32-разрядной машине:

 $i = unserialize('i:20110510134021;');
 

Выдает ошибку

 Notice: unserialize(): Error at offset 0 of 16 bytes
 

Теперь я понимаю, что этот метод сериализации не следует использовать для межсистемной передачи данных. Однако мы просто пытаемся перенести данные в другую систему, а не активно использовать их для передачи. Это одноразовая вещь.

Я бы подумал, что это может быть связано с переполнением целых чисел, но даже на 32-разрядном сервере я могу сделать что-то вроде

 $i = 20110510134021;
echo $i;
 

И это будет работать нормально. Я предполагаю, что целочисленные типы PHP масштабируются до некоторого двойного типа или что-то в этом роде. Но почему он этого не делает, когда он отменяет сериализацию?

Как я могу отменить сериализацию этих объектов? Если я не могу, есть ли какой-нибудь способ преобразовать их во что-то другое? Наконец, кто-нибудь написал метод десериализации в самом PHP? Или есть подробности о протоколе? Я мог бы использовать это и иметь пользовательский регистр для этих целых чисел.

Спасибо.

Примечание: у меня нет доступа к исходным данным, только к сериализованному результату.

Комментарии:

1. Возможно, попробуйте использовать JSON в качестве транспорта? Это использует совершенно другую кодовую базу и может не так сильно перегружать большие целые числа при декодировании строки в 32-битной системе.

2. Можете ли вы преобразовать это во временную строковую переменную и сериализовать ее вместо этого? ($i = serialize(‘20110510134021’); фактически сериализует строку ..)

3. Согласно документации PHP, целые числа масштабируются до плавающих значений при переполнении: php.net/manual/en/language.types.integer.php .

4. @MarcB я унаследовал эти данные. Если бы это был мой выбор, я бы никогда не использовал эти ужасные методы сериализации.

5. @jlb У меня нет доступа к исходным данным, только к сериализованному результату.

Ответ №1:

Максимальное целое число в 32-разрядной системе равно 4294967296 ; $i = 20110510134021; работает, потому что PHP преобразует переменную в double .

Поэтому замените i на d

 $i = unserialize('d:20110510134021;');
 

Если вы запустите этот скрипт, вы увидите правильное представление переменной в системе, которую вы запускаете (d: в 32-разрядной системе, i: в 64-разрядной системе):

 $int = 20110510134021;
var_dump(serialize($int));
 

Комментарии:

1. Я как раз собирался попробовать это. Это работает. Я попрошу регулярное выражение просмотреть мои существующие сериализованные строки и изменить их. Спасибо.

Ответ №2:

Простое решение, если вы знаете, что существует вероятность того, что данные, сериализованные на 64-разрядной машине, будут несериализованы на 32-разрядной машине, — это преобразовать их в (double) перед сериализацией.

Тогда оно будет несериализовано как double, что даст вам больше бит на целое число, чем стандартное 4-байтовое целое число (32 бита)

После несериализации просто работайте с этим числом как с двойным. В 99% случаев это хорошее решение. Все еще существует вероятность того, что для очень большого числа количество битов, выделенных для «реальной» части числа в double на 32-разрядной машине, будет недостаточным. Я думаю, что это 56 бит, поэтому максимальное целое число все еще значительно больше, чем 32 бита для типа int.

Комментарии:

1. К сожалению, у меня нет доступа к исходным данным. Я унаследовал сериализованную форму и должен ее использовать.

Ответ №3:

Как насчет:

 $serialized = 'i:20110510134021';

if (preg_match('/i:([d] )/', $serialized, $match))
{
    $unserialized = $match[1];
}
 

Комментарии:

1. Это часть очень сложного объекта, содержащего массивы и строки, а что нет. Мне нужно иметь возможность отменить сериализацию этого объекта в целом.

2. Извините, мой плохой — удалил мой комментарий.

3. Кстати, являются ли исходные данные сериализованным массивом или объектом?