@@ -134,16 +134,24 @@ class Search_Replace_Command extends WP_CLI_Command {
134134 *
135135 * ## OPTIONS
136136 *
137- * <old>
137+ * [ <old>]
138138 * : A string to search for within the database.
139139 *
140- * <new>
140+ * [ <new>]
141141 * : Replace instances of the first string with this new string.
142142 *
143143 * [<table>...]
144144 * : List of database tables to restrict the replacement to. Wildcards are
145145 * supported, e.g. `'wp_*options'` or `'wp_post*'`.
146146 *
147+ * [--old=<value>]
148+ * : An alternative way to specify the search string. Use this when the
149+ * search string starts with '--' (e.g., --old='--some-text').
150+ *
151+ * [--new=<value>]
152+ * : An alternative way to specify the replacement string. Use this when the
153+ * replacement string starts with '--' (e.g., --new='--other-text').
154+ *
147155 * [--dry-run]
148156 * : Run the entire search/replace operation and show report, but don't save
149157 * changes to the database.
@@ -183,8 +191,10 @@ class Search_Replace_Command extends WP_CLI_Command {
183191 * specify multiple columns.
184192 *
185193 * [--precise]
186- * : Force the use of PHP (instead of SQL) which is more thorough,
187- * but slower.
194+ * : Force the use of PHP (instead of SQL) for all columns. By default, the command
195+ * uses fast SQL queries, but automatically switches to PHP for columns containing
196+ * serialized data. Use this flag to ensure PHP processes all columns, which is
197+ * slower but handles complex serialized data structures more reliably.
188198 *
189199 * [--recurse-objects]
190200 * : Enable recursing into objects to replace strings. Defaults to true;
@@ -248,6 +258,12 @@ class Search_Replace_Command extends WP_CLI_Command {
248258 * # Search/replace to a SQL file without transforming the database
249259 * $ wp search-replace foo bar --export=database.sql
250260 *
261+ * # Search/replace string containing hyphens
262+ * $ wp search-replace --old='--old-string' --new='new-string'
263+ *
264+ * # Use precise mode for complex serialized data
265+ * $ wp search-replace 'oldurl.com' 'newurl.com' --precise
266+ *
251267 * # Bash script: Search/replace production to development url (multisite compatible)
252268 * #!/bin/bash
253269 * if $(wp --url=http://example.com core is-installed --network); then
@@ -257,12 +273,47 @@ class Search_Replace_Command extends WP_CLI_Command {
257273 * fi
258274 *
259275 * @param array<string> $args Positional arguments.
260- * @param array{'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.
276+ * @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.
261277 */
262278 public function __invoke ( $ args , $ assoc_args ) {
263279 global $ wpdb ;
264- $ old = array_shift ( $ args );
265- $ new = array_shift ( $ args );
280+
281+ // Support --old and --new flags as an alternative to positional arguments.
282+ // This allows users to search/replace strings that start with '--'.
283+ $ old_flag = Utils \get_flag_value ( $ assoc_args , 'old ' );
284+ $ new_flag = Utils \get_flag_value ( $ assoc_args , 'new ' );
285+
286+ // Check if both flags and positional arguments are provided.
287+ $ both_flags_provided = null !== $ old_flag && null !== $ new_flag ;
288+ $ has_positional_args = ! empty ( $ args );
289+ if ( $ both_flags_provided && $ has_positional_args ) {
290+ WP_CLI ::error ( 'Cannot use both positional arguments and --old/--new flags. Please use one method or the other. ' );
291+ }
292+
293+ // Determine old and new values.
294+ $ old = null !== $ old_flag ? $ old_flag : array_shift ( $ args );
295+ $ new = null !== $ new_flag ? $ new_flag : array_shift ( $ args );
296+
297+ // Validate that both old and new values are provided and not empty.
298+ if ( null === $ old || null === $ new || '' === $ old ) {
299+ $ missing = array ();
300+ if ( null === $ old || '' === $ old ) {
301+ $ missing [] = '<old> ' ;
302+ }
303+ // new value is allowed to be empty.
304+ if ( null === $ new ) {
305+ $ missing [] = '<new> ' ;
306+ }
307+ $ error_msg = count ( $ missing ) === 2
308+ ? 'Please provide both <old> and <new> arguments. '
309+ : sprintf ( 'Please provide the %s argument. ' , $ missing [0 ] );
310+
311+ $ error_msg .= "\n\nNote: If your search or replacement string starts with '--', use the flag syntax instead: "
312+ . "\n wp search-replace --old='--text' --new='replacement' " ;
313+
314+ WP_CLI ::error ( $ error_msg );
315+ }
316+
266317 $ total = 0 ;
267318 $ report = array ();
268319 $ this ->dry_run = Utils \get_flag_value ( $ assoc_args , 'dry-run ' , false );
0 commit comments