Подтвердить что ты не робот

Ошибка PHP unserialize с некодированными символами?

$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));
};
4b9b3361

Ответ 1

Причина, по которой 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, что более стандартизировано.

Ответ 2

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

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

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

Ответ 3

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 как preg_replace с модификатором /e устарел с PHP 5.5.

Ответ 4

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

До 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);

Ответ 5

В ответ на @Lionel выше на самом деле функция mb_unserialize(), которую вы предложили, не будет работать, если сама сериализованная строка содержит char последовательность "; (цитата, за которой следует точка с запятой). Используйте с осторожностью. Например:

$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 - это путь, как упоминалось другими, IMHO

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

Ответ 6

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

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

Ответ 7

Я бы посоветовал использовать javascript для кодирования как json, а затем использовать json_decode для unserialize.

Ответ 8

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

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

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

Ответ 9

мы можем разбить строку на массив:

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

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

Ответ 10

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

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

Ответ 11

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

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

Ответ 12

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