Regex / code для исправления поврежденных сериализованных данных PHP

У меня массивный многомерный массив, который был сериализован PHP. Он хранился в MySQL и поле данных было недостаточно большим … конец был отключен … Мне нужно извлечь данные … unserialize wont work … кто-нибудь знает код, который может закройте все массивы … пересчитайте длины строк … это слишком много данных, чтобы сделать вручную.

Большое спасибо.

Я думаю, это почти невозможно. Прежде чем вы сможете отремонтировать массив, вам нужно знать, как он поврежден. Сколько детей не хватает? Каков был контент?

Извините imho, вы не можете этого сделать.

Это перерасчет длины элементов в сериализованном массиве:

 $fixed = preg_replace_callback( '/s:([0-9]+):\"(.*?)\";/', function ($matches) { return "s:".strlen($matches[2]).':"'.$matches[2].'";'; }, $serialized ); 

Однако это не работает, если ваши строки содержат двойные кавычки. В этом случае невозможно автоматически исправить сериализованную строку массива – потребуется ручное редактирование.

Я пробовал все, что было найдено в этом посте, и ничего не работало для меня. После нескольких часов боли вот что я нашел на глубоких страницах Google и, наконец, работал:

 function fix_str_length($matches) { $string = $matches[2]; $right_length = strlen($string); // yes, strlen even for UTF-8 characters, PHP wants the mem size, not the char count return 's:' . $right_length . ':"' . $string . '";'; } function fix_serialized($string) { // securities if ( !preg_match('/^[aOs]:/', $string) ) return $string; if ( @unserialize($string) !== false ) return $string; $string = preg_replace("%\n%", "", $string); // doublequote exploding $data = preg_replace('%";%', "µµµ", $string); $tab = explode("µµµ", $data); $new_data = ''; foreach ($tab as $line) { $new_data .= preg_replace_callback('%\bs:(\d+):"(.*)%', 'fix_str_length', $line); } return $new_data; } 

Вы вызываете эту процедуру следующим образом:

 //Let's consider we store the serialization inside a txt file $corruptedSerialization = file_get_contents('corruptedSerialization.txt'); //Try to unserialize original string $unSerialized = unserialize($corruptedSerialization); //In case of failure let's try to repair it if(!$unSerialized){ $repairedSerialization = fix_serialized($corruptedSerialization); $unSerialized = unserialize($repairedSerialization); } //Keep your fingers crossed var_dump($unSerialized); 

Решение для меня:

unserialize( serialize_corrector( $serialized_string) );

код:

 function serialize_corrector($serialized_string){ // at first, check if "fixing" is really needed at all. After that, security checkup. if ( @unserialize($serialized_string) !== true && preg_match('/^[aOs]:/', $serialized_string) ) { $serialized_string = preg_replace_callback( '/s\:(\d+)\:\"(.*?)\";/s', function($matches){return 's:'.strlen($matches[2]).':"'.$matches[2].'";'; }, $serialized_string ); } return $serialized_string; } 

Используя preg_replace_callback() , вместо preg_replace(.../e) (поскольку модификатор /e устарел ).

 $fixed_serialized_String = preg_replace_callback('/s:([0-9]+):\"(.*?)\";/',function($match) { return "s:".strlen($match[2]).':"'.$match[2].'";'; }, $serializedString); $correct_array= unserialize($fixed_serialized_String); 

Я сомневаюсь, что кто-нибудь напишет код для извлечения частично сохраненных массивов 🙂 Я исправил такую ​​вещь один раз, но вручную, и потребовалось несколько часов, а затем я понял, что мне не нужна эта часть массива …

Если его действительно важные данные (и я имею в виду ДЕЙСТВИТЕЛЬНО важно), вам лучше оставить это

Основано на @Emil M Answer. Это фиксированная версия, которая работает с текстом, содержащим двойные кавычки.

 function fix_broken_serialized_array($match) { return "s:".strlen($match[2]).":\"".$match[2]."\";"; } $fixed = preg_replace_callback( '/s:([0-9]+):"(.*?)";/', "fix_broken_serialized_array", $serialized ); 

Следующий фрагмент попытается прочитать и проанализировать рекурсивно поврежденную сериализованную строку (данные blob). Например, если вы слишком долго сохранили строку столбца базы данных, она была отключена. Числовые примитивы и bool гарантированно действительны, строки могут быть обрезаны и / или клавиши массива могут отсутствовать. Подпрограмма может быть полезна, например, если восстановление значительной (не всей) части данных является для вас достаточным решением.

 class Unserializer { /** * Parse blob string tolerating corrupted strings & arrays * @param string $str Corrupted blob string */ public static function parseCorruptedBlob(&$str) { // array pattern: a:236:{...;} // integer pattern: i:123; // double pattern: d:329.0001122; // boolean pattern: b:1; or b:0; // string pattern: s:14:"date_departure"; // null pattern: N; // not supported: object O:{...}, reference R:{...} // NOTES: // - primitive types (bool, int, float) except for string are guaranteed uncorrupted // - arrays are tolerant to corrupted keys/values // - references & objects are not supported // - we use single byte string length calculation (strlen rather than mb_strlen) since source string is ISO-8859-2, not utf-8 if(preg_match('/^a:(\d+):{/', $str, $match)){ list($pattern, $cntItems) = $match; $str = substr($str, strlen($pattern)); $array = []; for($i=0; $i<$cntItems; ++$i){ $key = self::parseCorruptedBlob($str); if(trim($key)!==''){ // hmm, we wont allow null and "" as keys.. $array[$key] = self::parseCorruptedBlob($str); } } $str = ltrim($str, '}'); // closing array bracket return $array; }elseif(preg_match('/^s:(\d+):/', $str, $match)){ list($pattern, $length) = $match; $str = substr($str, strlen($pattern)); $val = substr($str, 0, $length + 2); // include also surrounding double quotes $str = substr($str, strlen($val) + 1); // include also semicolon $val = trim($val, '"'); // remove surrounding double quotes if(preg_match('/^a:(\d+):{/', $val)){ // parse instantly another serialized array return (array) self::parseCorruptedBlob($val); }else{ return (string) $val; } }elseif(preg_match('/^i:(\d+);/', $str, $match)){ list($pattern, $val) = $match; $str = substr($str, strlen($pattern)); return (int) $val; }elseif(preg_match('/^d:([\d.]+);/', $str, $match)){ list($pattern, $val) = $match; $str = substr($str, strlen($pattern)); return (float) $val; }elseif(preg_match('/^b:(0|1);/', $str, $match)){ list($pattern, $val) = $match; $str = substr($str, strlen($pattern)); return (bool) $val; }elseif(preg_match('/^N;/', $str, $match)){ $str = substr($str, strlen('N;')); return null; } } } // usage: $unserialized = Unserializer::parseCorruptedBlob($serializedString); 

Лучшее решение для меня:

$output_array = unserialize(My_checker($serialized_string));

код:

 function My_checker($serialized_string){ // securities if (empty($serialized_string)) return ''; if ( !preg_match('/^[aOs]:/', $serialized_string) ) return $serialized_string; if ( @unserialize($serialized_string) !== false ) return $serialized_string; return preg_replace_callback( '/s\:(\d+)\:\"(.*?)\";/s', function ($matches){ return 's:'.strlen($matches[2]).':"'.$matches[2].'";'; }, $serialized_string ) ; } 

Сериализация почти всегда плоха, потому что вы не можете ее каким-либо образом искать. Извините, но кажется, что вы оказались в углу …

Вы можете вернуть недействительные сериализованные данные в нормальное состояние, используя массив 🙂

 str = "a:1:{i:0;a:4:{s:4:\"name\";s:26:\"20141023_544909d85b868.rar\";s:5:\"dname\";s:20:\"HTxRcEBC0JFRWhtk.rar\";s:4:\"size\";i:19935;s:4:\"dead\";i:0;}}"; preg_match_all($re, $str, $matches); if(is_array($matches) && !empty($matches[1]) && !empty($matches[2])) { foreach($matches[1] as $ksel => $serv) { if(!empty($serv)) { $retva[] = $serv; }else{ $retva[] = $matches[2][$ksel]; } } $count = 0; $arrk = array(); $arrv = array(); if(is_array($retva)) { foreach($retva as $k => $va) { ++$count; if($count/2 == 1) { $arrv[] = $va; $count = 0; }else{ $arrk[] = $va; } } $returnse = array_combine($arrk,$arrv); } } print_r($returnse);