Исправить сериализованные данные, поврежденные из-за редактирования базы данных MySQL в текстовом редакторе?

Предыстория: я скачал резервную копию *.sql базы данных моего сайта WordPress и заменил все экземпляры старого префикса таблицы базы данных новым (например, с wp_ по умолчанию на что-то вроде asdfghjkl_).

Я только что узнал, что WordPress использует сериализованные строки PHP в базе данных, и то, что я сделал, нарушило целостность сериализованных длин строк.

Дело в том, что я удалил файл резервной копии незадолго до того, как узнал об этом (поскольку мой сайт все еще работал нормально), и с тех пор установил несколько плагинов. Итак, я не могу вернуться назад, и поэтому я хотел бы знать две вещи:

  1. Как я могу это исправить, если это вообще возможно?

  2. Какие проблемы это может вызвать?

(В этой статье говорится, что, например, блог WordPress может потерять свои настройки и виджеты. Но, похоже, со мной этого не произошло, так как все настройки моего блога остались нетронутыми. Но я понятия не имею, что может быть сломано внутри или какие проблемы возникнут в будущем. Отсюда и этот вопрос)


person its_me    schedule 28.02.2013    source источник
comment
Для исследователей см. этот пост, чтобы исправить количество неправильных байтов в сериализованной строке. stackoverflow.com/a/55074706/2943403 его легче читать, чем у Брэндона Эллиотта, он более совершенен, чем у wsizoo, и более надежный, чем фрагмент Бенуберда, не допускающий точку с запятой.   -  person mickmackusa    schedule 08.04.2019


Ответы (6)


Посетите эту страницу: http://unserialize.onlinephpfunctions.com/

На этой странице вы должны увидеть этот образец сериализованной строки: a:1:{s:4:"Test";s:17:"unserialize here!";}. Возьми кусочек-- s:4:"Test";. Это означает «строка», 4 символа, затем фактическая строка. Я почти уверен, что то, что вы сделали, привело к тому, что количество числовых символов не синхронизировалось со строкой. Поиграйте с инструментом на упомянутом выше сайте, и вы увидите, что вы получите сообщение об ошибке, если, например, измените «Test» на «Tes».

Что вам нужно сделать, так это получить количество символов, соответствующее вашей новой строке. Если вы не испортили какую-либо другую кодировку — удалили двоеточие или что-то в этом роде — это должно решить проблему.

person s_ha_dum    schedule 28.02.2013
comment
Спасибо за это объяснение! После обнаружения проблем я нашел этот код, чтобы снова исправить сериализацию. Проверьте stackoverflow.com/a/38890855/2323296. - person Krunal Panchal; 19.09.2017

Я пришел к этой же проблеме после попытки изменить домен с локального хоста на реальный URL-адрес. После некоторого поиска я нашел ответ в документации Wordpress:

https://codex.wordpress.org/Moving_WordPress

Процитирую то, что там написано:

Чтобы избежать этой проблемы с сериализацией, у вас есть три варианта:

  • Используйте плагины Better Search Replace или Velvet Blues Update URL, если у вас есть > доступ к панели инструментов.
  • Используйте поиск-замену WP-CLI, если ваш хостинг-провайдер (или вы) установили WP-CLI.
  • Запустите поиск и запрос на замену вручную в вашей базе данных. Примечание. Выполняйте поиск и замену только в таблице wp_posts.

В итоге я использовал WP-CLI, который может заменять вещи в базе данных, не нарушая сериализацию: http://wp-cli.org/commands/search-replace/

person carlosvini    schedule 22.09.2015
comment
Спасибо. Не знаю, почему это не привлекло больше внимания. Была та же проблема, и в итоге я также использовал WP-CLI. Это самый простой способ сделать это, если вам нужно изменить много внешности. - person Mario Werner; 26.10.2015
comment
Это было именно то, что я искал, не уверен в более старых версиях, но использование флага --precise является ключевым. - person Tom Raine; 25.03.2018

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

По пути я наткнулся на этот вопрос, и пост Бенуберда помог мне направить меня на правильный путь. Его пример кода не работал в производственной среде со сложными данными, содержащими множество специальных символов и HTML, с очень глубокими уровнями вложенности, и он неправильно обрабатывал определенные экранированные символы и кодировку. Поэтому я немного модифицировал его и потратил бессчетное количество часов на исправление дополнительных ошибок, чтобы моя версия «исправляла» сериализованные данные.

