File "class-optimize-fonts.php"
Full Path: /home/theinspectionboy/public_html/suffolk/includes-20250622113618/class-optimize-fonts.php
File size: 7.54 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* Class and methods to optimize third-party fonts.
*
* @link https://ewww.io/swis/
* @package SWIS_Performance
*/
namespace SWIS;
use MatthiasMullie\Minify;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Enables plugin to inline and optimize fonts.
*/
final class Optimize_Fonts extends Base {
/**
* The list of CSS (font) assets and associated information.
*
* @var array
*/
private $assets = array();
/**
* Register actions and filters for font optimization.
*/
function __construct() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
if ( ! $this->get_option( 'optimize_fonts' ) ) {
return;
}
parent::__construct();
if ( ! is_admin() ) {
$permissions = apply_filters( 'swis_performance_admin_permissions', 'manage_options' );
if ( current_user_can( $permissions ) && ! $this->get_option( 'optimize_fonts_css' ) ) {
// Auto-detect fonts and save the CSS code/handles to the db.
add_action( 'wp_head', array( $this, 'find_assets' ), 9999 );
add_action( 'wp_footer', array( $this, 'find_assets' ), 9999 );
add_action( 'wp_footer', array( $this, 'stash_css' ), 10000 );
}
if ( $this->get_option( 'optimize_fonts_css' ) ) {
add_action( 'wp', array( $this, 'disable_assets' ) );
add_action( 'wp_head', array( $this, 'inline_font_css' ) );
}
}
}
/**
* Inlines the font CSS in the <head>.
*/
function inline_font_css() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$font_css = $this->get_option( 'optimize_fonts_css' );
if ( empty( $font_css ) || ! is_string( $font_css ) ) {
return;
}
$minifier = new Minify\CSS( $font_css );
echo "<style id='swis-font-css'>\n" . wp_kses( $minifier->minify(), 'strip' ) . "\n</style>\n";
}
/**
* Check if an asset is from an external site.
*
* @param string $url The asset URL.
* @return bool True for external asset, false for local asset.
*/
function is_external( $url ) {
if ( 0 === strpos( $url, '/' ) && 0 !== strpos( $url, '//' ) ) {
return false;
}
$asset_url_parts = $this->parse_url( $url );
$local_url_parts = $this->parse_url( get_site_url() );
if ( ! empty( $asset_url_parts['host'] ) && ! empty( $local_url_parts['host'] ) && 0 === strcasecmp( $asset_url_parts['host'], $local_url_parts['host'] ) ) {
return false;
}
return true;
}
/**
* Get the contents of a CSS file.
*
* @param string $url The asset URL.
* @return string The CSS contents, but only if it consists solely of Google Font data.
*/
function get_font_css( $url ) {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$css = '';
$url_bits = explode( '?', $url );
$site_url = get_site_url();
$site_domain = $this->parse_url( $site_url, PHP_URL_HOST );
if ( false !== strpos( $url, $site_domain ) ) {
$this->debug_message( "found $site_domain, replacing in $url" );
$asset_path = trailingslashit( ABSPATH ) . str_replace( trailingslashit( $site_url ), '', $this->prepend_url_scheme( $url_bits[0] ) );
} elseif ( '/' === substr( $url, 0, 1 ) && '/' !== substr( $url, 1, 1 ) ) {
// Handle relative URLs like /wp-includes/css/something.css.
$asset_path = ABSPATH . ltrim( $url, '/' );
} else {
// Check for CDN URLs by swapping domains.
$asset_domain = $this->parse_url( $url, PHP_URL_HOST );
// Swapping $asset_domain with $site_domain to get a local URL (possibly).
$possible_url = str_replace( $asset_domain, $site_domain, $url );
$this->debug_message( "swapped $asset_domain for $site_domain to find a file via $possible_url" );
$url_bits = explode( '?', $possible_url );
$asset_path = trailingslashit( ABSPATH ) . str_replace( trailingslashit( $site_url ), '', $this->prepend_url_scheme( $url_bits[0] ) );
$this->debug_message( "now we have $asset_path, we'll see if it is local" );
}
if ( $url !== $asset_path && is_file( $asset_path ) ) {
$this->debug_message( "checking CSS from $asset_path" );
$css = file_get_contents( $asset_path );
} elseif ( strpos( $url, 'fonts.googleapis.com' ) ) {
$url = add_query_arg( 'display', 'swap', $url );
$this->debug_message( "getting CSS from $url" );
$response = wp_remote_get( $url );
if ( is_wp_error( $response ) ) {
$error_message = $response->get_error_message();
$this->debug_message( "request for $url failed: $error_message (" . wp_remote_retrieve_response_code( $response ) . ')' );
return $css;
} elseif ( ! empty( $response['body'] ) ) {
return $response['body'];
}
return $css;
}
// If there are no Google font URLs in the CSS, bail.
if ( false === strpos( $css, 'fonts.gstatic.com' ) ) {
$this->debug_message( "no Google Fonts in $url" );
return '';
}
// Grok through the CSS for @font-face rules, and if there is anything extra, bail.
$remaining_css = preg_replace( '/@font-face\s*?{[^}{]+?}/', '', $css );
$minifier = new Minify\CSS( $remaining_css );
$remaining_css = $minifier->minify();
if ( $remaining_css ) {
$this->debug_message( "extra CSS found in $url" );
return '';
}
$css = preg_replace( '/\s*?font-style:/', "\nfont-display: swap;\n font-style:", $css );
// At this point, we have a bit of CSS with nothing but @font-face rules, and confirmed Google font URLs.
// Even if there are some local URLs, let's just inline them anyway.
return $css;
}
/**
* Check to see which JS/CSS files have been registered for the current page.
*/
function find_assets() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$assets = wp_styles();
foreach ( $assets->done as $handle ) {
$url = $this->prepend_url_scheme( $assets->registered[ $handle ]->src );
$asset = array(
'url' => $url,
'external' => (int) $this->is_external( $url ),
);
$this->assets[ $handle ] = $asset;
}
}
/**
* Make sure protocol-relative URLs like //www.example.com/wp-includes/script.js get a scheme added.
*
* @param string $url The URL to potentially fix.
* @return string The properly-schemed URL.
*/
function prepend_url_scheme( $url ) {
if ( 0 === strpos( $url, '//' ) ) {
return ( is_ssl() ? 'https:' : 'http' ) . $url;
}
return $url;
}
/**
* Remove Google Font CSS files.
*/
function disable_assets() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$fonts_list = $this->get_option( 'optimize_fonts_list' );
if ( $this->is_iterable( $fonts_list ) ) {
foreach ( $fonts_list as $font_handle ) {
swis()->slim->add_exclusion( $font_handle );
}
}
}
/**
* Go through the list of discovered CSS files for the current page, retrieve the font CSS, and record the CSS handles.
*/
function stash_css() {
$this->debug_message( '<b>' . __METHOD__ . '()</b>' );
$font_css = '';
$font_list = array();
if ( ! empty( $this->assets ) ) {
foreach ( $this->assets as $handle => $asset ) {
$css = '';
if ( ! $asset['external'] ) {
$css = $this->get_font_css( $asset['url'] );
$this->debug_message( "retrieved CSS code for $handle with length: " . strlen( $css ) );
} elseif ( strpos( $asset['url'], 'fonts.googleapis.com' ) ) {
$css = $this->get_font_css( $asset['url'] );
$this->debug_message( "retrieved CSS code for $handle with length: " . strlen( $css ) );
} else {
$css = $this->get_font_css( $asset['url'] );
$this->debug_message( "retrieved CSS code for $handle with length: " . strlen( $css ) );
}
if ( ! empty( $css ) ) {
$font_css .= rtrim( $css ) . "\n";
$font_list[] = $handle;
}
}
}
if ( $font_css && $font_list ) {
$this->set_option( 'optimize_fonts_css', $font_css );
$this->set_option( 'optimize_fonts_list', $font_list );
}
}
}