PHP取消序列化失败,使用非编码字符?

PHP取消序列化失败,使用非编码字符?,php,character,encode,serialization,Php,Character,Encode,Serialization,我建议您使用javascript编码为json,然后使用来取消序列化。不要使用PHP序列化/取消序列化,而另一端不是PHP。它并不意味着是一种可移植的格式——例如,它甚至包括用于受保护密钥的ascii-1字符,这在javascript中是不需要处理的(尽管它工作得非常好,但它非常难看) 相反,使用类似JSON的可移植格式。XML也可以完成这项工作,但JSON的开销更小,而且对程序员更友好,因为您可以轻松地将其解析为简单的数据结构,而不必处理XPath、DOM树等。为什么取消序列化()失败的原因是

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

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


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

为什么
取消序列化()
失败的原因是:

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));
};
这是因为
héllö
wörld
的长度是错误的,因为PHP无法正确处理本机的多字节字符串:

$ser = 'a:2:{i:0;s:5:"héllö";i:1;s:5:"wörld";}';
但是,如果尝试
取消序列化()
以下正确字符串:

echo strlen('héllö'); // 7
echo strlen('wörld'); // 6
如果使用PHP
serialize()
,它应该正确计算多字节字符串索引的长度


另一方面,如果你想用多种(编程)语言处理序列化数据,你应该忘记它,转而使用JSON之类的标准化语言。

我知道这是一年前发布的,但我遇到了这个问题,事实上我找到了解决方案。这段代码很有魅力

背后的想法很简单。它只是帮助您重新计算上面@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);
}
资料来源:


在我的机器上测试过,效果很好

作为对上面@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是前进的方向

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

正如Alix所指出的,问题与编码有关

在PHP 5.4之前,PHP的内部编码是ISO-8859-1,这种编码对某些unicode中的多字节字符使用单字节。结果是,在UTF-8系统上序列化的多字节值在ISO-8859-1系统上不可读

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

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

foreach($nodeArr as $value){
    $childArr = explode('=', $value);
    $finalArray[$childArr[0]] = $childArr[1];
}
mb_内部编码('utf-8');
$arr=array('foo'=>'bár');
$buf=序列化($arr);
您可以使用
utf8(编码|解码)
进行清理:

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

foreach($nodeArr as $value){
    $childArr = explode('=', $value);
    $finalArray[$childArr[0]] = $childArr[1];
}
//将系统编码设置为iso-8859-1
mb_内部_编码('iso-8859-1');
$arr=unserialize(utf8_编码($serialized));
印刷费($arr);

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

原来我写的数据库longtext字段使用的是latin1而不是UTF8。当我切换它时,一切都按计划进行了

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

答案已修改为使用PHP>=5.5:

/**
 * 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);
}
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_回调,就像PHP 5.5一样。

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

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

foreach($nodeArr as $value){
    $childArr = explode('=', $value);
    $finalArray[$childArr[0]] = $childArr[1];
}
序列化:

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

这个对我有用

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

在我的例子中,问题出在行结尾上(可能是某个编辑器将我的文件从DOS更改为Unix)

我把这些apadtive包装放在一起:

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

这个解决方案对我有效:

$unserialized = unserialize(utf8_encode($st));

也就是说,$ser='a:2:{i:0;s:5:'héllö;i:1;s:5:'wörld”};var_dump(unserialize($ser)),对我来说很好。你说失败是什么意思?调用unserialize()失败?更不用说从不受信任的源进行序列化可能会导致任意代码执行。不幸的是,其他人的工作将此选择强加给了我们。这在从旧项目/系统导入数据时尤其常见,因为该项目/系统的数据中已经建立了序列化。您将能够很快回复注释。继续贡献!干杯~很高兴知道。有解决方案吗?在我的例子中,问题出在数据库编码中,所以我在
中丢失了部分数据,但是这个函数帮助我使代码工作,即使这样,谢谢,这让我省去了大量的头痛!感谢+1这项非常有用的工作。我也测试了它,它对我的UTF-8数据也有效使用法语口音(我的服务器上是PHP5.3)。我在下面发布了你的函数改为使用PHP5.5。感谢你的有用贡献。实际上正则表达式是错误的,因为字符串本身可能包含与序列化模式无关的模式。例如,序列化部分
…s:28:“一些”引号;在中间“…
之后,函数将返回
…s:13:“some\”引号;在中间“…
。这是创建序列化的原因之一。json_encode:“此函数仅适用于UTF-8编码的数据…”以及在使用serialize()和unserialize()的情况下如果仍然失败,请检查您的存储介质。例如,mysql您应该存储为二进制或blob。如果您在mysql中存储为文本,它将无法处理您的多字节字符。在php环境之间切换时也要小心。在保存到数据库之前,我在本地计算机上遇到了编码问题,然后尝试在live server上取消序列化。Adj不使用字符计数解决了这个问题。这可能也是我两年前遇到的一个问题的答案,我从未找到答案。我必须使用这个版本来防止编码数组中的HTML字符串在未序列化的字符串中得到错误的转义双引号。非常感谢@David。我一直是
$unserialized = unserialize(utf8_encode($st));