// do some DB query here
while($res = db_fetch($qry)){
    $str = $res->data;
    $sCount=1; // don't try to count manually, which can be inaccurate; let serialize do its thing
    $newstring = unserialize($str);
    if(!$newstring) {
        preg_match_all('/s:([0-9]+):"(.*?)"(?=;)/su',$str,$m);
#           preg_match_all("/s:([0-9]+):(\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\")(?=;)/u",$str,$m); // alternate: almost works but leave quotes in $m[2] output
#           print_r($m); exit;
        foreach($m[1] as $k => $len) {
            /*** Possibly specific to my case: Spyropress Builder in WordPress ***/
            $m_clean = str_replace('\"','"',$m[2][$k]); // convert escaped double quotes so that HTML will render properly
            // if newline is present, it will output directly in the HTML
            // nl2br won't work here (must find literally; not with double quotes!)
            $m_clean = str_replace('\n', '<br />', $m_clean); 
            $m_clean = nl2br($m_clean);  // but we DO need to convert actual newlines also
            /*********************************************************************/
            if($sCount){
                $m_new = $m[0][$k].';'; // we must account for the missing semi-colon not captured in regex!
                // NOTE: If we don't flush the buffers, things like <img src="http://whatever" can be replaced with <img src="//whatever" and break the serialize count!!!                  
                ob_end_flush(); // not sure why this is necessary but cost me 5 hours!!
                $m_ser = serialize($m_clean);
                if($m_new != $m_ser) {
                    print "Replacing: $m_new\n";
                    print "With: $m_ser\n";
                    $str = str_replace($m_new, $m_ser, $str);
                }
            }
            else{
                $m_len = (strlen($m[2][$k]) - substr_count($m[2][$k],'\n'));
                if($len != $m_len) {
                    $newstr='s:'.$m_len.':"'.$m[2][$k].'"';
                    echo "Replacing: {$m[0][$k]}\n";
                    echo "With: $newstr\n\n";
                    $str = str_replace($m_new, $newstr, $str);
                }
            }
        }
        print_r($str); // this is your FIXED serialized data!! Yay!
    }
}

Небольшое гиковское объяснение моих изменений:

  • Я обнаружил, что попытки подсчета с использованием кода Benubird в качестве базы были слишком неточными для больших наборов данных, поэтому в итоге я просто использовал сериализацию, чтобы убедиться, что подсчет точен.
  • Я избегал try/catch, потому что в моем случае попытка увенчалась успехом, но просто вернула пустую строку. Итак, вместо этого я проверяю наличие пустых данных.
  • Я пробовал множество регулярных выражений, но только мод на Benubird точно обрабатывал все случаи. В частности, мне пришлось изменить часть, которая проверяла наличие ";" потому что это будет соответствовать CSS, как «ширина: 100%; высота: 25 пикселей;» и сломал вывод. Итак, я использовал положительный просмотр вперед, чтобы соответствовать только тогда, когда «;» был вне набора двойных кавычек.
  • В моем случае было много новых строк, HTML и экранированных двойных кавычек, поэтому мне пришлось добавить блок, чтобы очистить это.
  • Было несколько странных ситуаций, когда данные неправильно заменялись регулярным выражением, а затем сериализация также считала их неправильно. Я НИЧЕГО не нашел на каких-либо сайтах, чтобы помочь с этим, и, наконец, подумал, что это может быть связано с кэшированием или чем-то в этом роде, и попытался сбросить буфер вывода (ob_end_flush()), что сработало, слава богу!

Надеюсь, это кому-то поможет... У меня ушло почти 20 часов, включая исследования и решение странных проблем! :)

person Brandon Elliott    schedule 03.04.2016
comment
Не могли бы вы создать исполняемый файл для Windows с возможностью поиска и замены строки в файле SQL? Или, по крайней мере, файл PHP, в котором вы можете указать исходный файл, файл назначения, строку поиска и строку замены. - person Vojkan Cvijanovic; 02.05.2017
comment
Как мне это использовать? - person SherylHohman; 28.11.2018
comment
Моя IDE говорит, что $m_new не определен для строки $str = str_replace($m_new, $newstr, $str); - person Vsevolod Azovsky; 28.08.2019

