diff --git a/features/search-replace.feature b/features/search-replace.feature index eef35187..53180378 100644 --- a/features/search-replace.feature +++ b/features/search-replace.feature @@ -1392,3 +1392,30 @@ Feature: Do global search/replace """ Success: Made 0 replacements. """ + + @require-mysql + Scenario: Progress bar shows when not in verbose mode + Given a WP install + And I run `wp post generate --count=100` + And I run `wp option set test_url 'Visit http://example.com for more'` + + When I run `wp search-replace http://example.com http://example.org --precise` + Then STDOUT should contain: + """ + Updating + """ + + @require-mysql + Scenario: Progress bar does not show in verbose mode + Given a WP install + And I run `wp post generate --count=10` + + When I run `wp search-replace http://example.com http://example.org --verbose` + Then STDOUT should contain: + """ + Checking: + """ + And STDOUT should not contain: + """ + Updating + """ diff --git a/src/Search_Replace_Command.php b/src/Search_Replace_Command.php index 6e3f85cf..a18b4b79 100644 --- a/src/Search_Replace_Command.php +++ b/src/Search_Replace_Command.php @@ -557,6 +557,19 @@ private function php_export_table( $table, $old, $new ) { WP_CLI::log( sprintf( 'Checking: %s', $table ) ); } + // Set up progress bar if appropriate + $progress = null; + if ( $this->should_show_progress_bar() ) { + global $wpdb; + $table_sql = self::esc_sql_ident( $table ); + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- escaped through self::esc_sql_ident + $total_rows = $wpdb->get_var( "SELECT COUNT(*) FROM {$table_sql}" ); + if ( $total_rows > 0 ) { + WP_CLI::log( sprintf( 'Exporting %s (%d rows)', $table, $total_rows ) ); + $progress = \WP_CLI\Utils\make_progress_bar( sprintf( 'Processing %s', $table ), $total_rows ); + } + } + $rows = array(); foreach ( new Iterators\Table( $args ) as $i => $row ) { $row_fields = array(); @@ -572,9 +585,17 @@ private function php_export_table( $table, $old, $new ) { $row_fields[ $col ] = $value; } $rows[] = $row_fields; + + if ( $progress ) { + $progress->tick(); + } } $this->write_sql_row_fields( $table, $rows ); + if ( $progress ) { + $progress->finish(); + } + $table_report = array(); $total_rows = 0; $total_cols = 0; @@ -649,12 +670,24 @@ static function ( $key ) { ); $order_by_sql = 'ORDER BY ' . implode( ',', $order_by_keys ); $limit = 1000; + $progress = null; // 2 errors: // - WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- escaped through self::esc_sql_ident // - WordPress.CodeAnalysis.AssignmentInCondition -- no reason to do copy-paste for a single valid assignment in while // phpcs:ignore while ( $rows = $wpdb->get_results( "SELECT {$primary_keys_sql} FROM {$table_sql} {$where_key} {$order_by_sql} LIMIT {$limit}" ) ) { + // Set up progress bar on first iteration if we have rows to process + if ( null === $progress && $this->should_show_progress_bar() ) { + // Count total rows to process + // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- escaped through self::esc_sql_ident + $total_rows = $wpdb->get_var( "SELECT COUNT(*) FROM {$table_sql} {$where_key}" ); + if ( $total_rows > 0 ) { + WP_CLI::log( sprintf( 'Updating %s.%s (%d rows)', $table, $col, $total_rows ) ); + $progress = \WP_CLI\Utils\make_progress_bar( sprintf( 'Processing %s.%s', $table, $col ), $total_rows ); + } + } + foreach ( $rows as $keys ) { $where_sql = ''; foreach ( (array) $keys as $k => $v ) { @@ -699,6 +732,10 @@ static function ( $key ) { } } + if ( $progress ) { + $progress->tick( count( $rows ) ); + } + // Because we are ordering by primary keys from least to greatest, // we can exclude previous chunks from consideration by adding greater-than conditions // to insist the next chunk's keys must be greater than the last of this chunk's keys. @@ -735,6 +772,10 @@ static function ( $key ) { $where_key = 'WHERE ' . implode( ' AND ', $where_key_conditions ); } + if ( $progress ) { + $progress->finish(); + } + if ( $this->verbose && 'table' === $this->format ) { $time = round( microtime( true ) - $this->start_time, 3 ); WP_CLI::log( sprintf( '%d rows affected using PHP (in %ss).', $count, $time ) ); @@ -850,6 +891,40 @@ private static function is_text_col( $type ) { return false; } + /** + * Determines whether a progress bar should be shown. + * + * @return bool True if progress bar should be shown. + */ + private function should_show_progress_bar() { + // Don't show progress bar if in quiet mode + if ( WP_CLI::get_config( 'quiet' ) ) { + return false; + } + + // Don't show progress bar if exporting to STDOUT + if ( STDOUT === $this->export_handle ) { + return false; + } + + // Don't show progress bar if logging is enabled (would interfere with log output) + if ( null !== $this->log_handle ) { + return false; + } + + // Don't show progress bar if verbose mode is enabled (it shows row-by-row updates) + if ( $this->verbose ) { + return false; + } + + // Don't show progress bar if format is 'count' + if ( 'count' === $this->format ) { + return false; + } + + return true; + } + private static function esc_like( $old ) { global $wpdb;