PHP反序列化失败了非编码字符?

FFi*_*ish 28 php serialization encode character

$ser = 'a:2:{i:0;s:5:"héllö";i:1;s:5:"wörld";}'; // fails
$ser2 = 'a:2:{i:0;s:5:"hello";i:1;s:5:"world";}'; // works
$out = unserialize($ser);
$out2 = unserialize($ser2);
print_r($out);
print_r($out2);
echo "<hr>";
Run Code Online (Sandbox Code Playgroud)

但为什么?
我应该在序列化之前进行编码吗?怎么样?

我使用Javascript将序列化字符串写入隐藏字段,而不是PHP的$ _POST
在JS中我有类似的东西:

function writeImgData() {
    var caption_arr = new Array();
    $('.album img').each(function(index) {
         caption_arr.push($(this).attr('alt'));
    });
    $("#hidden-field").attr("value", serializeArray(caption_arr));
};
Run Code Online (Sandbox Code Playgroud)

Ali*_*xel 52

unserialize()失败的原因是:

$ser = 'a:2:{i:0;s:5:"héllö";i:1;s:5:"wörld";}';
Run Code Online (Sandbox Code Playgroud)

是因为PHP 的长度héllöwörld错误,因为PHP本身不能正确处理多字节字符串:

echo strlen('héllö'); // 7
echo strlen('wörld'); // 6
Run Code Online (Sandbox Code Playgroud)

但是,如果您尝试unserialize()以下正确的字符串:

$ser = 'a:2:{i:0;s:7:"héllö";i:1;s:6:"wörld";}';

echo '<pre>';
print_r(unserialize($ser));
echo '</pre>';
Run Code Online (Sandbox Code Playgroud)

有用:

Array
(
    [0] => héllö
    [1] => wörld
)
Run Code Online (Sandbox Code Playgroud)

如果使用PHP,serialize()它应该正确计算多字节字符串索引的长度.

另一方面,如果你想使用多种(编程)语言的序列化数据,你应该忘记它并转向类似JSON的东西,这更加标准化.


Lio*_*han 51

我知道这张贴就像一年前一样,但是我只是遇到了这个问题并遇到了这个问题,事实上我找到了解决方案.这段代码就像魅力一样!

背后的想法很简单.它只是通过重新计算上面@Alix发布的多字节字符串的长度来帮助你.

一些修改应该适合您的代码:

/**
 * Mulit-byte Unserialize
 *
 * UTF-8 will screw up a serialized string
 *
 * @access private
 * @param string
 * @return string
 */
function mb_unserialize($string) {
    $string = preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $string);
    return unserialize($string);
}
Run Code Online (Sandbox Code Playgroud)

资料来源:http://snippets.dzone.com/posts/show/6592

在我的机器上测试过,它就像魅力一样!

  • 我发布下面你的功能改为使用PHP 5.5.感谢您的有益贡献. (2认同)

Dav*_*vid 25

Lionel Chan回答修改为使用PHP> = 5.5:

function mb_unserialize($string) {
    $string2 = preg_replace_callback(
        '!s:(\d+):"(.*?)";!s',
        function($m){
            $len = strlen($m[2]);
            $result = "s:$len:\"{$m[2]}\";";
            return $result;

        },
        $string);
    return unserialize($string2);
}    
Run Code Online (Sandbox Code Playgroud)

此代码使用preg_replace_callback作为preg_replace,其中/ e修饰符从PHP 5.5开始已过时.


小智 9

问题是 - 正如Alix所指出的 - 与编码有关.

在PHP 5.4之前,PHP的内部编码是ISO-8859-1,这种编码对unicode为多字节的某些字符使用单个字节.结果是在UTF-8系统上序列化的多字节值将无法在ISO-8859-1系统上读取.

避免这样的问题确保所有系统使用相同的编码:

mb_internal_encoding('utf-8');
$arr = array('foo' => 'bár');
$buf = serialize($arr);
Run Code Online (Sandbox Code Playgroud)

你可以utf8_(encode|decode)用来清理:

// Set system encoding to iso-8859-1
mb_internal_encoding('iso-8859-1');
$arr = unserialize(utf8_encode($serialized));
print_r($arr);
Run Code Online (Sandbox Code Playgroud)