Skip to content

Commit 377328f

Browse files
committed
feat: add --replace-keys flag to replace array keys
This change attempts to correct issue #137 by replacing keys for string arrays with a new --replace-keys flag. Existing default behavior remains unchanged when the flag is absent. Refs: #137
1 parent 35e214e commit 377328f

3 files changed

Lines changed: 31 additions & 17 deletions

File tree

features/search-replace.feature

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -328,18 +328,6 @@ Feature: Do global search/replace
328328
"""
329329
{"sr-key-new":"value"}
330330
"""
331-
332-
When I run `wp search-replace 'http://newdomain.com' 'http://example.com' wp_posts --include-columns=post_content --precise`
333-
Then STDOUT should be a table containing rows:
334-
| Table | Column | Replacements | Type |
335-
| wp_posts | post_content | 1 | PHP |
336-
337-
When I run `wp post get {POST_ID} --field=post_content`
338-
Then STDOUT should contain:
339-
"""
340-
http:\/\/example.com
341-
"""
342-
343331
@require-mysql
344332
Scenario: Search and replace with quoted strings
345333
Given a WP install

src/Search_Replace_Command.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ class Search_Replace_Command extends WP_CLI_Command {
3737
*/
3838
private $recurse_objects;
3939

40+
/**
41+
* @var bool
42+
*/
43+
private $replace_keys;
44+
4045
/**
4146
* @var bool
4247
*/
@@ -214,6 +219,9 @@ class Search_Replace_Command extends WP_CLI_Command {
214219
* : Enable recursing into objects to replace strings. Defaults to true;
215220
* pass --no-recurse-objects to disable.
216221
*
222+
* [--replace-keys]
223+
* : Enable replacing string keys in serialized arrays.
224+
*
217225
* [--verbose]
218226
* : Prints rows to the console as they're updated.
219227
*
@@ -287,7 +295,7 @@ class Search_Replace_Command extends WP_CLI_Command {
287295
* fi
288296
*
289297
* @param array<string> $args Positional arguments.
290-
* @param array{'old'?: string, 'new'?: string, 'dry-run'?: bool, 'network'?: bool, 'all-tables-with-prefix'?: bool, 'all-tables'?: bool, 'export'?: string, 'export_insert_size'?: string, 'skip-tables'?: string, 'skip-columns'?: string, 'include-columns'?: string, 'precise'?: bool, 'recurse-objects'?: bool, 'verbose'?: bool, 'regex'?: bool, 'regex-flags'?: string, 'regex-delimiter'?: string, 'regex-limit'?: string, 'format': string, 'report'?: bool, 'report-changed-only'?: bool, 'log'?: string, 'before_context'?: string, 'after_context'?: string} $assoc_args Associative arguments.
298+
* @param array{'old'?: string, 'new'?: string, 'dry-run'?: bool, 'network'?: bool, 'all-tables-with-prefix'?: bool, 'all-tables'?: bool, 'export'?: string, 'export_insert_size'?: string, 'skip-tables'?: string, 'skip-columns'?: string, 'include-columns'?: string, 'precise'?: bool, 'recurse-objects'?: bool, 'replace-keys'?: bool, 'verbose'?: bool, 'regex'?: bool, 'regex-flags'?: string, 'regex-delimiter'?: string, 'regex-limit'?: string, 'format': string, 'report'?: bool, 'report-changed-only'?: bool, 'log'?: string, 'before_context'?: string, 'after_context'?: string} $assoc_args Associative arguments.
291299
*/
292300
public function __invoke( $args, $assoc_args ) {
293301
global $wpdb;
@@ -333,6 +341,7 @@ public function __invoke( $args, $assoc_args ) {
333341
$this->dry_run = Utils\get_flag_value( $assoc_args, 'dry-run', false );
334342
$php_only = Utils\get_flag_value( $assoc_args, 'precise', false );
335343
$this->recurse_objects = Utils\get_flag_value( $assoc_args, 'recurse-objects', true );
344+
$this->replace_keys = Utils\get_flag_value( $assoc_args, 'replace-keys', false );
336345
$this->verbose = Utils\get_flag_value( $assoc_args, 'verbose', false );
337346
$this->format = Utils\get_flag_value( $assoc_args, 'format' );
338347
$this->regex = Utils\get_flag_value( $assoc_args, 'regex', false );
@@ -638,7 +647,7 @@ private function php_export_table( $table, $old, $new ) {
638647
'chunk_size' => $chunk_size,
639648
);
640649

641-
$replacer = new SearchReplacer( $old, $new, $this->recurse_objects, $this->regex, $this->regex_flags, $this->regex_delimiter, false, $this->regex_limit );
650+
$replacer = new SearchReplacer( $old, $new, $this->recurse_objects, $this->replace_keys, $this->regex, $this->regex_flags, $this->regex_delimiter, false, $this->regex_limit );
642651
$col_counts = array_fill_keys( $all_columns, 0 );
643652
if ( $this->verbose && 'table' === $this->format ) {
644653
$this->start_time = microtime( true );
@@ -743,7 +752,7 @@ private function php_handle_col( $col, $primary_keys, $table, $old, $new ) {
743752
global $wpdb;
744753

745754
$count = 0;
746-
$replacer = new SearchReplacer( $old, $new, $this->recurse_objects, $this->regex, $this->regex_flags, $this->regex_delimiter, null !== $this->log_handle, $this->regex_limit );
755+
$replacer = new SearchReplacer( $old, $new, $this->recurse_objects, $this->replace_keys, $this->regex, $this->regex_flags, $this->regex_delimiter, null !== $this->log_handle, $this->regex_limit );
747756

748757
$table_sql = self::esc_sql_ident( $table );
749758
$col_sql = self::esc_sql_ident( $col );

src/WP_CLI/SearchReplacer.php

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class SearchReplacer {
3232
*/
3333
private $recurse_objects;
3434

35+
/**
36+
* @var bool
37+
*/
38+
private $replace_keys;
39+
3540
/**
3641
* @var bool
3742
*/
@@ -71,16 +76,18 @@ class SearchReplacer {
7176
* @param string $from String we're looking to replace.
7277
* @param string $to What we want it to be replaced with.
7378
* @param bool $recurse_objects Should objects be recursively replaced?
79+
* @param bool $replace_keys Should array keys be replaced?
7480
* @param bool $regex Whether `$from` is a regular expression.
7581
* @param string $regex_flags Flags for regular expression.
7682
* @param string $regex_delimiter Delimiter for regular expression.
7783
* @param bool $logging Whether logging.
7884
* @param integer $regex_limit The maximum possible replacements for each pattern in each subject string.
7985
*/
80-
public function __construct( $from, $to, $recurse_objects = false, $regex = false, $regex_flags = '', $regex_delimiter = '/', $logging = false, $regex_limit = -1 ) {
86+
public function __construct( $from, $to, $recurse_objects = false, $replace_keys = false, $regex = false, $regex_flags = '', $regex_delimiter = '/', $logging = false, $regex_limit = -1 ) {
8187
$this->from = $from;
8288
$this->to = $to;
8389
$this->recurse_objects = $recurse_objects;
90+
$this->replace_keys = $replace_keys;
8491
$this->regex = $regex;
8592
$this->regex_flags = $regex_flags;
8693
$this->regex_delimiter = $regex_delimiter;
@@ -165,7 +172,17 @@ private function run_recursively( $data, $serialised, $recursion_level = 0, $vis
165172
} elseif ( is_array( $data ) ) {
166173
$keys = array_keys( $data );
167174
foreach ( $keys as $key ) {
168-
$data[ $key ] = $this->run_recursively( $data[ $key ], false, $recursion_level + 1, $visited_data );
175+
$value = $this->run_recursively( $data[ $key ], false, $recursion_level + 1, $visited_data );
176+
if ( $this->replace_keys && is_string( $key ) ) {
177+
$replaced_key = $this->run_recursively( $key, false, $recursion_level + 1, $visited_data );
178+
if ( $replaced_key !== $key ) {
179+
unset( $data[ $key ] );
180+
$data[ $replaced_key ] = $value;
181+
continue;
182+
}
183+
}
184+
185+
$data[ $key ] = $value;
169186
}
170187
} elseif ( $this->recurse_objects && ( is_object( $data ) || $data instanceof \__PHP_Incomplete_Class ) ) {
171188
if ( $data instanceof \__PHP_Incomplete_Class ) {

0 commit comments

Comments
 (0)