Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

unserialize() [function.unserialize]: Error at offset 49151 of 49151 bytes

Tags:

php

I have the offset error like many on the website after storing object into database and than retrieving it. If I don't store it everything works fine:

$serializedObject = serialize($this);
$unSerializedObject = unserialize($serializedObject);

Besides, I use base64 encoding when saving data and retrieving it from database, but this doesn't help. I don't do any escaping though. My object processes some string. What I've found out is that with this string:

A woman is travelling around the world. She is 28 years old and she is from Great Britain.
She cannot use a car or a plane on her

It works fine. But when I add one more space and word [ journey], the error pops up. Here is the string with this one word:

A woman is travelling around the world. She is 28 years old and she is from Great Britain.
She cannot use a car or a plane on her journey

My question is why does the error pop up?

Here is the output of the serialize($this) run against the text without the word journey

Here is the output of the serialize($this) run against the text with the word journey

UPDATE

The table I'm saving object into has charset utf-8 and the column with no charset defined since it's of BLOB type. The mb_detect_encoding(serialize($this)) returns UTF-8

There is no escaping for the $sql. This is how the query is executed inside Kohana framework that I'm using:

$result = mysql_query($sql, $this->_connection)
like image 240
Max Koretskyi Avatar asked Oct 19 '13 17:10

Max Koretskyi


1 Answers

Original answer:

A TEXT field in MySQL stores up to 65535 bytes, so my guess is it is being truncated there.

Use a MEDIUMTEXT or LONGTEXT instead.

In addition to that, there are potential issues with how you get the data into and out of the database. PHP serialized strings can contain null bytes (the byte 0) and that appears to be what is not getting transfered properly.

One way to work around that is to encode the string via something like base64_encode() that uses a very friendly alphanumeric/symbol alphabet. That will solve your problems if you increase your BLOB type to MEDIUMBLOB or LONGBLOB.

However, if you are properly sending your queries to the database you can safely send the original string. Since you are using Kohana, here is a sample that works perfectly fine for me.

Short version:

$sql = 'INSERT INTO serialized_object (data) VALUES (:data)';
DB::query(Database::INSERT, $sql)->
  param(':data', $serialization)->
  execute();

Code:

<?php 
class Article {}
class Word {}

class Controller_Welcome extends Controller
{
    public function action_index()
    {
        $object = unserialize(hex2bin(file_get_contents('/tmp/data.hex')));
        $serialization = serialize($object);

        $sql = 'INSERT INTO serialized_object (data) VALUES (:data)';
        DB::query(Database::INSERT, $sql)->
            param(':data', $serialization)->
            execute();

        $saved_length = DB::query(Database::SELECT, '
            SELECT LENGTH(data) AS l
            FROM serialized_object
            ORDER BY id DESC
            LIMIT 1
        ')->execute()->get('l');

        if ($saved_length != strlen($serialization))
        {
            throw new Exception("Database length is incorrect. Value is corrupted in database.");
        }

        $saved_serialization = DB::query(Database::SELECT, '
            SELECT data
            FROM serialized_object
            ORDER BY id DESC
            LIMIT 1
        ')->execute()->get('data');

        $saved_object = unserialize($saved_serialization);

        if (!$saved_object)
        {
            throw new Exception("Unable to unserialize object.");
        }

        if ($saved_object != $object)
        {
            throw new Exception("Saved object is not equal to object.");
        }

        $this->response->body('Everything is fine.');
    }

} 

database.php:

<?php

return array
(
    'default' => array(
        'type'       => 'PDO',
        'connection' => array(
            /**
             * The following options are available for PDO:
             *
             * string   dsn         Data Source Name
             * string   username    database username
             * string   password    database password
             * boolean  persistent  use persistent connections?
             */
            'dsn'        => 'mysql:host=127.0.0.1;dbname=test',
            'username'   => 'root',
            'password'   => '****',
            'persistent' => FALSE,
        ),
        /**
         * The following extra options are available for PDO:
         *
         * string   identifier  set the escaping identifier
         */
        'table_prefix' => '',
        'charset'      => 'utf8',
        'caching'      => FALSE,
    ),
);

Schema:

CREATE TABLE `serialized_object` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `data` longblob NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
like image 66
Matthew Avatar answered Oct 27 '22 01:10

Matthew