I have been trying to unserialize on a 32-bit server an object that was serialized on a 64-bit server. I have isolated my problem to an integer in the object. Here is a small reproduction of the problem.
On a 64-bit machine:
$i = serialize('20110510134021'); //i:20110510134021;
On a 32-bit machine:
$i = unserialize('i:20110510134021;');
Gives the error
Notice: unserialize(): Error at offset 0 of 16 bytes
Now I understand that those serialization method shouldn't be used for cross system data transmition. However, we are merely trying to migrate the data to another system, not use it actively for transfers. This is a one time thing.
I would think that this might be due to integer overflow, but even on the 32-bit server I can do something like
$i = 20110510134021;
echo $i;
And it will work fine. I'm guessing PHP integer types scale to some double type or something like that. But why doesn't it do that when it unserializes?
How can I unserialize those objects? If I can't, is there any way to convert them to something else? Finally, has anyone written a deserialize method in PHP itself? Or has details on the protocol? I could use that and have a custom case for those integers.
Thanks.
Note: I do not have access to the original data, only the serialized result.
The max integer on 32 bit system is 4294967296
; $i = 20110510134021;
works because PHP converts the variable to double.
So replace i
with d
$i = unserialize('d:20110510134021;');
If you run this script, you will see the correct represenation of the variable on the system you are running (d: on 32 bit system, i: on 64 bit system):
$int = 20110510134021;
var_dump(serialize($int));
The simple solution is if you know that there is a chance that data serialized on 64 bit will be unserialized on 32 bit machine is to cast it to (double) before serializing.
Then it will be unserialized as a double, giving you more bits per integer than a standard 4-byte per integer (32 bits)
Once unserialized just work with that number as a double. In 99% of the cases this is a good solution. There is still a chance that for a very large number the number of bits allocated to the 'real' part on the number to a double on a 32 bit machine will not be enough. I think it's 56 bits, so the maximum integer is still significantly larger that the 32bits for the int type.
How about:
$serialized = 'i:20110510134021';
if (preg_match('/i\:([\d]+)/', $serialized, $match))
{
$unserialized = $match[1];
}