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