@@ -3242,89 +3242,46 @@ static function( $matches ) {
32423242 *
32433243 * @since 5.1.0
32443244 * @since 5.6.0 Removed 'noreferrer' relationship.
3245+ * @since 6.3.0 Rely on the Tag Processor for HTML searching and modification.
32453246 *
32463247 * @param string $text Content that may contain HTML A elements.
32473248 * @return string Converted content.
32483249 */
32493250function wp_targeted_link_rel ( $ text ) {
3250- // Don't run (more expensive) regex if no links with targets.
3251+ // Don't run (more expensive) code if no links with targets are possible .
32513252 if ( stripos ( $ text , 'target ' ) === false || stripos ( $ text , '<a ' ) === false || is_serialized ( $ text ) ) {
32523253 return $ text ;
32533254 }
32543255
3255- $ script_and_style_regex = '/<(script|style).*?<\/ \\1>/si ' ;
3256-
3257- preg_match_all ( $ script_and_style_regex , $ text , $ matches );
3258- $ extra_parts = $ matches [0 ];
3259- $ html_parts = preg_split ( $ script_and_style_regex , $ text );
3260-
3261- foreach ( $ html_parts as &$ part ) {
3262- $ part = preg_replace_callback ( '|<a\s([^>]*target\s*=[^>]*)>|i ' , 'wp_targeted_link_rel_callback ' , $ part );
3263- }
3264-
3265- $ text = '' ;
3266- for ( $ i = 0 ; $ i < count ( $ html_parts ); $ i ++ ) {
3267- $ text .= $ html_parts [ $ i ];
3268- if ( isset ( $ extra_parts [ $ i ] ) ) {
3269- $ text .= $ extra_parts [ $ i ];
3256+ $ p = new WP_HTML_Tag_Processor ( $ text );
3257+ while ( $ p ->next_tag ( 'a ' ) ) {
3258+ $ target = $ p ->get_attribute ( 'target ' );
3259+ if ( null === $ target ) {
3260+ continue ;
32703261 }
3271- }
3272-
3273- return $ text ;
3274- }
3275-
3276- /**
3277- * Callback to add `rel="noopener"` string to HTML A element.
3278- *
3279- * Will not duplicate an existing 'noopener' value to avoid invalidating the HTML.
3280- *
3281- * @since 5.1.0
3282- * @since 5.6.0 Removed 'noreferrer' relationship.
3283- *
3284- * @param array $matches Single match.
3285- * @return string HTML A Element with `rel="noopener"` in addition to any existing values.
3286- */
3287- function wp_targeted_link_rel_callback ( $ matches ) {
3288- $ link_html = $ matches [1 ];
3289- $ original_link_html = $ link_html ;
3290-
3291- // Consider the HTML escaped if there are no unescaped quotes.
3292- $ is_escaped = ! preg_match ( '/(^|[^ \\\\])[ \'"]/ ' , $ link_html );
3293- if ( $ is_escaped ) {
3294- // Replace only the quotes so that they are parsable by wp_kses_hair(), leave the rest as is.
3295- $ link_html = preg_replace ( '/ \\\\([ \'"])/ ' , '$1 ' , $ link_html );
3296- }
3297-
3298- $ atts = wp_kses_hair ( $ link_html , wp_allowed_protocols () );
3299-
3300- /**
3301- * Filters the rel values that are added to links with `target` attribute.
3302- *
3303- * @since 5.1.0
3304- *
3305- * @param string $rel The rel values.
3306- * @param string $link_html The matched content of the link tag including all HTML attributes.
3307- */
3308- $ rel = apply_filters ( 'wp_targeted_link_rel ' , 'noopener ' , $ link_html );
33093262
3310- // Return early if no rel values to be added or if no actual target attribute.
3311- if ( ! $ rel || ! isset ( $ atts ['target ' ] ) ) {
3312- return "<a $ original_link_html> " ;
3313- }
3314-
3315- if ( isset ( $ atts ['rel ' ] ) ) {
3316- $ all_parts = preg_split ( '/\s/ ' , "{$ atts ['rel ' ]['value ' ]} $ rel " , -1 , PREG_SPLIT_NO_EMPTY );
3317- $ rel = implode ( ' ' , array_unique ( $ all_parts ) );
3318- }
3263+ $ rel = $ p ->get_attribute ( 'rel ' ) || '' ;
3264+ $ link_text = "rel= \"{$ rel }\"" ;
33193265
3320- $ atts ['rel ' ]['whole ' ] = 'rel=" ' . esc_attr ( $ rel ) . '" ' ;
3321- $ link_html = implode ( ' ' , array_column ( $ atts , 'whole ' ) );
3266+ /**
3267+ * Filters the rel values that are added to links with `target` attribute.
3268+ *
3269+ * @since 5.1.0
3270+ *
3271+ * @param string $rel The rel values.
3272+ * @param string $link_html The matched content of the link tag including all HTML attributes.
3273+ */
3274+ $ updated_rel = apply_filters ( 'wp_targeted_link_rel ' , 'noopener ' , $ link_text );
3275+ if ( ! $ updated_rel ) {
3276+ continue ;
3277+ }
33223278
3323- if ( $ is_escaped ) {
3324- $ link_html = preg_replace ( '/[ \'"]/ ' , '\\\\$0 ' , $ link_html );
3279+ $ all_parts = preg_split ( '/\s/ ' , "$ rel $ updated_rel " , -1 , PREG_SPLIT_NO_EMPTY );
3280+ $ new_rel = implode ( ' ' , array_unique ( $ all_parts ) );
3281+ $ p ->set_attribute ( 'rel ' , $ new_rel );
33253282 }
33263283
3327- return " <a $ link_html > " ;
3284+ return $ p -> get_updated_html () ;
33283285}
33293286
33303287/**
0 commit comments