a. 序列化数据的时候的编码和反序列化时的编码不一样导致字符串的长度出现偏差
解决方案:
$unserialized = preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $unserialized );
b. a中,在非utf-8情况下的BUG
ascii字符 "\0" 被解析成了 '\0' (\0在C中是字符串的结束符等于chr(0),错误解析后算了2个字符)
// 错误
$error = preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $unserialized );
// 正确
$works = preg_replace('!s:(\d+):"(.*?)";!se', '"s:".strlen("$2").":\"$2\";"', $unserialized );
单双引号也会出现长度计算错误的问题
// 请看例子的值. 数据从来那里来都没有关系,无论是数据库还 是代码中
$heightoptionslist = <<<HEIGHTEND
a:3:{s:26:"Hands/inches (eg. 13\' 2\"HH)";s:6:"option";s:20:"Inches only (eg.39\")";s:6:"option";s:24:"Centimeters (eg. 153cms)";s:6:"option";}
HEIGHTEND;
// 将序列化的字符串转换回数组
$heightoptionslist = unserialize($heightoptionslist);
// 打印出来,很容易看懂
echo "<div><pre>\$heightoptionslist = [\n".print_r($heightoptionslist, true)."\n]</pre></div>";
//当字符串中带有没有转换的引号的时候,就会出问题:
// 这次有没有转换过的引号在里面
$heightoptionslist = <<<HEIGHTEND
a:3:{s:26:"Hands/inches (eg. 13\' 2\"HH)";s:6:"option";s:20:"Inches only (eg.39\")";s:6:"option";s:24:"Centimeters (eg. 153cms)";s:6:"option";}
HEIGHTEND;
//--------------------------------------------
// 将序列化的字符串转换回数组.
$heightoptionslist = unserialize($heightoptionslist);
//--------------------------------------------
// 返回了一个空的结果.
echo "<div><pre>\$heightoptionslist = [\n".print_r($heightoptionslist, true)."\n]</pre></div>";
在字符串还有\r字符的时候计算字符串的长度的时候也会出现问题
// remove the \r caracters from the $unserialized string
$unserialized = str_replace("\r","",$unserialized);
// and then unserialize()
unserialize($unserialized);
解决方案
// UTF-8 下
function mb_unserialize($serial_str) {
$serial_str= preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $serial_str );
$serial_str= str_replace("\r", "", $serial_str);
return unserialize($serial_str);
}
// ASC 下
function asc_unserialize($serial_str) {
$serial_str = preg_replace('!s:(\d+):"(.*?)";!se', '"s:".strlen("$2").":\"$2\";"', $serial_str );
$serial_str= str_replace("\r", "", $serial_str);
return unserialize($serial_str);
}