is_wordpress() ) { // prevent warnings if the charset and collate aren't defined if ( !defined( 'DB_CHARSET') ) { define( 'DB_CHARSET', 'utf8' ); } if ( !defined( 'DB_COLLATE') ) { define( 'DB_COLLATE', '' ); } // populate db details $name = DB_NAME; $user = DB_USER; $pass = DB_PASSWORD; // Port and host need to be split apart. if ( strstr( DB_HOST, ':' ) !== false ) { $parts = explode( ':', DB_HOST ); $host = $parts[0]; $port_input = $parts[1]; $port = abs( (int)$port_input ); } else { $host = DB_HOST; $port = 3306; } $charset = DB_CHARSET; $collate = DB_COLLATE; $this->response( $name, $user, $pass, $host, $port, $charset, $collate ); } elseif( $bootstrap && $this->is_drupal() ) { $database = Database::getConnection(); $database_opts = $database->getConnectionOptions(); // populate db details $name = $database_opts[ 'database' ]; $user = $database_opts[ 'username' ]; $pass = $database_opts[ 'password' ]; $host = $database_opts[ 'host' ]; $port = $database_opts[ 'port' ]; $charset = 'utf8'; $collate = ''; $port_as_string = (string)$port ? (string)$port : "0"; if ( (string)abs( (int)$port ) !== $port_as_string ) { $port = 3306; } else { $port = (string)abs( (int)$port ); } $this->response( $name, $user, $pass, $host, $port, $charset, $collate ); } else { $this->response(); } } public function response( $name = '', $user = '', $pass = '', $host = '127.0.0.1', $port = 3306, $charset = 'utf8', $collate = '' ) { // always override with post data if ( isset( $_POST[ 'name' ] ) ) { $name = $_POST[ 'name' ]; // your database $user = $_POST[ 'user' ]; // your db userid $pass = $_POST[ 'pass' ]; // your db password $host = $_POST[ 'host' ]; // normally localhost, but not necessarily. $port_input = $_POST[ 'port' ]; // Make sure that the string version of absint(port) is identical to the string input. // This prevents expressions, decimals, spaces, etc. $port_as_string = (string)$port_input ? (string)$port_input : "0"; if ( (string)abs( (int)$port_input ) !== $port_as_string ) { // Mangled port number: non numeric. $this->add_error('Port number must be a positive integer. If you are unsure, try the default port 3306.', 'db'); // Force a bad run by supplying nonsense. $port = "nonsense"; } else { $port = abs( (int)$port_input ); } $charset = 'utf8'; // isset( $_POST[ 'char' ] ) ? stripcslashes( $_POST[ 'char' ] ) : ''; // your db charset $collate = ''; } // Search replace details $search = isset( $_POST[ 'search' ] ) ? $_POST[ 'search' ] : ''; $replace = isset( $_POST[ 'replace' ] ) ? $_POST[ 'replace' ] : ''; // regex options $regex = isset( $_POST[ 'regex' ] ); $regex_i = isset( $_POST[ 'regex_i' ] ); $regex_m = isset( $_POST[ 'regex_m' ] ); $regex_s = isset( $_POST[ 'regex_s' ] ); $regex_x = isset( $_POST[ 'regex_x' ] ); // Tables to scanned $tables = isset( $_POST[ 'tables' ] ) && is_array( $_POST[ 'tables' ] ) ? $_POST[ 'tables' ] : array( ); if ( isset( $_POST[ 'use_tables' ] ) && $_POST[ 'use_tables' ] == 'all' ) $tables = array(); // exclude / include columns $exclude_cols = isset( $_POST[ 'exclude_cols' ] ) ? $_POST[ 'exclude_cols' ] : array(); $include_cols = isset( $_POST[ 'include_cols' ] ) ? $_POST[ 'include_cols' ] : array(); foreach( array( 'exclude_cols', 'include_cols' ) as $maybe_string_arg ) { if ( is_string( $$maybe_string_arg ) ) $$maybe_string_arg = array_filter( array_map( 'trim', explode( ',', $$maybe_string_arg ) ) ); } // update class vars $vars = array( 'name', 'user', 'pass', 'host', 'port', 'charset', 'collate', 'tables', 'search', 'replace', 'exclude_cols', 'include_cols', 'regex', 'regex_i', 'regex_m', 'regex_s', 'regex_x' ); foreach( $vars as $var ) { if ( isset( $$var ) ) $this->set( $var, $$var ); } // are doing something? $show = ''; if ( isset( $_POST[ 'submit' ] ) ) { if ( is_array( $_POST[ 'submit' ] ) ) $show = key( $_POST[ 'submit' ] ); if ( is_string( $_POST[ 'submit' ] ) ) $show = preg_replace( '/submit\[([a-z0-9]+)\]/', '$1', $_POST[ 'submit' ] ); } // is it an AJAX call $ajax = isset( $_POST[ 'ajax' ] ); // body callback $html = 'ui'; switch( $show ) { // remove search replace case 'delete': // determine if it's the folder of compiled version if ( basename( __FILE__ ) == 'index.php' ) $path = str_replace( basename( __FILE__ ), '', __FILE__ ); else $path = __FILE__; $delete_script_success = $this->delete_script( $path ); if ( self::DELETE_SCRIPT_FAIL_UNSAFE === $delete_script_success) { $this->add_error( 'Delete aborted! You seem to have placed Search/Replace into your WordPress or Drupal root. Please remove Search/Replace manually.', 'delete' ); } else { if ( ( self::DELETE_SCRIPT_SUCCESS === $delete_script_success ) && !( is_file( __FILE__ ) && file_exists( __FILE__ ) ) ) { $this->add_error( 'Search/Replace has been successfully removed from your server', 'delete' ); } else { $this->add_error( 'Could not fully delete Search/Replace. You will have to delete it manually', 'delete' ); } } $html = 'deleted'; break; case 'liverun': // bsy-web, 20130621: Check live run was explicitly clicked and only set false then $this->set( 'dry_run', false ); case 'dryrun': // build regex string // non UI implements can just pass in complete regex string if ( $this->regex ) { $mods = ''; if ( $this->regex_i ) $mods .= 'i'; if ( $this->regex_s ) $mods .= 's'; if ( $this->regex_m ) $mods .= 'm'; if ( $this->regex_x ) $mods .= 'x'; $this->search = '/' . $this->search . '/' . $mods; } // call search replace class $parent = parent::__construct( array( 'name' => $this->get( 'name' ), 'user' => $this->get( 'user' ), 'pass' => $this->get( 'pass' ), 'host' => $this->get( 'host' ), 'port' => $this->get( 'port' ), 'search' => $this->get( 'search' ), 'replace' => $this->get( 'replace' ), 'tables' => $this->get( 'tables' ), 'dry_run' => $this->get( 'dry_run' ), 'regex' => $this->get( 'regex' ), 'exclude_cols' => $this->get( 'exclude_cols' ), 'include_cols' => $this->get( 'include_cols' ) ) ); break; case 'innodb': // call search replace class to alter engine $parent = parent::__construct( array( 'name' => $this->get( 'name' ), 'user' => $this->get( 'user' ), 'pass' => $this->get( 'pass' ), 'host' => $this->get( 'host' ), 'port' => $this->get( 'port' ), 'tables' => $this->get( 'tables' ), 'alter_engine' => 'InnoDB', ) ); break; case 'utf8': // call search replace class to alter engine $parent = parent::__construct( array( 'name' => $this->get( 'name' ), 'user' => $this->get( 'user' ), 'pass' => $this->get( 'pass' ), 'host' => $this->get( 'host' ), 'port' => $this->get( 'port' ), 'tables' => $this->get( 'tables' ), 'alter_collation' => 'utf8_unicode_ci', ) ); break; case 'utf8mb4': // call search replace class to alter engine $parent = parent::__construct( array( 'name' => $this->get( 'name' ), 'user' => $this->get( 'user' ), 'pass' => $this->get( 'pass' ), 'host' => $this->get( 'host' ), 'port' => $this->get( 'port' ), 'tables' => $this->get( 'tables' ), 'alter_collation' => 'utf8mb4_unicode_ci', ) ); break; case 'update': default: // get tables or error messages $this->db_setup(); if ( $this->db_valid() ) { // get engines $this->set( 'engines', $this->get_engines() ); // get tables $this->set( 'all_tables', $this->get_tables() ); } break; } $info = array( 'table_select' => $this->table_select( false ), 'engines' => $this->get( 'engines' ) ); // set header again before output in case WP does it's thing header( 'HTTP/1.1 200 OK' ); if ( ! $ajax ) { $this->html( $html ); } else { // return json version of results header( 'Content-Type: application/json' ); echo json_encode( array( 'errors' => $this->get( 'errors' ), 'report' => $this->get( 'report' ), 'info' => $info ) ); exit; } } public function exceptions( $exception ) { $this->add_error( '

' . $exception->getMessage() . '

' ); } public function errors( $no, $message, $file, $line ) { $this->add_error( '

' . "{$no}: {$message} in {$file} on line {$line}" . '

', 'results' ); } public function fatal_handler() { $error = error_get_last(); if( $error !== NULL ) { $errno = $error["type"]; $errfile = $error["file"]; $errline = $error["line"]; $errstr = $error["message"]; if ( $errno == 1 ) { header( 'HTTP/1.1 200 OK' ); $this->add_error( '

Could not bootstrap environment.
' . "Fatal error in {$errfile}, line {$errline}. {$errstr}" . '

', 'environment' ); $this->response(); } } } /** * Return an array of all files and directories recursively below $path. * * If $path is a file, returns an array containing just that filename. * * @param string $path directory/file path. * * @return array */ public function determine_all_files_below_path( $path ) { // A file contains only 'itself'. if ( is_file( $path ) ) { return array( $path ); } $directory_contents = glob( $path . '/*' ); $full_recursive_contents = array(); // Every directory contains all of its files, plus 'itself'. foreach ( $directory_contents as $item_filename ) { $full_recursive_contents = array_merge($full_recursive_contents, $this->determine_all_files_below_path( $item_filename ) ); } $full_recursive_contents = array_merge($full_recursive_contents, array( $path ) ); return $full_recursive_contents; } /** * @param $path Filename to inspect * * @return boolean true if it is most likely nothing to do with WordPress or Drupal. */ public function safe_to_delete_filename( $path ) { // You'll have to edit this list if // more files are included in SRDB. // Using an untargeted deletion operation is // entirely unacceptable. $srdb_filename_whitelist = array( 'composer.json', 'index.php', 'package.json', 'README.md', 'srdb.class.php', 'srdb.cli.php', 'srdb-tests', 'charset-test.php', 'DataSet.xml', 'DataSetGenerator.php', 'db.sql', 'SrdbTest.php' ); foreach ( $srdb_filename_whitelist as $whitelist_item ) { if ( false !== stripos( $path, $whitelist_item ) ) { return true; } } return false; } /** * Checks an array of fully qualified filenames to see if they are all * SRDB filenames. * * @param array $array_of_paths * * @return boolean true if all paths are most likely SRDB files. */ public function safe_to_delete_all_filenames( $array_of_paths ) { foreach ( $array_of_paths as $path ) { if ( !$this->safe_to_delete_filename( $path ) ) { return false; } } return true; } const DELETE_SCRIPT_SUCCESS = 0; const DELETE_SCRIPT_FAIL_CANT_DELETE = -1; const DELETE_SCRIPT_FAIL_UNSAFE = -2; /** * http://stackoverflow.com/questions/3349753/delete-directory-with-files-in-it * * @param string $path directory/file path * * @return integer DELETE_SCRIPT_SUCCESS for success, DELETE_SCRIPT_FAIL_CANT_DELETE for physical failure, DELETE_SCRIPT_FAIL_UNSAFE for 'Shouldn't delete wordpress' failure. */ public function delete_script( $path ) { $all_targets = $this->determine_all_files_below_path( $path ); $all_targets_minus_containing_directory = $all_targets; // Proceed if all files identified (except the current directory) // match a list of whitelisted deletable filenames. array_pop( $all_targets_minus_containing_directory ); $can_proceed = $this->safe_to_delete_all_filenames( $all_targets_minus_containing_directory ); if ( !$can_proceed ) return self::DELETE_SCRIPT_FAIL_UNSAFE; foreach ( $all_targets as $target_filename ) { if ( is_file( $target_filename ) ) { if ( false === @unlink( $target_filename ) ) return self::DELETE_SCRIPT_FAIL_CANT_DELETE; } else { if ( false === @rmdir( $target_filename ) ) return self::DELETE_SCRIPT_FAIL_CANT_DELETE; } } return self::DELETE_SCRIPT_SUCCESS; } /** * Attempts to detect a WordPress installation and bootstraps the environment with it * * @return bool Whether it is a WP install and we have database credentials */ public function is_wordpress() { $path_mod = ''; $depth = 0; $max_depth = 4; $bootstrap_file = 'wp-blog-header.php'; while( ! file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) { $path_mod .= '/..'; if ( $depth++ >= $max_depth ) break; } if ( file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) { // store WP path $this->path = dirname( __FILE__ ) . $path_mod; // just in case we're white screening try { // need to make as many of the globals available as possible or things can break // (globals suck) global $wp, $wpdb, $wp_query, $wp_the_query, $wp_version, $wp_db_version, $tinymce_version, $manifest_version, $required_php_version, $required_mysql_version, $post, $posts, $wp_locale, $authordata, $more, $numpages, $currentday, $currentmonth, $page, $pages, $multipage, $wp_rewrite, $wp_filesystem, $blog_id, $request, $wp_styles, $wp_taxonomies, $wp_post_types, $wp_filter, $wp_object_cache, $query_string, $single, $post_type, $is_iphone, $is_chrome, $is_safari, $is_NS4, $is_opera, $is_macIE, $is_winIE, $is_gecko, $is_lynx, $is_IE, $is_apache, $is_iis7, $is_IIS; // prevent multisite redirect define( 'WP_INSTALLING', true ); // prevent super/total cache define( 'DONOTCACHEDB', true ); define( 'DONOTCACHEPAGE', true ); define( 'DONOTCACHEOBJECT', true ); define( 'DONOTCDN', true ); define( 'DONOTMINIFY', true ); // cancel batcache if ( function_exists( 'batcache_cancel' ) ) batcache_cancel(); // bootstrap WordPress require( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ); $this->set( 'path', ABSPATH ); $this->set( 'is_wordpress', true ); return true; } catch( Exception $error ) { // try and get database values using regex approach $db_details = $this->define_find( $this->path . '/wp-config.php' ); if ( $db_details ) { define( 'DB_NAME', $db_details[ 'name' ] ); define( 'DB_USER', $db_details[ 'user' ] ); define( 'DB_PASSWORD', $db_details[ 'pass' ] ); define( 'DB_HOST', $db_details[ 'host' ] ); define( 'DB_CHARSET', $db_details[ 'char' ] ); define( 'DB_COLLATE', $db_details[ 'coll' ] ); // additional error message $this->add_error( 'WordPress detected but could not bootstrap environment. There might be a PHP error, possibly caused by changes to the database', 'db' ); } if ( $db_details ) return true; } } return false; } public function is_drupal() { $path_mod = ''; $depth = 0; $max_depth = 4; $bootstrap_file = 'includes/bootstrap.inc'; while( ! file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) { $path_mod .= '/..'; if ( $depth++ >= $max_depth ) break; } if ( file_exists( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ) ) { try { // require the bootstrap include require_once( dirname( __FILE__ ) . "{$path_mod}/{$bootstrap_file}" ); // define drupal root if ( ! defined( 'DRUPAL_ROOT' ) ) define( 'DRUPAL_ROOT', dirname( __FILE__ ) . $path_mod ); // load drupal drupal_bootstrap( DRUPAL_BOOTSTRAP_FULL ); // confirm environment $this->set( 'is_drupal', true ); return true; } catch( Exception $error ) { // We can't add_error here as 'db' because if the db errors array is not empty, the interface doesn't activate! // This is a consequence of the 'complete' method in JavaScript $this->add_error( 'Drupal detected but could not bootstrap to retrieve configuration. There might be a PHP error, possibly caused by changes to the database', 'recoverable_db' ); } } return false; } /** * Search through the file name passed for a set of defines used to set up * WordPress db access. * * @param string $filename The file name we need to scan for the defines. * * @return array List of db connection details. */ public function define_find( $filename = 'wp-config.php' ) { if ( $filename == 'wp-config.php' ) { $filename = dirname( __FILE__ ) . '/' . basename( $filename ); // look up one directory if config file doesn't exist in current directory if ( ! file_exists( $filename ) ) $filename = dirname( __FILE__ ) . '/../' . basename( $filename ); } if ( file_exists( $filename ) && is_file( $filename ) && is_readable( $filename ) ) { $file = @fopen( $filename, 'r' ); $file_content = fread( $file, filesize( $filename ) ); @fclose( $file ); } preg_match_all( '/define\s*?\(\s*?([\'"])(DB_NAME|DB_USER|DB_PASSWORD|DB_HOST|DB_CHARSET|DB_COLLATE)\1\s*?,\s*?([\'"])([^\3]*?)\3\s*?\)\s*?;/si', $file_content, $defines ); if ( ( isset( $defines[ 2 ] ) && ! empty( $defines[ 2 ] ) ) && ( isset( $defines[ 4 ] ) && ! empty( $defines[ 4 ] ) ) ) { foreach( $defines[ 2 ] as $key => $define ) { switch( $define ) { case 'DB_NAME': $name = $defines[ 4 ][ $key ]; break; case 'DB_USER': $user = $defines[ 4 ][ $key ]; break; case 'DB_PASSWORD': $pass = $defines[ 4 ][ $key ]; break; case 'DB_HOST': $host = $defines[ 4 ][ $key ]; break; case 'DB_CHARSET': $char = $defines[ 4 ][ $key ]; break; case 'DB_COLLATE': $coll = $defines[ 4 ][ $key ]; break; } } } return array( 'host' => $host, 'name' => $name, 'user' => $user, 'pass' => $pass, 'char' => $char, 'coll' => $coll ); } /** * Display the current url * */ public function self_link() { return 'http://' . $_SERVER[ 'HTTP_HOST' ] . rtrim( $_SERVER[ 'REQUEST_URI' ], '/' ); } /** * Simple html escaping * * @param string $string Thing that needs escaping * @param bool $echo Do we echo or return? * * @return string Escaped string. */ public function esc_html_attr( $string = '', $echo = false ) { $output = htmlentities( $string, ENT_QUOTES, 'UTF-8' ); if ( $echo ) echo $output; else return $output; } public function checked( $value, $value2, $echo = true ) { $output = $value == $value2 ? ' checked="checked"' : ''; if ( $echo ) echo $output; return $output; } public function selected( $value, $value2, $echo = true ) { $output = $value == $value2 ? ' selected="selected"' : ''; if ( $echo ) echo $output; return $output; } public function get_errors( $type ) { if ( ! isset( $this->errors[ $type ] ) || ! count( $this->errors[ $type ] ) ) return; echo '
'; foreach( $this->errors[ $type ] as $error ) { if ( $error instanceof Exception ) echo '

' . $error->getMessage() . '

'; elseif ( is_string( $error ) ) echo $error; } echo '
'; } public function get_report( $table = null ) { $report = $this->get( 'report' ); if ( empty( $report ) ) return; $dry_run = $this->get( 'dry_run' ); $search = $this->get( 'search' ); $replace = $this->get( 'replace' ); // Calc the time taken. $time = array_sum( explode( ' ', $report[ 'end' ] ) ) - array_sum( explode( ' ', $report[ 'start' ] ) ); $srch_rplc_input_phrase = $dry_run ? 'searching for "' . $search . '" (to be replaced by "' . $replace . '")' : 'replacing "' . $search . '" with "' . $replace . '"'; echo '
'; echo '

Report

'; echo '

'; printf( 'In the process of %s we scanned %d tables with a total of %d rows, %d cells %s changed. %d db updates were actually performed. It all took %f seconds.', $srch_rplc_input_phrase, $report[ 'tables' ], $report[ 'rows' ], $report[ 'change' ], $dry_run ? 'would have been' : 'were', $report[ 'updates' ], $time ); echo '

'; echo ' '; foreach( $report[ 'table_reports' ] as $table => $t_report ) { $t_time = array_sum( explode( ' ', $t_report[ 'end' ] ) ) - array_sum( explode( ' ', $t_report[ 'start' ] ) ); echo ' '; printf( ' ', $table, $t_report[ 'rows' ], $t_report[ 'change' ], $t_report[ 'updates' ], $t_time ); echo ' '; } echo '
Table Rows Cells changed Updates Seconds
%s: %d %d %d %f
'; echo '
'; } public function table_select( $echo = true ) { $table_select = ''; if ( ! empty( $this->all_tables ) ) { $table_select .= ''; } if ( $echo ) echo $table_select; return $table_select; } public function ui() { // Warn if we're running in safe mode as we'll probably time out. if ( ini_get( 'safe_mode' ) ) { ?>

Warning

Safe mode is on so you may run into problems if it takes longer than %s seconds to process your request.

', ini_get( 'max_execution_time' ) ); ?>

db details

get_errors( 'environment' ); ?> get_errors( 'recoverable_db' ); ?> get_errors( 'db' ); ?>

tables

get_errors( 'tables' ); ?>
table_select(); ?>

actions

get_errors( 'results' ); ?>
db_valid() ) echo 'disabled="disabled"'; ?> class="db-required" /> db_valid() ) echo 'disabled="disabled"'; ?> class="db-required" /> / get( 'engines' ) ) ) { ?> db_valid() ) echo 'disabled="disabled"'; ?> class="db-required secondary field-advanced" /> db_valid() ) echo 'disabled="disabled"'; ?> class="db-required secondary field-advanced" /> db_valid() ) echo 'disabled="disabled"'; ?> class="db-required secondary field-advanced" />
get_report(); ?>

