I’m currently in the process of redeveloping my bespoke website to a WordPress-driven CMS.
The website I’ve been working on is simply my existing URL plus /dev/
, i.e. http://my.website.com/dev/
.
I’ll be moving this website to http://my.website.com
over the weekend, and as such will need to remove all references to the /dev/
URL.
What I’d like to do is, basically, a “find and replace” for /dev
on my database. I can see exactly which tables have this value in, but naturally as per a WordPress install, quite a lot of these fields are serialized data – which a straightforward dump
> open with notepad++
> find & replace
will break.
The code I’ve developed for this purpose is here:
<?php
$look_at[] = array( "table" => "wp_options", "fields" => array( "option_value" ), "id_field" => "option_id" );
$look_at[] = array( "table" => "wp_postmeta", "fields" => array( "meta_value" ), "id_field" => "meta_id" );
$look_at[] = array( "table" => "wp_posts", "fields" => array( "post_content", "guid" ), "id_field" => "ID" );
$look_at[] = array( "table" => "wp_sfmeta", "fields" => array( "meta_value" ), "id_field" => "meta_id" );
$look_at[] = array( "table" => "wp_sfoptions", "fields" => array( "option_value" ), "id_field" => "option_id" );
$look_at[] = array( "table" => "wp_sferrorlog", "fields" => array( "error_text" ), "id_field" => "id" );
for ( $i = 0; $i < sizeof ( $look_at ); $i++ ) {
foreach( $look_at[$i]["fields"] as $field ) {
$sql = 'SELECT `' . $field . '`, `' . $look_at[$i]["id_field"] . '` FROM `' . $look_at[$i]["table"] . '`;';
$res = mysql_query( $sql );
while ( $row = mysql_fetch_assoc( $res ) ) {
$table = $look_at[$i]["table"];
$id_field = $look_at[$i]["id_field"];
$old_val = $row[$field];
$id = $row[$id_field];
$unserialized_value = @unserialize( $old_val );
if ( $old_val === 'b:0;' || $unserialized_value !== false )
$new_val = serialize( str_replace( array( "/dev/", "/dev" ), array( "/", "" ), $unserialized_value ) );
else
$new_val = str_replace( array( "/dev/", "/dev" ), array( "/", "" ), $old_val );
$update_array[] = array( "id_field" => $id_field, "id" => $id, "table" => $table, "key" => $key, "old_val" => $old_val, "new_val" => $new_val );
}
}
}
for ( $i = 0; $i < sizeof( $update_array ); $i++ ) {
if ( $update_array[$i]["old_val"] !== $update_array[$i]["new_val"] )
$updated_sql .= 'UPDATE ' . $update_array[$i]["table"] . ' SET `' . $update_array[$i]["key"] . '` = '' . $update_array[$i]["new_val"] . '' WHERE `' . $update_array[$i]["id_field"] . '` = '' . $update_array[$i]["id"] . '';';
}
mysql_query( $updated_sql );
?>
An example of the serialized data:
a:6:{s:5:"width";s:3:"400";s:6:"height";s:3:"530";s:14:"hwstring_small";s:22:"height='96' width='72'";s:4:"file";s:30:"2011/12/Amazonas-English-1.jpg";s:5:"sizes";a:13:{s:9:"thumbnail";a:3:{s:4:"file";s:30:"Amazonas-English-1-125x165.jpg";s:5:"width";s:3:"125";s:6:"height";s:3:"165";}s:6:"medium";a:3:{s:4:"file";s:30:"Amazonas-English-1-339x450.jpg";s:5:"width";s:3:"339";s:6:"height";s:3:"450";}s:5:"large";s:0:"";s:14:"post-thumbnail";a:3:{s:4:"file";s:30:"Amazonas-English-1-125x165.jpg";s:5:"width";s:3:"125";s:6:"height";s:3:"165";}s:23:"indexleft-species-thumb";a:3:{s:4:"file";s:30:"Amazonas-English-1-200x265.jpg";s:5:"width";s:3:"200";s:6:"height";s:3:"265";}s:13:"species-thumb";a:3:{s:4:"file";s:30:"Amazonas-English-1-288x381.jpg";s:5:"width";s:3:"288";s:6:"height";s:3:"381";}s:17:"indexheader-thumb";a:5:{s:4:"file";s:30:"Amazonas-English-1-400x300.jpg";s:5:"width";s:3:"400";s:6:"height";s:3:"300";s:4:"path";s:38:"2011/12/Amazonas-English-1-400x300.jpg";s:3:"url";s:88:"http://www.xxxxxxxxxxx.com/dev/wp-content/uploads/2011/12/Amazonas-English-1-400x300.jpg";}s:14:"random-thumb-1";a:3:{s:4:"file";s:28:"Amazonas-English-1-56x75.jpg";s:5:"width";s:2:"56";s:6:"height";s:2:"75";}s:14:"random-thumb-2";a:3:{s:4:"file";s:29:"Amazonas-English-1-75x100.jpg";s:5:"width";s:2:"75";s:6:"height";s:3:"100";}s:14:"random-thumb-3";a:3:{s:4:"file";s:29:"Amazonas-English-1-94x125.jpg";s:5:"width";s:2:"94";s:6:"height";s:3:"125";}s:14:"random-thumb-4";a:3:{s:4:"file";s:30:"Amazonas-English-1-113x150.jpg";s:5:"width";s:3:"113";s:6:"height";s:3:"150";}s:14:"random-thumb-5";a:3:{s:4:"file";s:30:"Amazonas-English-1-132x175.jpg";s:5:"width";s:3:"132";s:6:"height";s:3:"175";}s:13:"d4p-bbp-thumb";s:0:"";}s:10:"image_meta";a:10:{s:8:"aperture";s:1:"0";s:6:"credit";s:0:"";s:6:"camera";s:0:"";s:7:"caption";s:0:"";s:17:"created_timestamp";s:1:"0";s:9:"copyright";s:0:"";s:12:"focal_length";s:1:"0";s:3:"iso";s:1:"0";s:13:"shutter_speed";s:1:"0";s:5:"title";s:0:"";}}
ADDITIONAL EDIT
Unfortunately, there are other instances of /dev/
in other serialized arrays, such as this example:
'a:1:{i:0;a:5:{s:4:"type";s:5:"image";s:3:"loc";s:107:"/home/xxxxx/domains/xxxxxxxxx.com/public_html/dev/wp-content/sp-resources/forum-image-uploads/matt/2012/01/";...
Or,
a:1:{i:0;a:5:{s:4:"data";s:88:"Your search - <b>link:http://www.xxxxxxxxx.com/dev/</b> - did not match any documents. ";...
As such, I don’t think a simple preg_replace
(or callback) will do the trick, but I guess an advanced one might?
My questions are:
- Is there a simpler way of doing this?!
- Will the above code run into any problems?
I’m awful at forseeing problems with my code (bad programmer, I do apologise) and as such a little apprehensive about running tests with this code.
FINAL EDIT: WORKING CODE
Because my SQL dump was nearly 100mb
, I had to use WAMP with unlimited memory.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 'On');
ini_set('memory_limit', '-1');
$handle = @fopen("amend-this.sql", "r");
if ($handle) {
while (($buffer = fgets($handle, 4096)) !== false) {
$newLine = preg_replace_callback('@s:(d+)(:"[^"]*www.seriouslyfish.com)/dev@', create_function('$matches', 'return 's:'.($matches[1] - 4).$matches[2];'), $buffer);
$newLine = preg_replace_callback('@s:(d+)(:"[^"]*/home/sfish/domains/seriouslyfish.com/public_html)/dev@', create_function('$matches', 'return 's:'.($matches[1] - 4).$matches[2];'), $newLine);
$newLine = str_replace('http://dunc.seriouslyfish.com/dev/', 'http://www.seriouslyfish.com/', $newLine);
$newLine = str_replace('http://www.seriouslyfish.com/dev/', 'http://www.seriouslyfish.com/', $newLine);
$newLine = str_replace('/dev', '', $newLine);
file_put_contents( "amended.sql", $newLine, FILE_APPEND );
}
fclose($handle);
}
?>
This code put my new SQL file into the same directory (X:wampwww
) for me to manipulate further.
I had a few issues with data-repetition, and there were 67 instances of /dev
still in the file for some reason but I used Notepad++ and WinMerge to sort all of this out and in the end it took me around 45 minutes to search/replace a database of over 90 million characters.
When I had the same problem I ran a mysqldump of the database, then opened in a text editor and just search/replaced the values, before using the SQL to create the new database. Quite simple, surprisingly fast,especially for a one off.
As pointed out, you have the problem with serialized data, so you could do a similar thing with a simple PHP file:
Note: this works on a mysqldump, if you’re testing, you’ll need to remove the
\
before the"
s in thepreg_replace_callback
s – this is just mysqldump escaping quotes.Also Note: There are two preg replaces (one for normal URLs and one for server paths), and one str replace for standard URLs left over.
There is the serialization fixer wordpress plugin which does it for the non-programmer: http://davidcoveney.com/575/php-serialization-fix-for-wordpress-migrations/
You can also do it with PHP.
Also, here is a sample MySQL code which does it:
https://data.stackexchange.com/drupal%20answersmeta/query/80128/sql-search-and-replace
Take care, since this is a very dangerous tool.
Couldn’t you just use WP CLI for this?