File: /home/pclast/www/wp-content/plugins/wp-rocket/inc/Engine/Media/PreloadFonts/Frontend/Controller.php
<?php
declare(strict_types=1);
namespace WP_Rocket\Engine\Media\PreloadFonts\Frontend;
use WP_Rocket\Admin\Options_Data;
use WP_Rocket\Engine\Common\PerformanceHints\Frontend\ControllerInterface;
use WP_Rocket\Engine\Media\PreloadFonts\Database\Queries\PreloadFonts as PFQuery;
use WP_Rocket\Engine\Media\PreloadFonts\Context\Context;
use WP_Rocket\Engine\Media\PreloadFonts\Database\Rows\PreloadFonts;
use WP_Rocket\Engine\Optimization\UrlTrait;
use WP_Rocket\Engine\Support\CommentTrait;
use WP_Rocket\Engine\Common\Head\ElementTrait;
class Controller implements ControllerInterface {
use UrlTrait;
use CommentTrait;
use ElementTrait;
/**
* Used for debugging head elements.
*
* @var string
*/
private $feature = 'preload_fonts';
/**
* Options instance
*
* @var Options_Data
*/
private $options; // @phpstan-ignore-line Use of this will come later.
/**
* Queries instance
*
* @var PFQuery
*/
private $query; // @phpstan-ignore-line Use of this will come later.
/**
* Context instance.
*
* @var Context
*/
private $context; // @phpstan-ignore-line Use of this will come later.
/**
* Constructor
*
* @param Options_Data $options Options instance.
* @param PFQuery $query Queries instance.
* @param Context $context Context instance.
*/
public function __construct( Options_Data $options, PFQuery $query, Context $context ) {
$this->options = $options;
$this->query = $query;
$this->context = $context;
}
/**
* Applies optimization.
*
* @param string $html HTML content.
* @param object $row Database Row.
*
* @return string
*/
public function optimize( string $html, $row ): string {
if ( ! $this->context->is_allowed() || ! $row->has_preload_fonts() ) {
return $html;
}
return $this->add_meta_comment( 'auto_preload_fonts', $html );
}
/**
* Add custom data like the List of elements to be considered for optimization.
*
* @param array $data Array of data passed in beacon.
*
* @return array
*/
public function add_custom_data( array $data ): array {
if ( ! $this->context->is_allowed() ) {
return $data;
}
$data['preload_fonts_exclusions'] = $this->context->get_exclusions();
$data['status']['preload_fonts'] = $this->context->is_allowed();
$data['processed_extensions'] = $this->context->get_extensions();
/**
* Filters the external font domains to exclude from processing
*
* @since 3.19.1
*
* @param array $exclusions Array of domains to exclude from external font processing
*/
$data['external_font_exclusions'] = wpm_apply_filters_typed( 'string[]', 'rocket_external_font_exclusions', [] );
return $data;
}
/**
* Adds the preload fonts to the head tag.
*
* @param array $items added to the head.
* @return array Items to be added to the head.
*/
public function add_preload_fonts_in_head( $items ) {
if ( ! $this->context->is_allowed() ) {
return $items;
}
$row = $this->get_current_url_row();
if ( empty( $row ) ) {
return $items;
}
$fonts = json_decode( $row->fonts, true );
if ( empty( $fonts ) ) {
return $items;
}
foreach ( $fonts as $font ) {
$item_args = [
'href' => esc_url( $font ),
'as' => 'font',
2 => 'crossorigin',
];
$items[] = $this->preload_link( $item_args );
}
return $items;
}
/**
* Get current visited page row in DB.
*
* @return false|PreloadFonts
*/
private function get_current_url_row() {
global $wp;
$url = untrailingslashit( home_url( add_query_arg( [], $wp->request ) ) );
$is_mobile = $this->context->is_mobile_allowed();
$row = $this->query->get_row( $url, $is_mobile );
if ( empty( $row ) || 'completed' !== $row->status || empty( $row->fonts ) || '[]' === $row->fonts ) {
return false;
}
return $row;
}
/**
* Disables the Remove Unused CSS (RUCSS) feature for preloading fonts.
*
* This method can be used as a filter callback to control whether the RUCSS feature
* should be applied when preloading fonts.
*
* @param bool $status Current status of the RUCSS preload fonts feature.
* @return bool Modified status indicating whether RUCSS should be disabled for preloading fonts.
*/
public function disable_rucss_preload_fonts( $status ) {
if ( ! $this->context->is_allowed() ) {
return $status;
}
$row = $this->get_current_url_row();
if ( empty( $row ) ) {
return $status;
}
return false;
}
/**
* Removes existing preloaded font links from the provided HTML content if necessary.
*
* This method scans the given HTML string and removes any <link rel="preload" as="font"> tags
* that match certain criteria, to prevent duplicate or unnecessary font preloads.
* Only removes preloaded fonts when WP Rocket's font optimization is actually applied.
*
* @param string $html The HTML content to process.
* @return string The modified HTML content with preloaded font links removed if applicable.
*/
public function maybe_remove_existing_preloaded_fonts( string $html ): string {
if ( ! $this->context->is_allowed() ) {
return $html;
}
$row = $this->get_current_url_row();
if ( ! $row ) {
return $html;
}
/**
* Filter to enable or disable deleting existing preloaded tags.
*
* @param bool $should_remove
*/
$should_remove = wpm_apply_filters_typed( 'boolean', 'rocket_remove_existing_preloaded_fonts', true );
if ( ! $should_remove ) {
return $html;
}
// One regex to skip scripts and remove any <link rel=preload as=font…> tag (entire line, including indentation and newline).
$result = preg_replace(
'#<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>(*SKIP)(*FAIL)' // skip <script> blocks.
. '|^[ \t]*<link\b' // OR match a <link at line start (with optional indent).
. '(?=[^>]*\brel\s*=\s*(["\']?)preload\1)' // lookahead rel=preload.
. '(?=[^>]*\bas\s*=\s*(["\']?)font\2)' // lookahead as=font.
. '[^>]*?\/?>[ \t]*(?:\r?\n|$)#im', // up to /> or >, then trim whitespace and newline.
'',
$html
);
if ( null === $result ) {
return $html;
}
return $result;
}
}