【问题标题】:unserialize() [function.unserialize]: Error at offset 49151 of 49151 bytesunserialize() [function.unserialize]:49151 字节的偏移量 49151 处出错
【发布时间】:2013-10-28 10:37:35
【问题描述】:

在将对象存储到数据库并检索它之后,我在网站上遇到了许多偏移错误。如果我不存储它一切正常:

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

此外,我在保存数据并从数据库中检索数据时使用 base64 编码,但这无济于事。不过,我不做任何逃避。 我的对象处理一些字符串。我发现用这个字符串:

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

它工作正常。但是当我再添加一个空格和单词[旅程]时,就会弹出错误。这是带有这个单词的字符串:

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

我的问题是为什么会弹出错误?

Hereserialize($this) 对没有单词journey 的文本运行的输出

Hereserialize($this) 对带有单词journey 的文本运行的输出

更新

我保存对象的表有字符集utf-8 和没有定义字符集的列,因为它是 BLOB 类型。 mb_detect_encoding(serialize($this)) 返回UTF-8

$sql 没有转义。这就是我正在使用的 Kohana 框架中执行查询的方式:

$result = mysql_query($sql, $this->_connection)

【问题讨论】:

  • 这个函数只检查简单的可变长度,它不适用于对象的私有/公共属性。
  • @dev-null-dweller,所以如果我的对象具有私有属性,那么您的意思是它显示不正确的数据?
  • @Maximus,您提到您正在使用 Kohana。您是否通过扩展 Kohana_ORM 记录的对象保存它?另外,你能把echo serialize($this)echo bin2hex(serialize($this))的内容粘贴进去吗? (通过 pastebin.com 的东西,如果它真的很大的话。)
  • @Matthew,不,我正在手动操作DB::query(Database::INSERT, $sql)->execute();Here is 粘贴的序列化对象。
  • @Maximus,是的,UTF8 字符在存储在 blob/文本中时可能需要 2 个或更多字节。 (对于大小以字符为单位的 CHAR/VARCHAR 而言,情况并非如此。好吧,它仍然会消耗这些额外的字节,但这已计入 VARCHAR 的长度中。)

标签: php


【解决方案1】:

原答案:

MySQL 中的 TEXT 字段最多可存储 65535 个字节,所以我猜它在那里被截断了。

请改用 MEDIUMTEXT 或 LONGTEXT。

除此之外,如何将数据输入和输出数据库还存在潜在问题。 PHP 序列化字符串可以包含空字节(字节 0),这似乎是未正确传输的内容。

解决这个问题的一种方法是通过base64_encode() 之类的东西对字符串进行编码,它使用非常友好的字母数字/符号字母表。如果您将BLOB 类型增加到MEDIUMBLOBLONGBLOB,这将解决您的问题。

但是,如果您正确地将查询发送到数据库,则可以安全地发送原始字符串。由于您使用的是 Kohana,因此这里有一个非常适合我的示例。

短版:

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

代码:

<?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.');
    }

} 

数据库.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,
    ),
);

架构:

CREATE TABLE `serialized_object` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `data` longblob NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

【讨论】:

  • 感谢您的回复,但在我什至将对象存储到数据库之前就检测到了问题。如果您在谈论字段类型,我在数据库中使用 BLOB 字段。
  • 仅供参考:TINYTEXT 存储 254 字节,TEXT 存储 64KiB 减去 2 字节,MEDIUMTEXT 存储 16 MiB 减去 3 字节,LONGTEXT 存储 4 GiB 减去 4 字节。跨度>
  • Blob 具有相同的存储大小。保存到数据库时,您可能没有正确转义数据。我没有看到任何证据表明您的反序列化在使用数据库之外失败。
【解决方案2】:

用这种方法解决了问题:

$toDatabse = base64_encode(serialize($data));  // Save to database
$fromDatabase = unserialize(base64_decode($data)); //Getting Save Format 

但问题仍然存在,Baba's function findSerializeError 发现的错误的根本原因是什么?

【讨论】:

  • 这意味着您没有将数据正确地保存到数据库中。 base64_encode 仅使用“安全”字符,这可能会解决您进入数据库时​​发生的错误编码。
  • @Matthew,那么我该如何找出问题所在?
  • 用两件事更新您的问题:如何连接到数据库(密码/用户名无关紧要)以及如何发送查询。我猜要么字符集错误,要么你没有转义数据(例如,mysql_real_escape_string)。
  • @Matthew,我用有关字符集的信息更新了我的问题。我正在使用 Kohana 框架,所以我需要一些时间来弄清楚它如何连接到数据库并发送查询。你是对的,我没有转义数据,我应该在哪里做呢?
猜你喜欢
  • 2017-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多