32
$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>";

但为什么?
我应该在序列化之前编码吗?如何?

我正在使用 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));
};
4

14 回答 14

56

unserialize()失败的原因是:

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

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

echo strlen('héllö'); // 7
echo strlen('wörld'); // 6

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

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

echo '<pre>';
print_r(unserialize($ser));
echo '</pre>';

有用:

Array
(
    [0] => héllö
    [1] => wörld
)

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

另一方面,如果您想以多种(编程)语言处理序列化数据,您应该忘记它并转向 JSON 之类的东西,它更加标准化。

于 2010-05-17T23:33:41.610 回答
52

我知道这是在一年前发布的,但我只是遇到了这个问题并遇到了这个问题,实际上我找到了解决方案。这段代码就像魅力一样!

背后的想法很简单。它只是通过重新计算上面@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);
}

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

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

于 2011-04-28T02:50:49.960 回答
33

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

此代码使用preg_replace_callback作为带有 /e 修饰符的 preg_replace 自 PHP 5.5 起已过时

于 2015-01-13T14:30:19.870 回答
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);

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

// Set system encoding to iso-8859-1
mb_internal_encoding('iso-8859-1');
$arr = unserialize(utf8_encode($serialized));
print_r($arr);
于 2012-05-24T12:39:08.550 回答
3

";作为对上面@Lionel 的回复,实际上,如果序列化字符串本身包含字符序列(引号后跟分号),则您提出的函数 mb_unserialize() 将不起作用 。谨慎使用。例如:

$test = 'test";string'; 
// $test is now 's:12:"test";string";'
$string = preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $test);
print $string; 
// output: s:4:"test";string";  (Wrong!!)

JSON是要走的路,正如其他人所提到的,恕我直言

注意:我将此作为新答案发布,因为我不知道如何直接回复(此处为新)。

于 2012-04-02T03:33:04.963 回答
1

这里还有一个细微的变化,希望能对某人有所帮助……我正在序列化一个数组,然后将其写入数据库。在检索数据时,反序列化操作失败。

事实证明,我正在写入的数据库长文本字段使用的是 latin1 而不是 UTF8。当我切换它时,一切都按计划进行。

感谢以上所有提到字符编码并让我走上正轨的人!

于 2013-04-12T19:47:45.497 回答
1

当另一端不是PHP 时,不要使用 PHP 序列化/反序列化。它并不意味着是一种可移植的格式——例如,它甚至包括用于受保护密钥的 ascii-1 字符,这在 javascript 中是你不想处理的(即使它工作得很好,但它非常难看)。

相反,请使用JSON等可移植格式。XML 也可以完成这项工作,但 JSON 的开销更少,并且对程序员更友好,因为您可以轻松地将其解析为简单的数据结构,而不必处理 XPath、DOM 树等。

于 2010-05-17T23:11:34.443 回答
1

这个解决方案对我有用:

$unserialized = unserialize(utf8_encode($st));
于 2020-09-17T12:31:01.193 回答
0
/**
 * MULIT-BYTE UNSERIALIZE
 *
 * UTF-8 will screw up a serialized string
 *
 * @param string
 * @return string
 */
function mb_unserialize($string) {
    $string = preg_replace_callback('/!s:(\d+):"(.*?)";!se/', function($matches) { return 's:'.strlen($matches[1]).':"'.$matches[1].'";'; }, $string);
    return unserialize($string);
}
于 2014-10-10T16:46:03.550 回答
0

我建议您使用 javascript 编码为 json,然后使用json_decode来反序列化。

于 2010-05-17T22:57:18.220 回答
0

我们可以将字符串分解为一个数组:

$finalArray = array();
$nodeArr = explode('&', $_POST['formData']);

foreach($nodeArr as $value){
    $childArr = explode('=', $value);
    $finalArray[$childArr[0]] = $childArr[1];
}
于 2016-10-03T09:47:32.007 回答
0

连载:

foreach ($income_data as $key => &$value)
{
    $value = urlencode($value);
}
$data_str = serialize($income_data);

反序列化:

$data = unserialize($data_str);
foreach ($data as $key => &$value)
{
    $value = urldecode($value);
}
于 2016-10-17T11:16:44.833 回答
0

就我而言,问题在于行尾(可能某些编辑器已将我的文件从 DOS 更改为 Unix)。

我将这些自适应包装器放在一起:

function unserialize_fetchError($original, &$unserialized, &$errorMsg) {
    $unserialized = @unserialize($original);
    $errorMsg = error_get_last()['message'];
    return ( $unserialized !== false || $original == 'b:0;' );  // "$original == serialize(false)" is a good serialization even if deserialization actually returns false
}

function unserialize_checkAllLineEndings($original, &$unserialized, &$errorMsg, &$lineEndings) {
    if ( unserialize_fetchError($original, $unserialized, $errorMsg) ) {
        $lineEndings = 'unchanged';
        return true;
    } elseif ( unserialize_fetchError(str_replace("\n", "\n\r", $original), $unserialized, $errorMsg) ) {
        $lineEndings = '\n to \n\r';
        return true;
    } elseif ( unserialize_fetchError(str_replace("\n\r", "\n", $original), $unserialized, $errorMsg) ) {
        $lineEndings = '\n\r to \n';
        return true;
    } elseif ( unserialize_fetchError(str_replace("\r\n", "\n", $original), $unserialized, $errorMsg) ) {
        $lineEndings = '\r\n to \n';
        return true;
    } //else
    return false;
}
于 2018-04-13T07:26:14.397 回答
0

这个对我有用。

function mb_unserialize($string) {
    $string = mb_convert_encoding($string, "UTF-8", mb_detect_encoding($string, "UTF-8, ISO-8859-1, ISO-8859-15", true));
    $string = preg_replace_callback(
        '/s:([0-9]+):"(.*?)";/',
        function ($match) {
            return "s:".strlen($match[2]).":\"".$match[2]."\";"; 
        },
        $string
    );
    return unserialize($string);
}
于 2017-10-12T13:15:52.227 回答