@@ -872,52 +872,89 @@ function _get_wptexturize_shortcode_regex( $tagnames ) {
872872/**
873873 * Replaces characters or phrases within HTML elements only.
874874 *
875+ * This is a dangerous function which can break HTML syntax,
876+ * consider using methods from the HTML API instead.
877+ *
878+ * Example:
879+ *
880+ * '<p class="test">data-class</p>' === wp_replace_in_html_tags(
881+ * '<p data-class="test">data-class</p>',
882+ * array( 'data-class' => 'class' )
883+ * );
884+ *
875885 * @since 4.2.3
886+ * @since {WP_VERSION} Reliably parses HTML via the HTML API.
876887 *
877- * @param string $haystack The text which has to be formatted .
888+ * @param string $html Replace matches inside the tags of this HTML .
878889 * @param array $replace_pairs In the form array('from' => 'to', ...).
879- * @return string The formatted text.
890+ * @return string HTML after replacing the `$replace_pairs` matches, but only those
891+ * matches which appear inside HTML opening and closing tags.
880892 */
881- function wp_replace_in_html_tags ( $ haystack , $ replace_pairs ) {
882- // Find all elements.
883- $ textarr = wp_html_split ( $ haystack );
884- $ changed = false ;
893+ function wp_replace_in_html_tags ( $ html , $ replace_pairs ) {
894+ $ token_updater = new class ( $ html ) extends WP_HTML_Tag_Processor {
895+ public function extract_raw_token () {
896+ $ this ->set_bookmark ( 'here ' );
897+ $ here = $ this ->bookmarks ['here ' ];
898+
899+ return substr ( $ this ->html , $ here ->start , $ here ->length );
900+ }
901+
902+ public function replace_raw_token ( $ new_raw_html ) {
903+ $ this ->set_bookmark ( 'here ' );
904+ $ here = $ this ->bookmarks ['here ' ];
905+
906+ $ this ->lexical_updates [] = new WP_HTML_Text_Replacement (
907+ $ here ->start ,
908+ $ here ->length ,
909+ $ new_raw_html
910+ );
911+ }
912+ };
885913
886914 // Optimize when searching for one item.
887915 if ( 1 === count ( $ replace_pairs ) ) {
888916 // Extract $needle and $replace.
889917 $ needle = array_key_first ( $ replace_pairs );
890918 $ replace = $ replace_pairs [ $ needle ];
891919
892- // Loop through delimiters (elements) only.
893- for ( $ i = 1 , $ c = count ( $ textarr ); $ i < $ c ; $ i += 2 ) {
894- if ( str_contains ( $ textarr [ $ i ], $ needle ) ) {
895- $ textarr [ $ i ] = str_replace ( $ needle , $ replace , $ textarr [ $ i ] );
896- $ changed = true ;
920+ while ( $ token_updater ->next_token () ) {
921+ if ( '#text ' === $ token_updater ->get_token_name () ) {
922+ continue ;
923+ }
924+
925+ $ token = $ token_updater ->extract_raw_token ();
926+ $ updated = str_replace ( $ needle , $ replace , $ token );
927+
928+ if ( $ token !== $ updated ) {
929+ $ token_updater ->replace_raw_token ( $ updated );
897930 }
898931 }
899932 } else {
900933 // Extract all $needles.
901934 $ needles = array_keys ( $ replace_pairs );
902935
903- // Loop through delimiters (elements) only.
904- for ( $ i = 1 , $ c = count ( $ textarr ); $ i < $ c ; $ i += 2 ) {
936+ while ( $ token_updater ->next_token () ) {
937+ if ( '#text ' === $ token_updater ->get_token_name () ) {
938+ continue ;
939+ }
940+
941+ $ token = $ token_updater ->extract_raw_token ();
942+ $ updated = $ token ;
943+
905944 foreach ( $ needles as $ needle ) {
906- if ( str_contains ( $ textarr [ $ i ], $ needle ) ) {
907- $ textarr [ $ i ] = strtr ( $ textarr [ $ i ], $ replace_pairs );
908- $ changed = true ;
909- // After one strtr() break out of the foreach loop and look at next element.
945+ if ( str_contains ( $ token , $ needle ) ) {
946+ $ updated = strtr ( $ updated , $ replace_pairs );
910947 break ;
911948 }
912949 }
913- }
914- }
915950
916- if ( $ changed ) {
917- $ haystack = implode ( $ textarr );
951+ if ( $ token !== $ updated ) {
952+ $ token_updater ->replace_raw_token ( $ updated );
953+ }
954+ }
918955 }
919956
920- return $ haystack ;
957+ return $ token_updater -> get_updated_html () ;
921958}
922959
923960/**
0 commit comments