@@ -119,6 +119,11 @@ class Search_Replace_Command extends WP_CLI_Command {
119119 */
120120 private $ start_time ;
121121
122+ /**
123+ * @var \cli\progress\Bar|null
124+ */
125+ private $ progress_bar ;
126+
122127 /**
123128 * Searches/replaces strings in the database.
124129 *
@@ -557,6 +562,18 @@ private function php_export_table( $table, $old, $new ) {
557562 WP_CLI ::log ( sprintf ( 'Checking: %s ' , $ table ) );
558563 }
559564
565+ // Set up progress bar if appropriate
566+ $ progress = null ;
567+ if ( $ this ->should_show_progress_bar () ) {
568+ global $ wpdb ;
569+ $ table_sql = self ::esc_sql_ident ( $ table );
570+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- escaped through self::esc_sql_ident
571+ $ total_rows = $ wpdb ->get_var ( "SELECT COUNT(*) FROM {$ table_sql }" );
572+ if ( $ total_rows > 0 ) {
573+ $ progress = \WP_CLI \Utils \make_progress_bar ( sprintf ( 'Exporting %s ' , $ table ), $ total_rows );
574+ }
575+ }
576+
560577 $ rows = array ();
561578 foreach ( new Iterators \Table ( $ args ) as $ i => $ row ) {
562579 $ row_fields = array ();
@@ -572,9 +589,17 @@ private function php_export_table( $table, $old, $new ) {
572589 $ row_fields [ $ col ] = $ value ;
573590 }
574591 $ rows [] = $ row_fields ;
592+
593+ if ( $ progress ) {
594+ $ progress ->tick ();
595+ }
575596 }
576597 $ this ->write_sql_row_fields ( $ table , $ rows );
577598
599+ if ( $ progress ) {
600+ $ progress ->finish ();
601+ }
602+
578603 $ table_report = array ();
579604 $ total_rows = 0 ;
580605 $ total_cols = 0 ;
@@ -650,6 +675,17 @@ static function ( $key ) {
650675 $ order_by_sql = 'ORDER BY ' . implode ( ', ' , $ order_by_keys );
651676 $ limit = 1000 ;
652677
678+ // Set up progress bar if appropriate
679+ $ progress = null ;
680+ if ( $ this ->should_show_progress_bar () ) {
681+ // Count total rows to process
682+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- escaped through self::esc_sql_ident
683+ $ total_rows = $ wpdb ->get_var ( "SELECT COUNT(*) FROM {$ table_sql } {$ where_key }" );
684+ if ( $ total_rows > 0 ) {
685+ $ progress = \WP_CLI \Utils \make_progress_bar ( sprintf ( 'Updating %s.%s ' , $ table , $ col ), $ total_rows );
686+ }
687+ }
688+
653689 // 2 errors:
654690 // - WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- escaped through self::esc_sql_ident
655691 // - WordPress.CodeAnalysis.AssignmentInCondition -- no reason to do copy-paste for a single valid assignment in while
@@ -668,18 +704,27 @@ static function ( $key ) {
668704 $ col_value = $ wpdb ->get_var ( "SELECT {$ col_sql } FROM {$ table_sql } WHERE {$ where_sql }" );
669705
670706 if ( '' === $ col_value ) {
707+ if ( $ progress ) {
708+ $ progress ->tick ();
709+ }
671710 continue ;
672711 }
673712
674713 $ value = $ replacer ->run ( $ col_value );
675714
676715 if ( $ value === $ col_value ) {
716+ if ( $ progress ) {
717+ $ progress ->tick ();
718+ }
677719 continue ;
678720 }
679721
680722 // In case a needed re-serialization was unsuccessful, we should not update the value,
681723 // as this implies we hit an exception while processing.
682724 if ( gettype ( $ value ) !== gettype ( $ col_value ) ) {
725+ if ( $ progress ) {
726+ $ progress ->tick ();
727+ }
683728 continue ;
684729 }
685730
@@ -697,6 +742,10 @@ static function ( $key ) {
697742
698743 $ wpdb ->update ( $ table , [ $ col => $ value ], $ update_where );
699744 }
745+
746+ if ( $ progress ) {
747+ $ progress ->tick ();
748+ }
700749 }
701750
702751 // Because we are ordering by primary keys from least to greatest,
@@ -735,6 +784,10 @@ static function ( $key ) {
735784 $ where_key = 'WHERE ' . implode ( ' AND ' , $ where_key_conditions );
736785 }
737786
787+ if ( $ progress ) {
788+ $ progress ->finish ();
789+ }
790+
738791 if ( $ this ->verbose && 'table ' === $ this ->format ) {
739792 $ time = round ( microtime ( true ) - $ this ->start_time , 3 );
740793 WP_CLI ::log ( sprintf ( '%d rows affected using PHP (in %ss). ' , $ count , $ time ) );
@@ -850,6 +903,40 @@ private static function is_text_col( $type ) {
850903 return false ;
851904 }
852905
906+ /**
907+ * Determines whether a progress bar should be shown.
908+ *
909+ * @return bool True if progress bar should be shown.
910+ */
911+ private function should_show_progress_bar () {
912+ // Don't show progress bar if in quiet mode
913+ if ( WP_CLI ::get_config ( 'quiet ' ) ) {
914+ return false ;
915+ }
916+
917+ // Don't show progress bar if exporting to STDOUT
918+ if ( STDOUT === $ this ->export_handle ) {
919+ return false ;
920+ }
921+
922+ // Don't show progress bar if logging to STDOUT
923+ if ( STDOUT === $ this ->log_handle ) {
924+ return false ;
925+ }
926+
927+ // Don't show progress bar if verbose mode is enabled (it shows row-by-row updates)
928+ if ( $ this ->verbose ) {
929+ return false ;
930+ }
931+
932+ // Don't show progress bar if format is 'count'
933+ if ( 'count ' === $ this ->format ) {
934+ return false ;
935+ }
936+
937+ return true ;
938+ }
939+
853940 private static function esc_like ( $ old ) {
854941 global $ wpdb ;
855942
0 commit comments