php 如何修复因字节计数长度不正确而损坏的序列化字符串?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10152904/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to repair a serialized string which has been corrupted by an incorrect byte count length?
提问by user576820
I am using Hotaru CMS with the Image Upload plugin, I get this error if I try to attach an image to a post, otherwise there is no error:
我将 Hotaru CMS 与图像上传插件一起使用,如果我尝试将图像附加到帖子中,则会出现此错误,否则不会出现错误:
unserialize() [function.unserialize]: Error at offset
unserialize() [function.unserialize]:偏移量错误
The offending code (error points to line with **):
违规代码(错误指向**):
/**
* Retrieve submission step data
*
* @param $key - empty when setting
* @return bool
*/
public function loadSubmitData($h, $key = '')
{
// delete everything in this table older than 30 minutes:
$this->deleteTempData($h->db);
if (!$key) { return false; }
$cleanKey = preg_replace('/[^a-z0-9]+/','',$key);
if (strcmp($key,$cleanKey) != 0) {
return false;
} else {
$sql = "SELECT tempdata_value FROM " . TABLE_TEMPDATA . " WHERE tempdata_key = %s ORDER BY tempdata_updatedts DESC LIMIT 1";
$submitted_data = $h->db->get_var($h->db->prepare($sql, $key));
**if ($submitted_data) { return unserialize($submitted_data); } else { return false; }**
}
}
Data from the table, notice the end bit has the image info, I am not an expert in PHP so I was wondering what you guys/gals might think?
表格中的数据,注意末尾有图像信息,我不是 PHP 专家,所以我想知道你们/女孩会怎么想?
tempdata_value:
临时数据值:
a:10:{s:16:"submit_editorial";b:0;s:15:"submit_orig_url";s:13:"www.bbc.co.uk";s:12:"submit_title";s:14:"No title found";s:14:"submit_content";s:12:"dnfsdkfjdfdf";s:15:"submit_category";i:2;s:11:"submit_tags";s:3:"bbc";s:9:"submit_id";b:0;s:16:"submit_subscribe";i:0;s:15:"submit_comments";s:4:"open";s:5:"image";s:19:"C:fakepath100.jpg";}
Edit: I think I've found the serialize bit...
编辑:我想我已经找到了序列化位...
/**
* Save submission step data
*
* @return bool
*/
public function saveSubmitData($h)
{
// delete everything in this table older than 30 minutes:
$this->deleteTempData($h->db);
$sid = preg_replace('/[^a-z0-9]+/i', '', session_id());
$key = md5(microtime() . $sid . rand());
$sql = "INSERT INTO " . TABLE_TEMPDATA . " (tempdata_key, tempdata_value, tempdata_updateby) VALUES (%s,%s, %d)";
$h->db->query($h->db->prepare($sql, $key, serialize($h->vars['submitted_data']), $h->currentUser->id));
return $key;
}
回答by Baba
unserialize() [function.unserialize]: Error at offsetwas dues to invalid serialization datadue to invalid length
unserialize() [function.unserialize]: Error at offset是invalid serialization data由于长度无效造成的
Quick Fix
快速解决
What you can do is is recalculating the lengthof the elements in serialized array
你可以做的是recalculating the length序列化数组中的元素
You current serialized data
您当前的序列化数据
$data = 'a:10:{s:16:"submit_editorial";b:0;s:15:"submit_orig_url";s:13:"www.bbc.co.uk";s:12:"submit_title";s:14:"No title found";s:14:"submit_content";s:12:"dnfsdkfjdfdf";s:15:"submit_category";i:2;s:11:"submit_tags";s:3:"bbc";s:9:"submit_id";b:0;s:16:"submit_subscribe";i:0;s:15:"submit_comments";s:4:"open";s:5:"image";s:19:"C:fakepath100.jpg";}';
Example without recalculation
没有重新计算的示例
var_dump(unserialize($data));
Output
输出
Notice: unserialize() [function.unserialize]: Error at offset 337 of 338 bytes
Recalculating
重新计算
$data = preg_replace('!s:(\d+):"(.*?)";!e', "'s:'.strlen('').':\"\";'", $data);
var_dump(unserialize($data));
Output
输出
array
'submit_editorial' => boolean false
'submit_orig_url' => string 'www.bbc.co.uk' (length=13)
'submit_title' => string 'No title found' (length=14)
'submit_content' => string 'dnfsdkfjdfdf' (length=12)
'submit_category' => int 2
'submit_tags' => string 'bbc' (length=3)
'submit_id' => boolean false
'submit_subscribe' => int 0
'submit_comments' => string 'open' (length=4)
'image' => string 'C:fakepath100.jpg' (length=17)
Recommendation.. I
推荐..我
Instead of using this kind of quick fix ... i"ll advice you update the question with
而不是使用这种快速修复......我会建议你更新问题
How you are serializing your data
How you are Saving it ..
您如何序列化数据
你是如何保存它的..
================================ EDIT 1 ===============================
================================ 编辑 1 ================ ================
The Error
错误
The Error was generated because of use of double quote "instead single quote 'that is why C:\fakepath\100.pngwas converted to C:fakepath100.jpg
由于使用双引号"而不是单引号而生成错误',这C:\fakepath\100.png就是转换为C:fakepath100.jpg
To fix the error
修复错误
You need to change $h->vars['submitted_data']From (Note the singe quite ')
你需要改变$h->vars['submitted_data']From (注意单曲')
Replace
代替
$h->vars['submitted_data']['image'] = "C:\fakepath0.png" ;
With
和
$h->vars['submitted_data']['image'] = 'C:\fakepath0.png' ;
Additional Filter
附加过滤器
You can also add this simple filter before you call serialize
您还可以在调用序列化之前添加这个简单的过滤器
function satitize(&$value, $key)
{
$value = addslashes($value);
}
array_walk($h->vars['submitted_data'], "satitize");
If you have UTF Characters you can also run
如果你有 UTF 字符,你也可以运行
$h->vars['submitted_data'] = array_map("utf8_encode",$h->vars['submitted_data']);
How to detect the problem in future serialized data
如何检测未来序列化数据中的问题
findSerializeError ( $data1 ) ;
Output
输出
Diffrence 9 != 7
-> ORD number 57 != 55
-> Line Number = 315
-> Section Data1 = pen";s:5:"image";s:19:"C:fakepath100.jpg
-> Section Data2 = pen";s:5:"image";s:17:"C:fakepath100.jpg
^------- The Error (Element Length)
findSerializeErrorFunction
findSerializeError功能
function findSerializeError($data1) {
echo "<pre>";
$data2 = preg_replace ( '!s:(\d+):"(.*?)";!e', "'s:'.strlen('').':\"\";'",$data1 );
$max = (strlen ( $data1 ) > strlen ( $data2 )) ? strlen ( $data1 ) : strlen ( $data2 );
echo $data1 . PHP_EOL;
echo $data2 . PHP_EOL;
for($i = 0; $i < $max; $i ++) {
if (@$data1 {$i} !== @$data2 {$i}) {
echo "Diffrence ", @$data1 {$i}, " != ", @$data2 {$i}, PHP_EOL;
echo "\t-> ORD number ", ord ( @$data1 {$i} ), " != ", ord ( @$data2 {$i} ), PHP_EOL;
echo "\t-> Line Number = $i" . PHP_EOL;
$start = ($i - 20);
$start = ($start < 0) ? 0 : $start;
$length = 40;
$point = $max - $i;
if ($point < 20) {
$rlength = 1;
$rpoint = - $point;
} else {
$rpoint = $length - 20;
$rlength = 1;
}
echo "\t-> Section Data1 = ", substr_replace ( substr ( $data1, $start, $length ), "<b style=\"color:green\">{$data1 {$i}}</b>", $rpoint, $rlength ), PHP_EOL;
echo "\t-> Section Data2 = ", substr_replace ( substr ( $data2, $start, $length ), "<b style=\"color:red\">{$data2 {$i}}</b>", $rpoint, $rlength ), PHP_EOL;
}
}
}
A better way to save to Database
保存到数据库的更好方法
$toDatabse = base64_encode(serialize($data)); // Save to database
$fromDatabase = unserialize(base64_decode($data)); //Getting Save Format
回答by r00tAcc3ss
I don't have enough reputation to comment, so I hope this is seen by people using the above "correct" answer:
我没有足够的声誉来发表评论,所以我希望使用上述“正确”答案的人能看到这一点:
Since php 5.5 the /e modifier in preg_replace() has been deprecated completely and the preg_match above will error out. The php documentation recommends using preg_match_callback in its place.
从 php 5.5 开始, preg_replace() 中的 /e 修饰符已被完全弃用,上面的 preg_match 将出错。php 文档建议在其位置使用 preg_match_callback。
Please find the following solution as an alternative to the above proposed preg_match.
请找到以下解决方案作为上述建议的 preg_match 的替代方案。
$fixed_data = preg_replace_callback ( '!s:(\d+):"(.*?)";!', function($match) {
return ($match[1] == strlen($match[2])) ? $match[0] : 's:' . strlen($match[2]) . ':"' . $match[2] . '";';
},$bad_data );
回答by Ge Rong
There's another reason unserialize()failed because you improperly put serialized data into the database see Official Explanationhere. Since serialize()returns binary data and php variables don't care encoding methods, so that putting it into TEXT, VARCHAR() will cause this error.
unserialize()失败的另一个原因是您未正确地将序列化数据放入数据库,请参阅此处的官方说明。由于serialize()返回的二进制数据和php变量不关心编码方式,所以把它放到TEXT中,VARCHAR()会导致这个错误。
Solution: store serialized data into BLOB in your table.
解决方案:将序列化数据存储到表中的 BLOB 中。
回答by adilbo
Quick Fix
快速解决
Recalculating the length of the elements in serialized array - but don't use (preg_replace) it's deprecated - better use preg_replace_callback:
重新计算序列化数组中元素的长度 - 但不要使用 (preg_replace) 它已被弃用 - 最好使用 preg_replace_callback:
Edit: New Version now not just wrong length but it also fix line-breaks and count correct characters with aczent (thanks to mickmackusa)
编辑:新版本现在不仅长度错误,而且还修复了换行符并使用 aczent 计算正确的字符(感谢mickmackusa)
// New Version
$data = preg_replace_callback('!s:\d+:"(.*?)";!s', function($m) { return "s:" . strlen($m[1]) . ':"'.$m[1].'";'; }, $data);
回答by Will
This error is caused because your charset is wrong.
此错误是由于您的字符集错误引起的。
Set charset after open tag:
在打开标签后设置字符集:
header('Content-Type: text/html; charset=utf-8');
And set charset utf8 in your database :
并在您的数据库中设置字符集 utf8 :
mysql_query("SET NAMES 'utf8'");
回答by Rajesh Meniya
You can fix broken serialize string using following function, with multibyte characterhandling.
您可以使用以下函数修复损坏的序列化字符串,以及多字节字符处理。
function repairSerializeString($value)
{
$regex = '/s:([0-9]+):"(.*?)"/';
return preg_replace_callback(
$regex, function($match) {
return "s:".mb_strlen($match[2]).":\"".$match[2]."\"";
},
$value
);
}
回答by Даниил Путилин
$badData = 'a:2:{i:0;s:16:"as:45:"d";
Is \n";i:1;s:19:"as:45:"d";
Is \r\n";}';
You can not fix a broken serialize string using the proposed regexes:
您无法使用建议的正则表达式修复损坏的序列化字符串:
$data = preg_replace('!s:(\d+):"(.*?)";!e', "'s:'.strlen('').':\"\";'", $badData);
var_dump(@unserialize($data)); // Output: bool(false)
// or
$data = preg_replace_callback(
'/s:(\d+):"(.*?)";/',
function($m){
return 's:' . strlen($m[2]) . ':"' . $m[2] . '";';
},
$badData
);
var_dump(@unserialize($data)); // Output: bool(false)
You can fix broken serialize string using following regex:
您可以使用以下正则表达式修复损坏的序列化字符串:
$data = preg_replace_callback(
'/(?<=^|\{|;)s:(\d+):\"(.*?)\";(?=[asbdiO]\:\d|N;|\}|$)/s',
function($m){
return 's:' . strlen($m[2]) . ':"' . $m[2] . '";';
},
$badData
);
var_dump(@unserialize($data));
Output
输出
array(2) {
[0] =>
string(17) "as:45:"d";
Is \n"
[1] =>
string(19) "as:45:"d";
Is \r\n"
}
or
或者
array(2) {
[0] =>
string(16) "as:45:"d";
Is \n"
[1] =>
string(18) "as:45:"d";
Is \r\n"
}
回答by Pardeep Goyal
public function unserializeKeySkills($string) {
公共函数 unserializeKeySkills($string) {
$output = array();
$string = trim(preg_replace('/\s\s+/', ' ',$string));
$string = preg_replace_callback('!s:(\d+):"(.*?)";!', function($m) { return 's:'.strlen($m[2]).':"'.$m[2].'";'; }, utf8_encode( trim(preg_replace('/\s\s+/', ' ',$string)) ));
try {
$output = unserialize($string);
} catch (\Exception $e) {
\Log::error("unserialize Data : " .print_r($string,true));
}
return $output;
}
回答by Muayyad Alsadi
the official docssays it should return false and set E_NOTICE
在官方的文档说,它应该返回false和集E_NOTICE
but since you got error then the error reporting is set to be triggered by E_NOTICE
但是由于您遇到错误,因此错误报告设置为由 E_NOTICE 触发
here is a fix to allow you detect false returned by unserialize
这是一个修复程序,可让您检测返回的错误 unserialize
$old_err=error_reporting();
error_reporting($old_err & ~E_NOTICE);
$object = unserialize($serialized_data);
error_reporting($old_err);
you might want to consider use base64 encode/decode
您可能需要考虑使用 base64 编码/解码
$string=base64_encode(serialize($obj));
unserialize(base64_decode($string));
回答by Adam Bubela
In my case I was storing serialized data in BLOBfield of MySQL DB which apparently wasn't big enough to contain the whole value and truncated it. Such a string obviously could not be unserialized.
Once converted that field to MEDIUMBLOBthe problem dissipated.
Also it may be needed to switch in table options ROW_FORMATto DYNAMICor COMPRESSED.
在我的情况下,我将序列化数据存储在BLOBMySQL DB 的字段中,这显然不足以包含整个值并截断它。这样的字符串显然无法反序列化。
一旦将该字段转换MEDIUMBLOB为问题就消失了。此外,可能需要将表选项切换ROW_FORMAT到DYNAMIC或COMPRESSED。

