mysql unserialize_php unserialize返回false 报错,不能范序列化

我从用户那里取到的缓存的序列化数据为:

a:1:{s:8:"kl_album";a:4:{s:5:"title";s:12:"精彩瞬

间";s:3:"url";s:41:"http://www.kaisay.cn/?plugin=kl_album";s:8:"is_blank";s:7:"_parent";s:4:"hide";s:1:"n";}}

咋一看了解序列化的人都会说,这个数据很正常啊,没什么问题呢。可是直接把这段字符串进行unserialize,返回的值却是个False;

代码

var_dump(unserialize('a:1:{s:8:"kl_album";a:4:{s:5:"title";s:12:"精彩瞬间";s:3:"url";s:41:"http://www.kaisay.cn/?plugin=kl_album";s:8:"is_blank";s:7:"_parent";s:4:"hide";s:1:"n";}}'));

a4c26d1e5885305701be709a3d33442f.png

运行结果

a4c26d1e5885305701be709a3d33442f.png

问题出在哪里呢?答案是 s:41:"http://www.kaisay.cn/?plugin=kl_album"

序列化字符串中标定该字符串http://www.kaisay.cn/?plugin=kl_album的长度是41,可是我们自己数一下却只有37个字符。就是因为这个问题,导致php反序列化字符串失效。

如果将字符串长度改成37,那么程序就会顺利的反序列化

代码:

var_dump(unserialize('a:1:{s:8:"kl_album";a:4:{s:5:"title";s:12:"精彩瞬

间";s:3:"url";s:37:"http://www.kaisay.cn/?plugin=kl_album";s:8:"is_blank";s:7:"_parent";s:4:"hide";s:1:"n";}}'));

a4c26d1e5885305701be709a3d33442f.png

通过google后才发现,这个问题国外已经很多的网友遇到了,在官方手册unserialize函数页面的评论中就有很多网友在讨论和研究这个问题的解决方案。

这种情况发生的原因有多种可能,最大的可能就是在序列化数据的时候的编码和反序列化时的编码不一样导致字符串的长度出现偏差。例如数据库编码latin1和UTF-8字符长度是不一样的。

解决方案:

自己用php来纠正序列化字符串中字符串长度的问题,链接

$unserialized=preg_replace('!s:(\d+):"(.*?)";!se',"'s:'.strlen('$2').':\"$2\";'",$unserialized);?>

另外一个网友提出一个在非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);

// 根据上面的情况我写出的测试例子(注意代码必须用asc格式保存运行)

$test = 's:7:"hahaha'. chr(0) .'";';

echo preg_replace('!s:(\d+):"(.*?)";!se',

"'s:'.strlen('$2').':\"$2\";'", $test );

echo '
';

echo preg_replace('!s:(\d+):"(.*?)";!se',

'"s:".strlen("$2").":\"$2\";"', $test );

echo '
';

echo unserialize(preg_replace('!s:(\d+):"(.*?)";!se',

"'s:'.strlen('$2').':\"$2\";'", $test ));

echo '
';

echo unserialize(preg_replace('!s:(\d+):"(.*?)";!se',

'"s:".strlen("$2").":\"$2\";"', $test ));

下图是运行结果:很显然chr(0) 变成了 \0

a4c26d1e5885305701be709a3d33442f.png

还有一个情况就是单双引号也会出现长度计算错误的问题:链接

// 请看例子的值. 数据从来那里来都没有关系,无论是数据库还是代码中$heightoptionslist=

<<<3:>

(eg. 13'

2"HH)";s:6:"option";s:25:"Inches only

(eg.39")";s:6:"option";s:24:"Centimeters (eg.

153cms)";s:6:"option";}HEIGHTEND;//--------------------------------------------

// 将序列化的字符串转换回数组$heightoptionslist=unserialize($heightoptionslist);//--------------------------------------------

// 打印值,我们可以很容易看懂echo"

\$heightoptionslist

= [\n".print_r($heightoptionslist,true)."\n]

";?>3:>

当字符串中带有没有转换的引号的时候,就会出问题了:

// 另外一个例子,

// 这次有没有转换过的引号在里面$heightoptionslist=

<<<3:>

(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"

\$heightoptionslist

= [\n".print_r($heightoptionslist,true)."\n]

";?>3:>

以上两端代码运行结果:

a4c26d1e5885305701be709a3d33442f.png

在字符串还有\r字符的时候计算字符串的长度的时候也会出现问题:链接

When dealing with a string which contain

"\r", it seems that the length is not evaluated correctly. The

following solves the problem for me :

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);

}

希望本文能给所有遇到该问题的朋友一点帮助

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值