it-roy-ru.com

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

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

50
Alix Axel

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

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

24
David

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

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

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

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

2
Joe Hong

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

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

1
ThiefMaster

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

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

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

1
Mike

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

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

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

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

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

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

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

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
Paolo Josef Abadesco

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

0
Artefacto