制作一个可以在PHP中恢复损坏的序列化字符串的脚本

时间:2011-11-13 18:03:40

标签: php serialization

好的,我有这个:

a:1:{i:0;a:3:{s:7:"address";s:52:"Elågåresgude 41, 2200 Københamm N";s:12:"company_name";s:14:"Kaffe og Kluns";s:9:"telephone";s:0:"";}}

这不适用于unserialize($ string);

我知道错误在哪里。这是前口地址的数字。它不应该是52,而是36。

我通过计算字符串(给了我33)然后在字符串中存在的每个å或ø上加上1来得到这个数字。

当我用36替换52时,它会无法完全取消。

现在我想编写一个脚本来为我的所有地址执行此操作。

但我怎么能这样做呢?当地址/公司名称/电话字符串“损坏”时,提取它?

4 个答案:

答案 0 :(得分:4)

function fix_corrupted_serialized_string($string) {
    $tmp = explode(':"', $string);
    $length = count($tmp);
    for($i = 1; $i < $length; $i++) {    
        list($string) = explode('"', $tmp[$i]);
        $str_length = strlen($string);    
        $tmp2 = explode(':', $tmp[$i-1]);
        $last = count($tmp2) - 1;    
        $tmp2[$last] = $str_length;         
        $tmp[$i-1] = join(':', $tmp2);
    }
    return join(':"', $tmp);
}

工作演示: http://codepad.viper-7.com/GNbM25

答案 1 :(得分:1)

此问题是典型的情况,有人在更新序列化字符串中的值时尝试执行快捷方式。迅速学到的避免这种麻烦的教训是对数据进行反序列化,修改您的值,然后重新对其进行序列化。

我觉得正则表达式为 try 解析损坏的序列化字符串提供了更直接的方法。十分清楚,我的代码段只会更新字节/字符数;如果您的序列化字符串已通过其他方式损坏,则这不是解决方法。

这里是一个简单的preg_replace_callback()调用,它仅捕获值子字符串,并无条件替换序列化字符串中的所有字节数:

代码:(Demo

$corrupted_byte_counts = <<<STRING
a:1:{i:0;a:3:{s:7:"address";s:52:"Elågåresgude 41, 2200 Københamm N";s:12:"company_name";s:14:"Kaffe og Kluns";s:9:"telephone";s:0:"";}}
STRING;

$repaired = preg_replace_callback(
        '/s:\d+:"(.*?)";/s',
        function ($m) {
            return 's:' . strlen($m[1]) . ":\"{$m[1]}\";";
        },
        $corrupted_byte_counts
    );

echo "corrupted serialized array:\n$corrupted_byte_counts";
echo "\n---\n";
echo "repaired serialized array:\n$repaired";
echo "\n---\n";
print_r(unserialize($repaired));

输出:

corrupted serialized array:
a:1:{i:0;a:3:{s:7:"address";s:52:"Elågåresgude 41, 2200 Københamm N";s:12:"company_name";s:14:"Kaffe og Kluns";s:9:"telephone";s:0:"";}}
---
repaired serialized array:
a:1:{i:0;a:3:{s:7:"address";s:36:"Elågåresgude 41, 2200 Københamm N";s:12:"company_name";s:14:"Kaffe og Kluns";s:9:"telephone";s:0:"";}}
---
Array
(
    [0] => Array
        (
            [address] => Elågåresgude 41, 2200 Københamm N
            [company_name] => Kaffe og Kluns
            [telephone] => 
        )

)

I've even gone a bit further to address a possible fringe case.无需在该链接中实现模式扩展,上述代码段将按需在具有以下条件的字符串上工作:

  • 多字节字符
  • 换行符
  • 冒号
  • 分号
  • 逗号
  • 单引号
  • 双引号

仅当要匹配的字符串包含";时才会中断-在这种情况下,我上面的链接试图解决这种可能性。

答案 2 :(得分:0)

在处理多字节字符时看起来像函数中的错误。您可能还想在序列化之前尝试explicitly encoding the string as utf-8

作为一种解决方法,您可以在序列化之前base64 encode地址,然后在反序列化时base64 decode

答案 3 :(得分:-1)

我认为一个solution应该测试unserialize是否有效。如果没有,删除它并重新序列化。

$yourserializestring = '...';

$data = @unserialize($yourserializestring);
if ($yourserializestring === 'b:0;' || $data !== false) {
    // Something didn't work, you should recreate it
} else {
    echo "ok";
}