Skip to content

Commit 9947e9e

Browse files
authored
Merge pull request #62 from wp-cli/patch-61
Add --regex-limit option.
2 parents be21639 + c52fd0f commit 9947e9e

File tree

3 files changed

+67
-12
lines changed

3 files changed

+67
-12
lines changed

features/search-replace.feature

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ Feature: Do global search/replace
674674
When I run `wp post create --post_title='Title_baz__baz_' --post_content='Content_baz_12345678901234567890_baz_12345678901234567890' --porcelain`
675675
Then save STDOUT as {POST_ID}
676676

677-
When I run `wp search-replace '_baz_' '_' wp_posts --dry-run --log --before_context=10 --after_context=10`
677+
When I run `wp search-replace '_baz_' '_' wp_posts --dry-run --log --before_context=10 --after_context=10`
678678
Then STDOUT should contain:
679679
"""
680680
Success: 2 replacements to be made.
@@ -851,7 +851,7 @@ Feature: Do global search/replace
851851
"""
852852
And STDERR should be empty
853853

854-
When I run `wp search-replace '_b([aeiou])z_' '_$1b\\1z_\0' wp_posts --regex --log --before_context=11 --after_context=11`
854+
When I run `wp search-replace '_b([aeiou])z_' '_$1b\\1z_\0' wp_posts --regex --log --before_context=11 --after_context=11`
855855
Then STDOUT should contain:
856856
"""
857857
Success: Made 2 replacements.
@@ -1055,3 +1055,46 @@ Feature: Do global search/replace
10551055
"""
10561056
a:1:{i:0;O:10:"CornFlakes":0:{}}
10571057
"""
1058+
1059+
Scenario: Regex search/replace with `--regex-limit=1` option
1060+
Given a WP install
1061+
And I run `wp post create --post_content="I have a pen, I have an apple. Pen, pine-apple, apple-pen."`
1062+
1063+
When I run `wp search-replace --regex "ap{2}le" "orange" --regex-limit=1 --log`
1064+
Then STDOUT should contain:
1065+
"""
1066+
I have a pen, I have an orange. Pen, pine-apple, apple-pen.
1067+
"""
1068+
1069+
Scenario: Regex search/replace with `--regex-limit=2` option
1070+
Given a WP install
1071+
And I run `wp post create --post_content="I have a pen, I have an apple. Pen, pine-apple, apple-pen."`
1072+
1073+
When I run `wp search-replace --regex "ap{2}le" "orange" --regex-limit=2 --log`
1074+
Then STDOUT should contain:
1075+
"""
1076+
I have a pen, I have an orange. Pen, pine-orange, apple-pen.
1077+
"""
1078+
1079+
Scenario: Regex search/replace with incorrect or default `--regex-limit`
1080+
Given a WP install
1081+
When I try `wp search-replace '(Hello)\s(world)' '$2, $1' --regex --regex-limit=asdf`
1082+
Then STDERR should be:
1083+
"""
1084+
Error: `--regex-limit` expects a non-zero positive integer or -1.
1085+
"""
1086+
When I try `wp search-replace '(Hello)\s(world)' '$2, $1' --regex --regex-limit=0`
1087+
Then STDERR should be:
1088+
"""
1089+
Error: `--regex-limit` expects a non-zero positive integer or -1.
1090+
"""
1091+
When I try `wp search-replace '(Hello)\s(world)' '$2, $1' --regex --regex-limit=-2`
1092+
Then STDERR should be:
1093+
"""
1094+
Error: `--regex-limit` expects a non-zero positive integer or -1.
1095+
"""
1096+
When I run `wp search-replace '(Hello)\s(world)' '$2, $1' --regex --regex-limit=-1`
1097+
Then STDOUT should contain:
1098+
"""
1099+
Success:
1100+
"""

src/Search_Replace_Command.php

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class Search_Replace_Command extends WP_CLI_Command {
99
private $regex;
1010
private $regex_flags;
1111
private $regex_delimiter;
12+
private $regex_limit = -1;
1213
private $skip_tables;
1314
private $skip_columns;
1415
private $include_columns;
@@ -22,7 +23,6 @@ class Search_Replace_Command extends WP_CLI_Command {
2223
private $log_prefixes = array( '< ', '> ' );
2324
private $log_colors;
2425
private $log_encoding;
25-
private $log_run_data = array();
2626

2727
/**
2828
* Searches/replaces strings in the database.
@@ -108,6 +108,9 @@ class Search_Replace_Command extends WP_CLI_Command {
108108
* [--regex-delimiter=<regex-delimiter>]
109109
* : The delimiter to use for the regex. It must be escaped if it appears in the search string. The default value is the result of `chr(1)`.
110110
*
111+
* [--regex-limit=<regex-limit>]
112+
* : The maximum possible replacements for the regex per row (or per unserialized data bit per row). Defaults to -1 (no limit).
113+
*
111114
* [--format=<format>]
112115
* : Render output in a particular format.
113116
* ---
@@ -165,7 +168,7 @@ public function __invoke( $args, $assoc_args ) {
165168
$total = 0;
166169
$report = array();
167170
$this->dry_run = \WP_CLI\Utils\get_flag_value( $assoc_args, 'dry-run' );
168-
$php_only = \WP_CLI\Utils\get_flag_value( $assoc_args, 'precise' );
171+
$php_only = \WP_CLI\Utils\get_flag_value( $assoc_args, 'precise' );
169172
$this->recurse_objects = \WP_CLI\Utils\get_flag_value( $assoc_args, 'recurse-objects', true );
170173
$this->verbose = \WP_CLI\Utils\get_flag_value( $assoc_args, 'verbose' );
171174
$this->format = \WP_CLI\Utils\get_flag_value( $assoc_args, 'format' );
@@ -180,6 +183,13 @@ public function __invoke( $args, $assoc_args ) {
180183
}
181184
}
182185

186+
if ( null !== ( $regex_limit = \WP_CLI\Utils\get_flag_value( $assoc_args, 'regex-limit' ) ) ) {
187+
if ( ! preg_match( '/^(?:[0-9]+|-1)$/', $regex_limit ) || 0 === (int) $regex_limit ) {
188+
WP_CLI::error( '`--regex-limit` expects a non-zero positive integer or -1.' );
189+
}
190+
$this->regex_limit = (int) $regex_limit;
191+
}
192+
183193
if ( ! empty( $this->regex ) ) {
184194
if ( '' === $this->regex_delimiter ) {
185195
$this->regex_delimiter = chr( 1 );
@@ -402,7 +412,7 @@ private function php_export_table( $table, $old, $new ) {
402412
'chunk_size' => $chunk_size
403413
);
404414

405-
$replacer = new \WP_CLI\SearchReplacer( $old, $new, $this->recurse_objects, $this->regex, $this->regex_flags, $this->regex_delimiter );
415+
$replacer = new \WP_CLI\SearchReplacer( $old, $new, $this->recurse_objects, $this->regex, $this->regex_flags, $this->regex_delimiter, false, $this->regex_limit );
406416
$col_counts = array_fill_keys( $all_columns, 0 );
407417
if ( $this->verbose && 'table' === $this->format ) {
408418
$this->start_time = microtime( true );
@@ -476,7 +486,7 @@ private function php_handle_col( $col, $primary_keys, $table, $old, $new ) {
476486
global $wpdb;
477487

478488
$count = 0;
479-
$replacer = new \WP_CLI\SearchReplacer( $old, $new, $this->recurse_objects, $this->regex, $this->regex_flags, $this->regex_delimiter, null !== $this->log_handle );
489+
$replacer = new \WP_CLI\SearchReplacer( $old, $new, $this->recurse_objects, $this->regex, $this->regex_flags, $this->regex_delimiter, null !== $this->log_handle, $this->regex_limit );
480490

481491
$table_sql = self::esc_sql_ident( $table );
482492
$col_sql = self::esc_sql_ident( $col );
@@ -798,12 +808,11 @@ private function log_bits( $search_regex, $old_data, $old_matches, $new ) {
798808
$diff += strlen( $new ) - strlen( $old_matches[0][ $i ][0] );
799809
$i++;
800810
return $new;
801-
}, $old_data );
811+
}, $old_data, $this->regex_limit, $match_cnt );
802812

803813
$old_bits = $new_bits = array();
804814
$append_next = false;
805815
$last_old_offset = $last_new_offset = 0;
806-
$match_cnt = count( $old_matches[0] );
807816
for ( $i = 0; $i < $match_cnt; $i++ ) {
808817
$old_match = $old_matches[0][ $i ][0];
809818
$old_offset = $old_matches[0][ $i ][1];
@@ -828,8 +837,8 @@ private function log_bits( $search_regex, $old_data, $old_matches, $new ) {
828837
$new_after = \cli\safe_substr( substr( $new_data, $new_end_offset ), 0, $this->log_after_context, false /*is_width*/, $encoding );
829838
// To lessen context duplication in output, shorten the after context if it overlaps with the next match.
830839
if ( $i + 1 < $match_cnt && $old_end_offset + strlen( $old_after ) > $old_matches[0][ $i + 1 ][1] ) {
831-
$old_after = substr( $old_after, 0, $old_matches[0][ $i + 1 ][1] - $old_end_offset );
832-
$new_after = substr( $new_after, 0, $new_matches[0][ $i + 1 ][1] - $new_end_offset );
840+
$old_after = substr( $old_after, 0, $old_matches[0][ $i + 1 ][1] - $old_end_offset );
841+
$new_after = substr( $new_after, 0, $new_matches[0][ $i + 1 ][1] - $new_end_offset );
833842
$after_shortened = true;
834843
// On the next iteration, will append with no before context.
835844
}

src/WP_CLI/SearchReplacer.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class SearchReplacer {
1212
private $regex;
1313
private $regex_flags;
1414
private $regex_delimiter;
15+
private $regex_limit;
1516
private $logging;
1617
private $log_data;
1718
private $max_recursion;
@@ -24,14 +25,16 @@ class SearchReplacer {
2425
* @param string $regex_flags Flags for regular expression.
2526
* @param string $regex_delimiter Delimiter for regular expression.
2627
* @param bool $logging Whether logging.
28+
* @param integer $regex_limit The maximum possible replacements for each pattern in each subject string.
2729
*/
28-
function __construct( $from, $to, $recurse_objects = false, $regex = false, $regex_flags = '', $regex_delimiter = '/', $logging = false ) {
30+
function __construct( $from, $to, $recurse_objects = false, $regex = false, $regex_flags = '', $regex_delimiter = '/', $logging = false, $regex_limit = -1 ) {
2931
$this->from = $from;
3032
$this->to = $to;
3133
$this->recurse_objects = $recurse_objects;
3234
$this->regex = $regex;
3335
$this->regex_flags = $regex_flags;
3436
$this->regex_delimiter = $regex_delimiter;
37+
$this->regex_limit = $regex_limit;
3538
$this->logging = $logging;
3639
$this->clear_log_data();
3740

@@ -116,7 +119,7 @@ private function _run( $data, $serialised, $recursion_level = 0, $visited_data =
116119
$search_regex .= $this->regex_delimiter;
117120
$search_regex .= $this->regex_flags;
118121

119-
$result = preg_replace( $search_regex, $this->to, $data );
122+
$result = preg_replace( $search_regex, $this->to, $data, $this->regex_limit );
120123
if ( null === $result || PREG_NO_ERROR !== preg_last_error() ) {
121124
\WP_CLI::warning(
122125
sprintf(

0 commit comments

Comments
 (0)