@@ -849,52 +849,89 @@ function _get_wptexturize_shortcode_regex( $tagnames ) {
849849/**
850850 * Replaces characters or phrases within HTML elements only.
851851 *
852+ * This is a dangerous function which can break HTML syntax,
853+ * consider using methods from the HTML API instead.
854+ *
855+ * Example:
856+ *
857+ * '<p class="test">data-class</p>' === wp_replace_in_html_tags(
858+ * '<p data-class="test">data-class</p>',
859+ * array( 'data-class' => 'class' )
860+ * );
861+ *
852862 * @since 4.2.3
863+ * @since {WP_VERSION} Reliably parses HTML via the HTML API.
853864 *
854- * @param string $haystack The text which has to be formatted .
865+ * @param string $html Replace matches inside the tags of this HTML .
855866 * @param array $replace_pairs In the form array('from' => 'to', ...).
856- * @return string The formatted text.
867+ * @return string HTML after replacing the `$replace_pairs` matches, but only those
868+ * matches which appear inside HTML opening and closing tags.
857869 */
858- function wp_replace_in_html_tags ( $ haystack , $ replace_pairs ) {
859- // Find all elements.
860- $ textarr = wp_html_split ( $ haystack );
861- $ changed = false ;
870+ function wp_replace_in_html_tags ( $ html , $ replace_pairs ) {
871+ $ token_updater = new class ( $ html ) extends WP_HTML_Tag_Processor {
872+ public function extract_raw_token () {
873+ $ this ->set_bookmark ( 'here ' );
874+ $ here = $ this ->bookmarks ['here ' ];
875+
876+ return substr ( $ this ->html , $ here ->start , $ here ->length );
877+ }
878+
879+ public function replace_raw_token ( $ new_raw_html ) {
880+ $ this ->set_bookmark ( 'here ' );
881+ $ here = $ this ->bookmarks ['here ' ];
882+
883+ $ this ->lexical_updates [] = new WP_HTML_Text_Replacement (
884+ $ here ->start ,
885+ $ here ->length ,
886+ $ new_raw_html
887+ );
888+ }
889+ };
862890
863891 // Optimize when searching for one item.
864892 if ( 1 === count ( $ replace_pairs ) ) {
865893 // Extract $needle and $replace.
866894 $ needle = array_key_first ( $ replace_pairs );
867895 $ replace = $ replace_pairs [ $ needle ];
868896
869- // Loop through delimiters (elements) only.
870- for ( $ i = 1 , $ c = count ( $ textarr ); $ i < $ c ; $ i += 2 ) {
871- if ( str_contains ( $ textarr [ $ i ], $ needle ) ) {
872- $ textarr [ $ i ] = str_replace ( $ needle , $ replace , $ textarr [ $ i ] );
873- $ changed = true ;
897+ while ( $ token_updater ->next_token () ) {
898+ if ( '#text ' === $ token_updater ->get_token_name () ) {
899+ continue ;
900+ }
901+
902+ $ token = $ token_updater ->extract_raw_token ();
903+ $ updated = str_replace ( $ needle , $ replace , $ token );
904+
905+ if ( $ token !== $ updated ) {
906+ $ token_updater ->replace_raw_token ( $ updated );
874907 }
875908 }
876909 } else {
877910 // Extract all $needles.
878911 $ needles = array_keys ( $ replace_pairs );
879912
880- // Loop through delimiters (elements) only.
881- for ( $ i = 1 , $ c = count ( $ textarr ); $ i < $ c ; $ i += 2 ) {
913+ while ( $ token_updater ->next_token () ) {
914+ if ( '#text ' === $ token_updater ->get_token_name () ) {
915+ continue ;
916+ }
917+
918+ $ token = $ token_updater ->extract_raw_token ();
919+ $ updated = $ token ;
920+
882921 foreach ( $ needles as $ needle ) {
883- if ( str_contains ( $ textarr [ $ i ], $ needle ) ) {
884- $ textarr [ $ i ] = strtr ( $ textarr [ $ i ], $ replace_pairs );
885- $ changed = true ;
886- // After one strtr() break out of the foreach loop and look at next element.
922+ if ( str_contains ( $ token , $ needle ) ) {
923+ $ updated = strtr ( $ updated , $ replace_pairs );
887924 break ;
888925 }
889926 }
890- }
891- }
892927
893- if ( $ changed ) {
894- $ haystack = implode ( $ textarr );
928+ if ( $ token !== $ updated ) {
929+ $ token_updater ->replace_raw_token ( $ updated );
930+ }
931+ }
895932 }
896933
897- return $ haystack ;
934+ return $ token_updater -> get_updated_html () ;
898935}
899936
900937/**
0 commit comments