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