text, deleting nodes) use tightly-scoped helpers. * * @package EasyFonts */ namespace EasyFonts\Frontend; use WP_HTML_Tag_Processor; defined( 'ABSPATH' ) || exit; /** * Reliable HTML read/rewrite helpers. */ class HtmlProcessor { /** * Iterate every , letting a callback rewrite the href. * * @param string $html HTML. * @param callable $callback fn(string $href, array $attrs): ?string — return new href or null to skip. * @return array{html:string,changed:bool} */ public function rewrite_stylesheet_links( string $html, callable $callback ): array { $processor = new WP_HTML_Tag_Processor( $html ); $changed = false; while ( $processor->next_tag( array( 'tag_name' => 'LINK' ) ) ) { $rel = (string) $processor->get_attribute( 'rel' ); if ( false === stripos( $rel, 'stylesheet' ) ) { continue; } $href = $processor->get_attribute( 'href' ); if ( ! is_string( $href ) || '' === $href ) { continue; } $attrs = array( 'id' => $processor->get_attribute( 'id' ), 'media' => $processor->get_attribute( 'media' ), ); $new = $callback( $href, $attrs ); if ( is_string( $new ) && $new !== $href ) { $processor->set_attribute( 'href', $new ); $changed = true; } } return array( 'html' => $processor->get_updated_html(), 'changed' => $changed, ); } /** * Collect all hrefs (read-only). * * @param string $html HTML. * @return string[] */ public function stylesheet_hrefs( string $html ): array { $processor = new WP_HTML_Tag_Processor( $html ); $hrefs = array(); while ( $processor->next_tag( array( 'tag_name' => 'LINK' ) ) ) { $rel = (string) $processor->get_attribute( 'rel' ); if ( false === stripos( $rel, 'stylesheet' ) ) { continue; } $href = $processor->get_attribute( 'href' ); if ( is_string( $href ) && '' !== $href ) { $hrefs[] = $href; } } return $hrefs; } /** * Physically remove specific stylesheet tags by exact href. * * The tokenizer can't delete nodes, so we mark them inert with the * tokenizer (reliable matching) and then strip the marked tags by regex. * * @param string $html HTML. * @param string[] $hrefs Exact href values to remove. * @return string */ public function remove_stylesheet_links( string $html, array $hrefs ): string { if ( empty( $hrefs ) ) { return $html; } $targets = array_fill_keys( $hrefs, true ); $processor = new WP_HTML_Tag_Processor( $html ); $hit = false; while ( $processor->next_tag( array( 'tag_name' => 'LINK' ) ) ) { $rel = (string) $processor->get_attribute( 'rel' ); if ( false === stripos( $rel, 'stylesheet' ) ) { continue; } $href = $processor->get_attribute( 'href' ); if ( is_string( $href ) && isset( $targets[ $href ] ) ) { $processor->set_attribute( 'data-easyfonts-remove', '1' ); $hit = true; } } if ( ! $hit ) { return $html; } $html = $processor->get_updated_html(); // Drop any carrying our marker. $html = preg_replace( '#]*\bdata-easyfonts-remove=([\'"])1\1[^>]*/?>\s*#i', '', $html ); return null === $html ? '' : $html; } /** * Process the CSS contents of each