239 lines
6.7 KiB
PHP
239 lines
6.7 KiB
PHP
|
#!/usr/bin/php -q
|
||
|
<?php
|
||
|
|
||
|
/**
|
||
|
* To run this script, execute something like this:
|
||
|
* `./srdb.cli.php -h localhost -u root -n test -s "findMe" -r "replaceMe"`
|
||
|
* use the --dry-run flag to do a dry run without searching/replacing.
|
||
|
*/
|
||
|
|
||
|
// php 5.3 date timezone requirement, shouldn't affect anything
|
||
|
date_default_timezone_set( 'Europe/London' );
|
||
|
|
||
|
// include the srdb class
|
||
|
require_once( realpath( dirname( __FILE__ ) ) . '/srdb.class.php' );
|
||
|
|
||
|
$opts = array(
|
||
|
'h:' => 'host:',
|
||
|
'n:' => 'name:',
|
||
|
'u:' => 'user:',
|
||
|
'p:' => 'pass:',
|
||
|
'c:' => 'char:',
|
||
|
's:' => 'search:',
|
||
|
'r:' => 'replace:',
|
||
|
't:' => 'tables:',
|
||
|
'i:' => 'include-cols:',
|
||
|
'x:' => 'exclude-cols:',
|
||
|
'g' => 'regex',
|
||
|
'l:' => 'pagesize:',
|
||
|
'z' => 'dry-run',
|
||
|
'e:' => 'alter-engine:',
|
||
|
'a:' => 'alter-collation:',
|
||
|
'v:' => 'verbose:',
|
||
|
'port:',
|
||
|
'help'
|
||
|
);
|
||
|
|
||
|
$required = array(
|
||
|
'h:',
|
||
|
'n:',
|
||
|
'u:',
|
||
|
'p:'
|
||
|
);
|
||
|
|
||
|
function strip_colons( $string ) {
|
||
|
return str_replace( ':', '', $string );
|
||
|
}
|
||
|
|
||
|
// store arg values
|
||
|
$arg_count = $_SERVER[ 'argc' ];
|
||
|
$args_array = $_SERVER[ 'argv' ];
|
||
|
|
||
|
$short_opts = array_keys( $opts );
|
||
|
$short_opts_normal = array_map( 'strip_colons', $short_opts );
|
||
|
|
||
|
$long_opts = array_values( $opts );
|
||
|
$long_opts_normal = array_map( 'strip_colons', $long_opts );
|
||
|
|
||
|
// store array of options and values
|
||
|
$options = getopt( implode( '', $short_opts ), $long_opts );
|
||
|
|
||
|
if ( isset( $options[ 'help' ] ) ) {
|
||
|
echo "
|
||
|
#####################################################################
|
||
|
|
||
|
interconnect/it Safe Search & Replace tool
|
||
|
|
||
|
#####################################################################
|
||
|
|
||
|
This script allows you to search and replace strings in your database
|
||
|
safely without breaking serialised PHP.
|
||
|
|
||
|
Please report any bugs or fork and contribute to this script via
|
||
|
Github: https://github.com/interconnectit/search-replace-db
|
||
|
|
||
|
Argument values are strings unless otherwise specified.
|
||
|
|
||
|
ARGS
|
||
|
-h, --host
|
||
|
Required. The hostname of the database server.
|
||
|
-n, --name
|
||
|
Required. Database name.
|
||
|
-u, --user
|
||
|
Required. Database user.
|
||
|
-p, --pass
|
||
|
Required. Database user's password.
|
||
|
--port
|
||
|
Optional. Port on database server to connect to.
|
||
|
The default is 3306. (MySQL default port).
|
||
|
-s, --search
|
||
|
String to search for or `preg_replace()` style
|
||
|
regular expression.
|
||
|
-r, --replace
|
||
|
None empty string to replace search with or
|
||
|
`preg_replace()` style replacement.
|
||
|
-t, --tables
|
||
|
If set only runs the script on the specified table, comma
|
||
|
separate for multiple values.
|
||
|
-i, --include-cols
|
||
|
If set only runs the script on the specified columns, comma
|
||
|
separate for multiple values.
|
||
|
-x, --exclude-cols
|
||
|
If set excludes the specified columns, comma separate for
|
||
|
multiple values.
|
||
|
-g, --regex [no value]
|
||
|
Treats value for -s or --search as a regular expression and
|
||
|
-r or --replace as a regular expression replacement.
|
||
|
-l, --pagesize
|
||
|
How rows to fetch at a time from a table.
|
||
|
-z, --dry-run [no value]
|
||
|
Prevents any updates happening so you can preview the number
|
||
|
of changes to be made
|
||
|
-e, --alter-engine
|
||
|
Changes the database table to the specified database engine
|
||
|
eg. InnoDB or MyISAM. If specified search/replace arguments
|
||
|
are ignored. They will not be run simultaneously.
|
||
|
-a, --alter-collation
|
||
|
Changes the database table to the specified collation
|
||
|
eg. utf8_unicode_ci. If specified search/replace arguments
|
||
|
are ignored. They will not be run simultaneously.
|
||
|
-v, --verbose [true|false]
|
||
|
Defaults to true, can be set to false to run script silently.
|
||
|
--help
|
||
|
Displays this help message ;)
|
||
|
";
|
||
|
exit;
|
||
|
}
|
||
|
|
||
|
// missing field flag, show all missing instead of 1 at a time
|
||
|
$missing_arg = false;
|
||
|
|
||
|
// check required args are passed
|
||
|
foreach( $required as $key ) {
|
||
|
$short_opt = strip_colons( $key );
|
||
|
$long_opt = strip_colons( $opts[ $key ] );
|
||
|
if ( ! isset( $options[ $short_opt ] ) && ! isset( $options[ $long_opt ] ) ) {
|
||
|
fwrite( STDERR, "Error: Missing argument, -{$short_opt} or --{$long_opt} is required.\n" );
|
||
|
$missing_arg = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// bail if requirements not met
|
||
|
if ( $missing_arg ) {
|
||
|
fwrite( STDERR, "Please enter the missing arguments.\n" );
|
||
|
exit( 1 );
|
||
|
}
|
||
|
|
||
|
// new args array
|
||
|
$args = array(
|
||
|
'verbose' => true,
|
||
|
'dry_run' => false
|
||
|
);
|
||
|
|
||
|
// create $args array
|
||
|
foreach( $options as $key => $value ) {
|
||
|
|
||
|
// transpose keys
|
||
|
if ( ( $is_short = array_search( $key, $short_opts_normal ) ) !== false )
|
||
|
$key = $long_opts_normal[ $is_short ];
|
||
|
|
||
|
// boolean options as is, eg. a no value arg should be set true
|
||
|
if ( in_array( $key, $long_opts ) )
|
||
|
$value = true;
|
||
|
|
||
|
switch ( $key ) {
|
||
|
// boolean options.
|
||
|
case 'verbose':
|
||
|
$value = (boolean)filter_var( $value, FILTER_VALIDATE_BOOLEAN );
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// change to underscores
|
||
|
$key = str_replace( '-', '_', $key );
|
||
|
|
||
|
$args[ $key ] = $value;
|
||
|
}
|
||
|
|
||
|
// modify the log output
|
||
|
class icit_srdb_cli extends icit_srdb {
|
||
|
|
||
|
public function log( $type = '' ) {
|
||
|
|
||
|
$args = array_slice( func_get_args(), 1 );
|
||
|
|
||
|
$output = "";
|
||
|
|
||
|
switch( $type ) {
|
||
|
case 'error':
|
||
|
list( $error_type, $error ) = $args;
|
||
|
$output .= "$error_type: $error";
|
||
|
break;
|
||
|
case 'search_replace_table_start':
|
||
|
list( $table, $search, $replace ) = $args;
|
||
|
$output .= "{$table}: replacing {$search} with {$replace}";
|
||
|
break;
|
||
|
case 'search_replace_table_end':
|
||
|
list( $table, $report ) = $args;
|
||
|
$time = number_format( $report[ 'end' ] - $report[ 'start' ], 8 );
|
||
|
$output .= "{$table}: {$report['rows']} rows, {$report['change']} changes found, {$report['updates']} updates made in {$time} seconds";
|
||
|
break;
|
||
|
case 'search_replace_end':
|
||
|
list( $search, $replace, $report ) = $args;
|
||
|
$time = number_format( $report[ 'end' ] - $report[ 'start' ], 8 );
|
||
|
$dry_run_string = $this->dry_run ? "would have been" : "were";
|
||
|
$output .= "
|
||
|
Replacing {$search} with {$replace} on {$report['tables']} tables with {$report['rows']} rows
|
||
|
{$report['change']} changes {$dry_run_string} made
|
||
|
{$report['updates']} updates were actually made
|
||
|
It took {$time} seconds";
|
||
|
break;
|
||
|
case 'update_engine':
|
||
|
list( $table, $report, $engine ) = $args;
|
||
|
$output .= $table . ( $report[ 'converted' ][ $table ] ? ' has been' : 'has not been' ) . ' converted to ' . $engine;
|
||
|
break;
|
||
|
case 'update_collation':
|
||
|
list( $table, $report, $collation ) = $args;
|
||
|
$output .= $table . ( $report[ 'converted' ][ $table ] ? ' has been' : 'has not been' ) . ' converted to ' . $collation;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( $this->verbose )
|
||
|
echo $output . "\n";
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
$report = new icit_srdb_cli( $args );
|
||
|
|
||
|
// Only print a separating newline if verbose mode is on to separate verbose output from result
|
||
|
if ($args[ 'verbose' ]) {
|
||
|
echo "\n";
|
||
|
}
|
||
|
|
||
|
if ( $report && ( ( isset( $args[ 'dry_run' ] ) && $args[ 'dry_run' ] ) || empty( $report->errors[ 'results' ] ) ) ) {
|
||
|
echo "And we're done!\n";
|
||
|
} else {
|
||
|
echo "Check the output for errors. You may need to ensure verbose output is on by using -v or --verbose.\n";
|
||
|
}
|