@@ -652,19 +652,29 @@ private function sql_handle_col( $col, $primary_keys, $table, $old, $new ) {
652652
653653 $ table_sql = self ::esc_sql_ident ( $ table );
654654 $ col_sql = self ::esc_sql_ident ( $ col );
655+ $ old_json = self ::json_encode_strip_quotes ( $ old );
656+ $ new_json = self ::json_encode_strip_quotes ( $ new );
655657 if ( $ this ->dry_run ) {
656658 if ( $ this ->log_handle ) {
657659 $ count = $ this ->log_sql_diff ( $ col , $ primary_keys , $ table , $ old , $ new );
658660 } else {
659661 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- escaped through self::esc_sql_ident
660662 $ count = $ wpdb ->get_var ( $ wpdb ->prepare ( "SELECT COUNT( $ col_sql) FROM $ table_sql WHERE $ col_sql LIKE BINARY %s; " , '% ' . self ::esc_like ( $ old ) . '% ' ) );
663+ if ( $ old_json !== $ old ) {
664+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- escaped through self::esc_sql_ident
665+ $ count += (int ) $ wpdb ->get_var ( $ wpdb ->prepare ( "SELECT COUNT( $ col_sql) FROM $ table_sql WHERE $ col_sql LIKE BINARY %s; " , '% ' . self ::esc_like ( $ old_json ) . '% ' ) );
666+ }
661667 }
662668 } else {
663669 if ( $ this ->log_handle ) {
664670 $ this ->log_sql_diff ( $ col , $ primary_keys , $ table , $ old , $ new );
665671 }
666672 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- escaped through self::esc_sql_ident
667673 $ count = $ wpdb ->query ( $ wpdb ->prepare ( "UPDATE $ table_sql SET $ col_sql = REPLACE( $ col_sql, %s, %s); " , $ old , $ new ) );
674+ if ( $ old_json !== $ old ) {
675+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- escaped through self::esc_sql_ident
676+ $ count += (int ) $ wpdb ->query ( $ wpdb ->prepare ( "UPDATE $ table_sql SET $ col_sql = REPLACE( $ col_sql, %s, %s); " , $ old_json , $ new_json ) );
677+ }
668678 }
669679
670680 if ( $ this ->verbose && 'table ' === $ this ->format ) {
@@ -686,8 +696,12 @@ private function php_handle_col( $col, $primary_keys, $table, $old, $new ) {
686696 $ base_key_condition = '' ;
687697 $ where_key = '' ;
688698 if ( ! $ this ->regex ) {
699+ $ old_json = self ::json_encode_strip_quotes ( $ old );
689700 $ base_key_condition = "$ col_sql " . $ wpdb ->prepare ( ' LIKE BINARY %s ' , '% ' . self ::esc_like ( $ old ) . '% ' );
690- $ where_key = "WHERE $ base_key_condition " ;
701+ if ( $ old_json !== $ old ) {
702+ $ base_key_condition = "( $ base_key_condition OR $ col_sql " . $ wpdb ->prepare ( ' LIKE BINARY %s ' , '% ' . self ::esc_like ( $ old_json ) . '% ' ) . ' ) ' ;
703+ }
704+ $ where_key = "WHERE $ base_key_condition " ;
691705 }
692706
693707 $ escaped_primary_keys = self ::esc_sql_ident ( $ primary_keys );
@@ -917,6 +931,19 @@ private static function esc_like( $old ) {
917931 return $ old ;
918932 }
919933
934+ /**
935+ * Returns the JSON-encoded representation of a string with the surrounding quotes stripped.
936+ * This is used to also handle values stored as raw JSON in the database (e.g. WordPress font data).
937+ * Returns the original string unchanged if JSON encoding fails (e.g. invalid UTF-8).
938+ *
939+ * @param string $str The string to encode.
940+ * @return string The JSON-encoded string without surrounding quotes, or the original string on failure.
941+ */
942+ private static function json_encode_strip_quotes ( $ str ) {
943+ $ encoded = json_encode ( $ str ); // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode
944+ return false !== $ encoded ? substr ( $ encoded , 1 , -1 ) : $ str ;
945+ }
946+
920947 /**
921948 * Escapes (backticks) MySQL identifiers (aka schema object names) - i.e. column names, table names, and database/index/alias/view etc names.
922949 * See https://dev.mysql.com/doc/refman/5.5/en/identifiers.html
0 commit comments