Этот скрипт (https://interconnectit.com/products/search-and-replace-for-wordpress-databases/) может помочь обновить базу данных sql с правильными URL-адресами везде, не сталкиваясь с проблемами сериализованных данных, поскольку он обновит «счетчик символов», который может нарушить синхронизацию ваших URL-адресов. всякий раз, когда появляются сериализованные данные.

Шаги будут такими:

  1. если вы уже импортировали испорченную базу данных (виджеты не работают, параметры темы отсутствуют и т. д.), просто удалите эту базу данных с помощью PhpMyAdmin. То есть удалить все на нем. Затем экспортируйте и имейте под рукой неотредактированный дамп старой базы данных.

  2. Теперь вам нужно импортировать (неотредактированную) старую базу данных во вновь созданную. Вы можете сделать это с помощью импорта или копирования базы данных из PhpMyAdmin. Обратите внимание, что до сих пор мы еще не выполняли поиск и замену; у нас просто есть старое содержимое базы данных и структура в новую базу данных со своим собственным пользователем и паролем. Ваш сайт, вероятно, будет недоступен в этот момент.

  3. Убедитесь, что ваши файлы WordPress только что загружены в нужную папку на сервере, и отредактируйте файл wp-config.php, чтобы он подключался к новой базе данных.
  4. Загрузите скрипт в «секретную» папку — просто из соображений безопасности — на том же уровне, что и wp-admin, wp-content и wp-includes. Не забудьте удалить все это после того, как поиск и замена были выполнены, потому что вы рискуете предоставить информацию о своей БД всему Интернету.
  5. Теперь укажите ваш браузер на секретную папку и используйте прекрасный интерфейс скрипта. Это очень понятно. После использования мы приступаем к полному удалению его с сервера.

Это должно правильно обновить вашу базу данных, без каких-либо проблем с сериализованными данными: новый URL-адрес будет установлен везде, и количество символов сериализованных данных будет соответственно обновлено.

Виджеты, а также настройки темы — два типичных места, которые используют сериализованные данные в WordPress, будут опущены.

Готовое и проверенное решение!

person Peanuts    schedule 08.02.2018

Если ошибка связана с неправильной длиной строк (что я часто видел), то вы сможете адаптировать этот скрипт, чтобы исправить это:

foreach($strings as $key => $str)
{
    try {
        unserialize($str);
    } catch(exception $e) {
        preg_match_all('#s:([0-9]+):"([^;]+)"#',$str,$m);
        foreach($m[1] as $k => $len) {
            if($len != strlen($m[2][$k])) {
                $newstr='s:'.strlen($m[2][$k]).':"'.$m[2][$k].'"';
                echo "len mismatch: {$m[0][$k]}\n";
                echo "should be:    $newstr\n\n";
                $strings[$key] = str_replace($m[0][$k], $newstr, $str);
            }
        }
    }
}
person Benubird    schedule 04.01.2016

Лично мне не нравится работать на PHP или размещать свои учетные данные БД в общедоступном файле. Я создал ruby-скрипт для исправления сериализации, которую вы можете запускать локально:

https://github.com/wsizoo/wordpress-fix-serialization

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

$content_to_fix.gsub!(/s:([0-9]+):\"((.|\n)*?)\";/) {"s:#{$2.bytesize}:\"#{$2}\";"}

Затем я обновляю указанные данные с помощью экранированного запроса на обновление sql.

escaped_fix_content = client.escape($fixed_content)

query = client.query("UPDATE #{$table} SET #{$column} = '#{escaped_fix_content}' WHERE #{$column_identifier} LIKE '#{$column_identifier_value}'")
person wsizoo    schedule 13.10.2016
comment
Можете ли вы обобщить подход и опубликовать часть кода здесь? Ссылки сами по себе не являются ответом, они могут сломаться или их содержимое может быть удалено. - person brichins; 14.10.2016
comment
Спасибо @brichins! Все еще новичок в этом... отредактировано для предоставления контекста. - person wsizoo; 28.12.2016