2

I'm trying to unserialize a saved instance state of an object but my serialized object cannot be resumed due to an "error at offset" error.

This applies to all objects I try to unserialize, even the most simplest of objects.

class Object
{
    protected $variable = true;
}

$object = serialize(new Object());
$string = 'O:6:"Object":1:{s:11:"*variable";b:1;}';

echo $object."\n";
echo "length: ". strlen($object)."\n\n";
echo $string . "\n"; // Strangely 2 characters shorter than $object
echo "length: ". strlen($string)."\n";

unserialize($object); // Works
unserialize($string); // Does not work

This code outputs:

O:6:"Object":1:{s:11:"*variable";b:1;}
length: 40 

O:6:"Object":1:{s:11:"*variable";b:1;} 
length: 38 

Notice: unserialize(): Error at offset 33 of 38 bytes

I'm stuck, why can I not unserialize saved strings?

Daniel
  • 3,726
  • 4
  • 26
  • 49
  • @Baba Ok, missed that question. I'll try to encode to base64 before saving. – Daniel Mar 22 '13 at 16:41
  • + for observation .... looking at your question .. this could be a common mistake .... you can also use `hex` to save – Baba Mar 22 '13 at 16:48

2 Answers2

1

The two missing characters are null bytes that are used for the protected attribute. You cannot see them but they are still there. Thus, your $string is just not a valid serialization.

Update:

Actually the null bytes can be made visible (with the invalid character symbol) in UTF-8 encoding. You can see it in this demo if you select Output: Textbox

string(40) "O:6:"Object":1:{s:11:"�*�variable";b:1;}"
Fabian Schmengler
  • 24,155
  • 9
  • 79
  • 111
  • Ok thank you, suggested solution [here](http://stackoverflow.com/questions/10152904/unserialize-function-unserialize-error-at-offset) is to encode to base64 before saving. – Daniel Mar 22 '13 at 16:40
  • Why would you encode it with base64? Just store it in a _binary_ column, not a _char_ column. – Wrikken Mar 22 '13 at 16:43
  • base64 is a viable solution if you have to transfer the string encoding neutral. However, you did not show how you save the values. base64 surely will work but maybe there is a better way. – Fabian Schmengler Mar 22 '13 at 16:47
  • @fab encoding to base64 worked like a charm! Wrikken: That is the plan. But that is not my question, my question is why the strings does not work. – Daniel Mar 22 '13 at 16:49
1

The * ("\x2A") is actually terminated by 2 non-visible null bytes ("\x0\x2A\x0"). This means that you have to treat it as BINARY (which also means in database storing in BINARY/BLOB columns rather then CHAR/TEXT columns). Moral of the story is: PHP reserves to right to alter serialization methods at any time, so do not use anything BUT php to serialize strings, and treat serialized strings as BINARY rather then character data.

Wrikken
  • 69,272
  • 8
  • 97
  • 136
  • If PHP were to _“alter serialization methods at any time”_ – then I would probably not be able to unserialize something that I serialized a year ago now … even if I stuck to just using PHP and nothing else to do it. – CBroe Mar 22 '13 at 16:49
  • That doesn't make sense, the result of serialization is not an implementation detail but part of the interface. It will *not* change without good cause. So, `$string` is not the result of a changed serialization method, it was just not stored correctly. – Fabian Schmengler Mar 22 '13 at 16:52
  • That _is_ unlikely yes, BUT it can make `unserialize()` detect the format & unserialize() as necessary, but make `serialize()` (==new data) only use a new format. I grant you that the chances of them _ever_ changing it are slim. But the meat of my answer was more: treat as binary, but I could have worded that better. – Wrikken Mar 22 '13 at 16:54
  • "But the meat of my answer was more: treat as binary" - with that I happily agree – Fabian Schmengler Mar 22 '13 at 16:57
  • As I said, the changes are very slim, and most likely backwards compatible (there already have been changes, for instance [check this example in PHP4](http://sandbox.onlinephpfunctions.com/code/2675d9ecc581d7d6d547df74ebf6def8c53313cd) and see what a 'weird' property it creates, as PHP4 had no notion of protected/private). – Wrikken Mar 22 '13 at 17:05