#php #mysql #serialization
#php #mysql #сериализация
Вопрос:
У меня возникли некоторые проблемы при попытке рассериализовать массив в PHP.
У меня есть два метода в классе, один для обработки хранения сериализованного массива в таблице mysql, а другой для его извлечения.
Я получаю следующую ошибку при выполнении второго метода (при извлечении / несериализации):
Обратите внимание: unserialize(): ошибка при смещении 50623 на 50647 байт в /Users/benwaine/NetBeansProjects/SentimentEngineMk2/lib/TSE/Mapper/Filter.php в строке 39
Строка хранится в поле blob-объекта в MySQL. Массив является многомерным и состоит из токена с массивом значений типа float. Проблема возникает периодически, возможно, предполагая, что проблема связана с токенами?
// Структура Ключевое слово - 0.234 - 0.234 KeywordB - 0.23 - 0.47
// Методы публичная функция insertFilterForKeyword($id, массив $filterProbs) { $sql = "ВСТАВИТЬ В фильтр (keyword_id, filter_probs, sample_size_pos, sample_size_neg) ЗНАЧЕНИЕ (:id, :filterProbs, :pos, :neg)"; $stmt = $this->pdo-> подготовить($sql); $meta = array_shift($filterProbs); $stmt->bindParam(':id', $id); $stmt->bindParam(':filterProbs',сериализовать($filterProbs)); $stmt->bindParam(':pos', $meta['sample_size_pos']); $stmt->bindParam(':neg', $meta['sample_size_neg']); $result = $stmt-> выполнить(); возвращает $resu< } общедоступная функция getFilterByKeyword(ключевое слово $) { $sql = 'ВЫБРАТЬ f.id , например,keyword_id, например,filter_probs, например,sample_size_pos, например,sample_size_neg ИЗ фильтра f Ключевое слово JOIN k НА k.id = f.keyword_id ГДЕ k.ключевое слово = :ключевоеслово'; $stmt = $this->pdo-> подготовить($sql); $stmt->bindParam(':ключевое слово', $keyword); $result = $stmt-> выполнить(); $data = $stmt->выборка(PDO::FETCH_ASSOC); $probArray = несериализовать($data['filter_probs']); $filter = новый TSE_Filter($probArray); возвращает $filter; }
Я предполагаю, что ошибка связана с символами в данных, которые вызывают проблемы с десериализацией.
После поиска в Google я попробовал использовать строку десериализации следующими способами, ни один из которых не сработал:
1) base64_encode / декодирование сериализованной строки.
//для безопасной сериализации $safe_string_to_store = base64_encode(сериализовать($multidimensional_array)); //для несериализации... $array_restored_from_db = несериализовать(base64_decode($encoded_serialized_string));
2) Используйте предварительное соответствие для изменения потенциально сложных символов:
рассериализовать(preg_replace('/;n;/', ';N;', strtolower($data['filter_probs'])));
Любая помощь была бы высоко оценена!
Спасибо.
Комментарии:
1. Можете ли вы опубликовать пример того, как выглядят сериализованные данные, когда вы запрашиваете их из базы данных, не пытаясь их рассериализовать?
Ответ №1:
Я бы предположил, что проблема заключается в длине strin в db. Что произойдет, если вы повторите сериализованную строку после того, как извлекли ее из базы данных? Выглядит ли это как готовая строка или она прерывается? Я думаю, что большой двоичный объект содержит около 65 тысяч символов, вы могли бы попробовать изменить его на longblob, а затем повторно сохранить данные?
Кроме того, если у вас есть тот, который, как вы знаете, не работает, пропустите сохранение базы данных и попробуйте сразу рассериализовать строку. Тогда, по крайней мере, вы знаете, проблема в несериализации или в данных в вашей БД?
Комментарии:
1. Спасибо, Дуглас. Вы правы. После перехода на длинный большой двоичный объект несериализация прошла успешно. В итоге я записал строку в файл до и после сохранения базы данных, а затем я их разделил, и было легко увидеть упомянутое вами сокращение. Приветствия!
Ответ №2:
В вашем примере кода вы используете urlencode
для сериализованной строки. Вы не декодируете urlencoding при отмене сериализации, что может привести к сбою отмены сериализации.
Возможно, в этом проблема?
(Вы должны иметь возможность хранить сериализованные данные без urlencoding)
Комментарии:
1. Спасибо за ответ, Джани. Я случайно оставил это в одном из решений, которые я пытался использовать перед публикацией. Я отредактировал его и удалил из вопроса сейчас.
Ответ №3:
Я предполагаю, что это как-то связано с кодировками столбцов, соединений или клиентов.
Из http://dev.mysql.com/doc/refman/5.1/en/charset-conversion.html:
Если столбец имеет двоичный тип данных (BINARY, VARBINARY, BLOB), все значения, которые он содержит, должны быть закодированы с использованием одного набора символов (набора символов, в который вы преобразуете столбец). Если вы используете двоичный столбец для хранения информации в нескольких наборах символов, MySQL не имеет способа узнать, какие значения используют какой набор символов, и не может преобразовать данные должным образом.
Итак, я бы попробовал сначала явно установить для столбца ДВОИЧНЫЙ НАБОР СИМВОЛОВ. Помните, что сериализованные строки содержат нулевые байты и другие странные вещи, если вы сериализуете объекты (я понимаю, что вы этого не делаете, но кто знает, что содержит ваш массив в виде ключей и значений).
Кроме того, вы не помечаете сериализованный аргумент как большой двоичный объект при его подготовке, поэтому PDO, вероятно, также применяет некоторое преобразование символов. Для вставки попробуйте использовать это:
$stmt->bindParam(':filterProbs', serialize($filterProbs), PDO::PARAM_LOB);
И, возможно, используйте тот же подход и при выборке, только с PDOStatement::bindColumn()
(и помните о выборке с PDOStatement::fetch(PDO::FETCH_BOUND)
тогда).
Ответ №4:
Проверьте определение схемы базы данных для поля filter
.filter_probs проверяет, достаточно ли он длинный для сохранения сериализованной строки.
Также вы можете проверить массив перед сериализацией и после несериализации, содержащий одно и то же значение.
Комментарии:
1. Привет, Энди, я проверил определение, это ‘blob’ без значения длины. Этого достаточно?
Ответ №5:
Соответствует ли комментарий getmequickhttp://php.net/manual/en/function.unserialize.php помочь? Найдите «ошибку при смещении»…