delete

Once you’re done click the delete me button to secure your server

interconnect/it

Safe Search and Replace on Database with Serialized Data v3.1.0

This developer/sysadmin tool carries out search/replace functions on MySQL DBs and can handle serialised PHP Arrays and Objects.

WARNINGS! Ensure data is backed up. We take no responsibility for any damage caused by this script or its misuse. DB Connection Settings are auto-filled when WordPress or Drupal is detected but can be confused by commented out settings so CHECK! There is NO UNDO! Be careful running this script on a production server.

Don't Forget to Remove Me!

Delete this utility from your server after use by clicking the 'delete me' button. It represents a major security threat to your database if maliciously used.

If you have feedback or want to contribute to this script click the delete button to find out how.

We don't put links on the search replace UI itself to avoid seeing URLs for the script in our access logs.

Again, use Of This Script Is Entirely At Your Own Risk

The easiest and safest way to use this script is to copy your site's files and DB to a new location. You then, if required, fix up your .htaccess and wp-config.php appropriately. Once done, run this script, select your tables (in most cases all of them) and then enter the search replace strings. You can press back in your browser to do this several times, as may be required in some cases.

interconnect/it

get_errors( 'delete' ); ?>

Thanks for using our search/replace tool! We’d really appreciate it if you took a minute to join our mailing list and check out some of our other products.

newsletter


contribute

Got suggestions? Found a bug? Want to contribute code? Join us on Github!

blogs

We couldn't load our blog feed for some reason so here's a link instead!

products

We couldn't load our product feed for some reason so here's a link instead!

regex ? 'regex-on' : 'regex-off'; ?> interconnect/it : search replace db meta(); ?> css(); ?> js(); ?> $body(); ?>