PHP unserialize терпит неудачу с некодированными символами? - PullRequest
28 голосов
/ 18 мая 2010
$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));
};

Ответы [ 13 ]

51 голосов
/ 28 апреля 2011

Я знаю, что это было опубликовано, как год назад, но у меня просто есть эта проблема, и я столкнулся с ней, и фактически я нашел решение для нее Этот кусок кода работает как шарм!

Идея проста. Это просто помогает вам пересчитать длину многобайтовых строк, как написано @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

Проверено на моей машине, и она работает как шарм !!

50 голосов
/ 18 мая 2010

Причина сбоя 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() ввести следующую правильную строку:

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

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

Работает:

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

Если вы используете PHP serialize(), он должен правильно вычислять длины многобайтовых строковых индексов.

С другой стороны, если вы хотите работать с сериализованными данными на нескольких языках программирования, вы должны забыть об этом и перейти к чему-то вроде JSON, который более стандартизирован.

26 голосов
/ 13 января 2015

Лайонел Чан ответ изменен для работы с 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 как preg_replace с модификатором / e устарел начиная с PHP 5.5.

8 голосов
/ 24 мая 2012

Проблема - , как указывает Аликс , - связана с кодировкой.

До PHP 5.4 внутренняя кодировка для PHP была ISO-8859-1, эта кодировка использует один байт для некоторых символов, которые в Юникоде являются многобайтовыми. В результате многобайтовые значения, сериализованные в системе 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);
2 голосов
/ 02 апреля 2012

В ответ на @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 - это путь, о котором говорили другие, ИМХО

Примечание: я публикую это как новый ответ, так как не знаю, как ответить прямо (новое здесь).

1 голос
/ 12 апреля 2013

Еще одна небольшая вариация, которая, надеюсь, поможет кому-то ... Я сериализировал массив, а затем записывал его в базу данных. При получении данных операция десериализации завершилась неудачно.

Оказывается, что поле длинного текста базы данных, в которое я писал, использовало latin1, а не UTF8. Когда я его переключил, все заработало как запланировано.

Спасибо всем выше, кто упомянул кодировку символов и вывел меня на правильный путь!

1 голос
/ 18 мая 2010

Do not использовать сериализацию / десериализацию PHP, когда другой конец не является PHP. Он не предназначен для переносимого формата - например, он даже включает символы ascii-1 для защищенных ключей, с которыми вы не хотите иметь дело в javascript (даже несмотря на то, что он будет работать идеально, это просто ужасно). *

Вместо этого используйте переносимый формат, такой как JSON . XML тоже справится с этой задачей, но JSON имеет меньше накладных расходов и более удобен для программистов, так как вы можете легко разобрать его в простую структуру данных вместо того, чтобы иметь дело с XPath, деревьями DOM и т. Д.

0 голосов
/ 13 апреля 2018

В моем случае проблема была с окончаниями строк (вероятно, некоторые редакторы изменили мой файл с 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;
}
0 голосов
/ 12 октября 2017

этот работал для меня.

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);
}
0 голосов
/ 17 октября 2016

Сериализация:

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