Ошибка 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)); }; 

Solutions Collecting From Web of "Ошибка PHP unserialize с некодированными символами?"

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

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

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

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

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.

Проблема заключается в том, что, как указывает 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); 

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

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

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

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

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

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

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

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

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

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