将序列化对象存储在MySql数据库中

12

我有一个很大的PHP对象,我想将其序列化并存储在MySQL数据库中。表格编码为UTF-8,用于保存序列化对象编码的列也是UTF-8

问题是该对象包含一个包含法语字符的文本字符串。

例如:

Merci d'avoir passé commande avec Lovre. Voici le récapitulatif de votre commande 

当我将对象序列化并直接反序列化时,字符串保持不变且格式正确。

然而,当我将序列化后的对象存储到MySQL数据库中,然后再次检索并反序列化时,该字符串会变成这样:

Merci d'avoir passé commande avec Lovre. Voici le récapitulatif de votre commande 

当我将对象存储到数据库中时,出现了一些问题。

注:

  • 使用propel ORM存储对象。
  • 列类型为text
  • 字符串从html文件中存储和读取。

你的文件采用什么编码格式? - alexn
你可以尝试对其进行base_64编码,但你不应该这样做。数据库列是什么类型?你有检查过php中的数据库连接设置吗? - The Silencer
@TheSilencer 数据库列类型为文本。数据库连接是使用 PROPEL 完成的。 - Songo
@alexn 字符串存储在HTML文件中。 - Songo
4个回答

16
serialize创建的字符串是二进制字符串,它们没有特定的字符集编码,只是一个字节的“数组”(其中一个字节是8位,即一个八位组)。
如果你现在拿这样一个字符串告诉你的数据库它是LATIN-1编码的,并且你的数据库将其存储到UTF-8编码的文本字段中,数据库将自动将编码从LATIN-1更改为UTF-8。UTF-8是一种字符集编码,对于某些字符使用多个字节表示一个字符,例如你在问题中提到的é
然后将字符é存储为é在数据库中,这是é的UTF-8字节序列。
如果你现在从数据库中获取数据而没有指定需要哪种编码,数据库将以UTF-8返回它。
现在unserialize有一个问题,因为二进制字符串已经被修改,使其无效。

相反,您需要告诉数据库在存储序列化字符串时不要修改编码,例如通过选择正确的列类型和编码(二进制字段,BLOB - 二进制大对象MySQL文档,也可以参考二进制类型Propel文档) -或者- 在从数据库获取数据时将字符集编码恢复为原始格式。第一种方法(二进制字段)更好,因为这正是您要寻找的。

对于已经以错误格式存储到数据库中的数据,您需要纠正数据。为此,您首先需要找出应用了哪种重新编码,例如从哪种字符集到哪种字符集。我假设它是LATIN-1,但不能保证。您需要检查当前应用程序数据和流程的编码以找出答案。

在找到答案后,将值从UTF-8重新编码为原始编码。


我尝试了你说的并把列类型转换成了BLOB,但问题仍然存在。不过,在从数据库检索对象后,我决定对消息本身进行utf_decode处理,这解决了问题。 - Songo
它是否对现有数据和/或新数据持久存在?此外,我不是专业人士,但我相信数据库层必须有一个简单的解决方案,因此您无需关心应用程序逻辑代码中的编码。如果您无法在数据库层解决它,则 PHP 中还有 Serializable 接口可能有助于保持代码清洁。 - hakre
我目前还处于开发阶段,因此没有任何现有数据。我检查了 PROPEL 配置并确信它在连接中使用 UTF-8。也许我在这方面错过了什么,但我会研究你提到的 Serializable 接口。感谢你的帮助。 - Songo

5

请确保在任何地方都使用 utf-8 - 听起来你可能漏掉了一些东西。

在您的情况下,我认为您忘记了为数据库连接设置正确的字符集(使用SET NAMES语句或mysql_set_charset())- 但是没有看到您的代码很难说(我也不知道propel)。

以下是来自chazomaticus的引用,在UTF-8 all the way through中给出了完美的答案,列出了您需要注意的所有要点:

存储:

  • 在数据库中的所有表和文本列上指定utf8_unicode_ci(或等价)排序规则。这将使MySQL原生地以UTF-8存储和检索值。

检索:

  • 在PHP中,无论您使用哪个DB包装器,都需要将连接字符集设置为utf8。这样,当MySQL将数据交给PHP时,它就不会从其原生UTF-8进行转换。 * 请注意,如果您不使用DB包装器,则可能必须发出查询来告诉MySQL以UTF-8提供结果:SET NAMES 'utf8'(一旦连接)。

传递:

  • 您必须告诉PHP向客户端提供正确的标题,以便文本被解释为UTF-8。在PHP中,您可以使用default_charset php.ini选项,或手动发出Content-Type标题本身,这仅仅是更多的工作,但具有相同的效果。

提交:

  • 您希望浏览器发送给您的所有数据都是UTF-8格式的。不幸的是,可靠地实现这一点的唯一方法是将accept-charset属性添加到所有<form>标记中:<form ... accept-charset="UTF-8">
  • 请注意,W3C HTML规范指出,客户端“应该”默认将表单发送回服务器以服务器提供的任何字符集,但这显然只是一个建议,因此需要在每个单独的<form>标记上明确指定。
  • 尽管如此,在此方面上,您仍然需要验证每个提交的字符串是否为有效的UTF-8,然后再尝试在任何地方存储或使用它。PHP的mb_check_encoding()可以完成此操作,但必须要严格使用。

处理:

  • 不幸的是,这是最困难的部分。您需要确保每次处理UTF-8字符串时都能安全地进行。最简单的方法是广泛利用PHP的mbstring扩展。
  • PHP的字符串操作默认情况下不是UTF-8安全的。某些事情您可以通过常规PHP字符串操作来安全地完成(如串联),但对于大多数事情,您应该使用等效的mbstring函数。
  • 要知道您正在做什么(即:不要把它搞砸),您确实需要了解UTF-8以及它在可能的最低级别上的工作原理。从utf8.com中选择任何链接,以获取您需要了解的所有信息的好资源。
  • 此外,我觉得应该在某个地方提到这一点,即使它似乎很明显:您将提供的每个PHP或HTML文件都应编码为有效的UTF-8。

请注意,您不必使用utf-8——重要的是在任何情况下都使用相同的字符集。但如果您需要进行更改,请使用utf-8。


2

我经常使用base64_encode()来存储序列化数据。有时候,序列化数据会出现问题,但是使用它的base64值后,只剩下简单的字符。


1
我强烈推荐你使用 json_encode而不是serialize。有一天,你会发现自己试图从另一个不是PHP的地方使用那些数据,并且将其存储在JSON中使其可读性非常高,几乎每种语言都支持解码JSON并且它是一个被广泛认可的标准。

关于到处使用utf8的回答是正确的!:-D


2
不是一个好主意:1.它将数组转换为对象,2.类类型和方法都会丢失。 - Elia Weiss
方法无论如何都会丢失……你不能通过serialize调用来保存它们。 相信我,将序列化的东西存储起来是一个更糟糕的想法.. 你最终需要从其他地方读取那些东西。 如果你需要反序列化回一个类,只需将类型存储为字符串,然后在之后切换以使用json数据作为字段实例化正确的类,而不是返回纯json,就像任何ORM基本上为DB记录所做的那样。 - Ariel Scarpinelli
1
在某些情况下,JSON可能更好。然而,这里的问题是如何序列化PHP对象,因此你可以继续使用PHP,并且无法将PHP对象转换为JSON,因为那样你就无法对其进行反序列化/ json_decode,从而无法重新创建原始的PHP对象。 - Jan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接