我在尝试反序列化数据时遇到错误。发生以下错误:
unserialize(): Error at offset 46 of 151 bytes
以下是序列化数据:
s:151:"a:1:{i:0;a:4:{s:4:"name";s:15:"Chloe O'Gorman";s:6:"gender";s:6:"female";s:3:"age";s:3:"3_6";s:7:"present";s:34:"Something from Frozen or a jigsaw ";}}";
该错误是由数据中的单个引号引起的。当我正在使用的网站和数据库已经上线时,如何缓解此问题?
不幸的是,我无法重写负责序列化数据并将其插入数据库的代码。很可能在整个数据库中多次出现此问题。
是否有可用于转义数据的函数?
在进行了进一步的研究之后,我找到了解决方法。根据这篇博文:
"事实证明,如果有一个"、'、:或 ;在任何数组中 值序列化已损坏。
如果我在一个尚未上线的网站上工作,一种预防方法是在序列化数据存储到数据库中之前base64_encode
我的序列化数据,如下所示:
base64_encode( serialize( $my_data ) );
然后:
unserialize( base64_decode( $encoded_serialized_string ) );
检索数据时。
但是,由于我无法更改数据库中已经存储的内容,因此这篇非常有用的帖子(原始帖子不再可用,但看起来像这样)提供了一种解决此问题的解决方案:
$fixed_serialized_data = preg_replace_callback ( '!s:('d+):"(.*?)";!', function($match) {
return ($match[1] == strlen($match[2])) ? $match[0] : 's:' . strlen($match[2]) . ':"' . $match[2] . '";';
}, $my_data );
$result = unserialize( $fixed_serialized_data );
据我所知,您有一个有效的序列化字符串嵌套在有效的序列化字符串中 - 这意味着serialize()
在形成您发布的字符串时被调用了两次。
看看你是如何s:151:
,然后是:
"a:1:{i:0;a:4:{s:4:"name";s:15:"Chloe O'Gorman";s:6:"gender";s:6:"female";s:3:"age";s:3:"3_6";s:7:"present";s:34:"Something from Frozen or a jigsaw ";}}";
⮤ 是包含预序列化数据的有效单个字符串。
反序列化 THAT 后,您将获得:
a:1:{i:0;a:4:{s:4:"name";s:15:"Chloe O'Gorman";s:6:"gender";s:6:"female";s:3:"age";s:3:"3_6";s:7:"present";s:34:"Something from Frozen or a jigsaw ";}}
// ^^--^^^^^^^^^^^^^^-- uh oh, that string value has 14 bytes/characters not 15
它看起来像在字符串处理中的某个地方,并且删除了转义斜杠并损坏了字符串。
序列化数据中的单引号没有任何污点。
您可以选择:
- 执行转义调用以盲目地将斜杠应用于字符串中的所有单引号(这可能会导致其他地方的损坏)——假设您想转义项目的后续进程的单引号或
- 执行我的以下代码片段,它不会转义单引号,而是调整字节/字符计数以形成有效的
代码:(演示)
$corrupted_byte_counts = <<<STRING
s:151:"a:1:{i:0;a:4:{s:4:"name";s:15:"Chloe O'Gorman";s:6:"gender";s:6:"female";s:3:"age";s:3:"3_6";s:7:"present";s:34:"Something from Frozen or a jigsaw ";}}";
STRING;
$repaired = preg_replace_callback(
'/s:'d+:"(.*?)";/s',
function ($m) {
return 's:' . strlen($m[1]) . ":'"{$m[1]}'";";
},
unserialize($corrupted_byte_counts) // first unserialize string before repairing
);
echo "corrupted serialized array:'n$corrupted_byte_counts";
echo "'n---'n";
echo "repaired serialized array:'n$repaired";
echo "'n---'n";
print_r(unserialize($repaired)); // unserialize repaired string
echo "'n---'n";
echo serialize($repaired);
输出:
corrupted serialized array:
s:151:"a:1:{i:0;a:4:{s:4:"name";s:15:"Chloe O'Gorman";s:6:"gender";s:6:"female";s:3:"age";s:3:"3_6";s:7:"present";s:34:"Something from Frozen or a jigsaw ";}}";
---
repaired serialized array:
a:1:{i:0;a:4:{s:4:"name";s:14:"Chloe O'Gorman";s:6:"gender";s:6:"female";s:3:"age";s:3:"3_6";s:7:"present";s:34:"Something from Frozen or a jigsaw ";}}
---
Array
(
[0] => Array
(
[name] => Chloe O'Gorman
[gender] => female
[age] => 3_6
[present] => Something from Frozen or a jigsaw
)
)
---
s:151:"a:1:{i:0;a:4:{s:4:"name";s:14:"Chloe O'Gorman";s:6:"gender";s:6:"female";s:3:"age";s:3:"3_6";s:7:"present";s:34:"Something from Frozen or a jigsaw ";}}";
*请记住,如果要将数据返回到其原始的 Matryoshka 序列化形式,则需要在 $repaired
上再次调用 serialize()
。
**如果您有包含";
的子字符串,您可以尝试我的代码段的扩展版本。
发布的序列化文本没有错。里面的引号不需要转义,因为 PHP 使用类型长度指示符来找出事情开始/停止的位置。例如
php > $foo = "This string contains a '" quote and a ' quote";
php > $bar = serialize($foo);
php > $baz = unserialize($bar);
php > echo "$foo'n$bar'n$baz'n";
This string contains a " quote and a ' quote
s:44:"This string contains a " quote and a ' quote";
This string contains a " quote and a ' quote
请注意,序列化字符串中缺少任何类型的转义 - 字符串中的引号按原样存在,没有引号,没有转义,没有编码。
发布后,序列化数据将正确反序列化为纯 JSON 字符串,而不会出现问题。
php nowdoc
unserialize(<<<'DDDD'
[SERIALIZE_STR]
DDDD
);