@@ -774,52 +774,89 @@ function _get_wptexturize_shortcode_regex( $tagnames ) {
774774/**
775775 * Replaces characters or phrases within HTML elements only.
776776 *
777+ * This is a dangerous function which can break HTML syntax,
778+ * consider using methods from the HTML API instead.
779+ *
780+ * Example:
781+ *
782+ * '<p class="test">data-class</p>' === wp_replace_in_html_tags(
783+ * '<p data-class="test">data-class</p>',
784+ * array( 'data-class' => 'class' )
785+ * );
786+ *
777787 * @since 4.2.3
788+ * @since {WP_VERSION} Reliably parses HTML via the HTML API.
778789 *
779- * @param string $haystack The text which has to be formatted .
790+ * @param string $html Replace matches inside the tags of this HTML .
780791 * @param array $replace_pairs In the form array('from' => 'to', ...).
781- * @return string The formatted text.
792+ * @return string HTML after replacing the `$replace_pairs` matches, but only those
793+ * matches which appear inside HTML opening and closing tags.
782794 */
783- function wp_replace_in_html_tags ( $ haystack , $ replace_pairs ) {
784- // Find all elements.
785- $ textarr = wp_html_split ( $ haystack );
786- $ changed = false ;
795+ function wp_replace_in_html_tags ( $ html , $ replace_pairs ) {
796+ $ token_updater = new class ( $ html ) extends WP_HTML_Tag_Processor {
797+ public function extract_raw_token () {
798+ $ this ->set_bookmark ( 'here ' );
799+ $ here = $ this ->bookmarks ['here ' ];
800+
801+ return substr ( $ this ->html , $ here ->start , $ here ->length );
802+ }
803+
804+ public function replace_raw_token ( $ new_raw_html ) {
805+ $ this ->set_bookmark ( 'here ' );
806+ $ here = $ this ->bookmarks ['here ' ];
807+
808+ $ this ->lexical_updates [] = new WP_HTML_Text_Replacement (
809+ $ here ->start ,
810+ $ here ->length ,
811+ $ new_raw_html
812+ );
813+ }
814+ };
787815
788816 // Optimize when searching for one item.
789817 if ( 1 === count ( $ replace_pairs ) ) {
790818 // Extract $needle and $replace.
791819 $ needle = array_key_first ( $ replace_pairs );
792820 $ replace = $ replace_pairs [ $ needle ];
793821
794- // Loop through delimiters (elements) only.
795- for ( $ i = 1 , $ c = count ( $ textarr ); $ i < $ c ; $ i += 2 ) {
796- if ( str_contains ( $ textarr [ $ i ], $ needle ) ) {
797- $ textarr [ $ i ] = str_replace ( $ needle , $ replace , $ textarr [ $ i ] );
798- $ changed = true ;
822+ while ( $ token_updater ->next_token () ) {
823+ if ( '#text ' === $ token_updater ->get_token_name () ) {
824+ continue ;
825+ }
826+
827+ $ token = $ token_updater ->extract_raw_token ();
828+ $ updated = str_replace ( $ needle , $ replace , $ token );
829+
830+ if ( $ token !== $ updated ) {
831+ $ token_updater ->replace_raw_token ( $ updated );
799832 }
800833 }
801834 } else {
802835 // Extract all $needles.
803836 $ needles = array_keys ( $ replace_pairs );
804837
805- // Loop through delimiters (elements) only.
806- for ( $ i = 1 , $ c = count ( $ textarr ); $ i < $ c ; $ i += 2 ) {
838+ while ( $ token_updater ->next_token () ) {
839+ if ( '#text ' === $ token_updater ->get_token_name () ) {
840+ continue ;
841+ }
842+
843+ $ token = $ token_updater ->extract_raw_token ();
844+ $ updated = $ token ;
845+
807846 foreach ( $ needles as $ needle ) {
808- if ( str_contains ( $ textarr [ $ i ], $ needle ) ) {
809- $ textarr [ $ i ] = strtr ( $ textarr [ $ i ], $ replace_pairs );
810- $ changed = true ;
811- // After one strtr() break out of the foreach loop and look at next element.
847+ if ( str_contains ( $ token , $ needle ) ) {
848+ $ updated = strtr ( $ updated , $ replace_pairs );
812849 break ;
813850 }
814851 }
815- }
816- }
817852
818- if ( $ changed ) {
819- $ haystack = implode ( $ textarr );
853+ if ( $ token !== $ updated ) {
854+ $ token_updater ->replace_raw_token ( $ updated );
855+ }
856+ }
820857 }
821858
822- return $ haystack ;
859+ return $ token_updater -> get_updated_html () ;
823860}
824861
825862/**
0 commit comments