765 lines
25 KiB
PHP
765 lines
25 KiB
PHP
|
<?php
|
||
|
namespace WpAssetCleanUp\OptimiseAssets;
|
||
|
|
||
|
use WpAssetCleanUp\Main;
|
||
|
use WpAssetCleanUp\Misc;
|
||
|
use WpAssetCleanUp\Plugin;
|
||
|
|
||
|
/**
|
||
|
* Class FontsGoogle
|
||
|
* @package WpAssetCleanUp\OptimiseAssets
|
||
|
*/
|
||
|
class FontsGoogle
|
||
|
{
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
public static $containsStr = '//fonts.googleapis.com/';
|
||
|
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
public static $matchesStr = '//fonts.googleapis.com/(css|icon)\?';
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
const NOSCRIPT_WEB_FONT_LOADER = '<span style="display: none;" data-name=wpacu-delimiter content="ASSET CLEANUP NOSCRIPT WEB FONT LOADER"></span>';
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
const COMBINED_LINK_DEL = '<span style="display: none;" data-name=wpacu-delimiter content="ASSET CLEANUP COMBINED LINK LOCATION"></span>';
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
public function init()
|
||
|
{
|
||
|
if (self::preventAnyChange()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
add_filter('wp_resource_hints', array($this, 'resourceHints'), PHP_INT_MAX, 2);
|
||
|
|
||
|
add_action('wp_head', array($this, 'preloadFontFiles'), 1);
|
||
|
add_action('wp_footer', static function() {
|
||
|
if ( Plugin::preventAnyFrontendOptimization() || Main::isTestModeActive() || Main::instance()->settings['google_fonts_remove'] ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
echo self::NOSCRIPT_WEB_FONT_LOADER;
|
||
|
}, PHP_INT_MAX);
|
||
|
|
||
|
add_filter('wpacu_html_source_after_optimization', static function($htmlSource) {
|
||
|
// Is the mark still there and wasn't replaced? Strip it
|
||
|
return str_replace(FontsGoogle::NOSCRIPT_WEB_FONT_LOADER, '', $htmlSource);
|
||
|
});
|
||
|
|
||
|
add_action('init', function() {
|
||
|
// don't apply any changes if not in the front-end view (e.g. Dashboard view)
|
||
|
// or test mode is enabled and a guest user is accessing the page
|
||
|
if ( Plugin::preventAnyFrontendOptimization() || Main::isTestModeActive() || Main::instance()->settings['google_fonts_remove'] ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
add_filter('style_loader_src', array($this, 'alterGoogleFontLink'));
|
||
|
}, 20);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $urls
|
||
|
* @param $relationType
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function resourceHints($urls, $relationType)
|
||
|
{
|
||
|
// don't apply any changes if not in the front-end view (e.g. Dashboard view)
|
||
|
// or test mode is enabled and a guest user is accessing the page
|
||
|
if (is_admin() || Main::isTestModeActive() || Plugin::preventAnyFrontendOptimization()) {
|
||
|
return $urls;
|
||
|
}
|
||
|
|
||
|
// Are the Google Fonts removed? Do not add it and strip any existing ones
|
||
|
if (! empty($urls) && Main::instance()->settings['google_fonts_remove']) {
|
||
|
foreach ($urls as $urlKey => $urlValue) {
|
||
|
if (is_string($urlValue) && ((stripos($urlValue, 'fonts.googleapis.com') !== false) || (stripos($urlValue, 'fonts.gstatic.com') !== false))) {
|
||
|
unset($urls[$urlKey]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $urls; // Finally, return the list after any removals
|
||
|
}
|
||
|
|
||
|
// Google Fonts "preconnect"
|
||
|
if ('preconnect' === $relationType
|
||
|
&& ! Main::instance()->settings['google_fonts_remove'] // "Remove Google Fonts" has to be turned off
|
||
|
&& Main::instance()->settings['google_fonts_preconnect']) { // Needs to be enabled within "Plugin Usage Preferences" in "Settings"
|
||
|
$urls[] = array(
|
||
|
'href' => 'https://fonts.gstatic.com/',
|
||
|
'crossorigin'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return $urls;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
public function preloadFontFiles()
|
||
|
{
|
||
|
// don't apply any changes if not in the front-end view (e.g. Dashboard view)
|
||
|
// or test mode is enabled and a guest user is accessing the page
|
||
|
if ( Plugin::preventAnyFrontendOptimization() || Main::isTestModeActive() ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (! $preloadFontFiles = trim(Main::instance()->settings['google_fonts_preload_files'])) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$preloadFontFilesArray = array();
|
||
|
|
||
|
if (strpos($preloadFontFiles, "\n") !== false) {
|
||
|
foreach (explode("\n", $preloadFontFiles) as $preloadFontFile) {
|
||
|
$preloadFontFile = trim($preloadFontFile);
|
||
|
|
||
|
if (! $preloadFontFile) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$preloadFontFilesArray[] = $preloadFontFile;
|
||
|
}
|
||
|
} else {
|
||
|
$preloadFontFilesArray[] = $preloadFontFiles;
|
||
|
}
|
||
|
|
||
|
$preloadFontFilesArray = array_unique($preloadFontFilesArray);
|
||
|
|
||
|
$preloadFontFilesOutput = '';
|
||
|
|
||
|
// Finally, go through the list
|
||
|
foreach ($preloadFontFilesArray as $preloadFontFile) {
|
||
|
$preloadFontFilesOutput .= '<link rel="preload" as="font" href="'.esc_attr($preloadFontFile).'" data-wpacu-preload-font="1" crossorigin>'."\n";
|
||
|
}
|
||
|
|
||
|
echo apply_filters('wpacu_preload_google_font_files_output', $preloadFontFilesOutput);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $htmlSource
|
||
|
*
|
||
|
* @return false|mixed|string|void
|
||
|
*/
|
||
|
public static function alterHtmlSource($htmlSource)
|
||
|
{
|
||
|
// don't apply any changes if not in the front-end view (e.g. Dashboard view)
|
||
|
// or test mode is enabled and a guest user is accessing the page
|
||
|
// or an AMP page is accessed
|
||
|
if ( Plugin::preventAnyFrontendOptimization() || Main::isTestModeActive()) {
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Remove Google Fonts? Stop here as optimization is no longer relevant
|
||
|
*/
|
||
|
if (Main::instance()->settings['google_fonts_remove']) {
|
||
|
return FontsGoogleRemove::cleanHtmlSource($htmlSource);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Optimize Google Fonts
|
||
|
*/
|
||
|
if (stripos($htmlSource, self::$containsStr) !== false) {
|
||
|
// Cleaner HTML Source
|
||
|
$altHtmlSource = preg_replace( '@<(script|style|noscript)[^>]*?>.*?</\\1>@si', '', $htmlSource ); // strip irrelevant tags for the collection
|
||
|
$altHtmlSource = preg_replace( '/<!--[^>]*' . preg_quote( self::$containsStr, '/' ) . '.*?-->/', '', $altHtmlSource ); // strip any comments containing the string
|
||
|
|
||
|
// Get all valid LINKs that have the $string within them
|
||
|
preg_match_all( '#<link[^>]*' . self::$matchesStr . '.*(>)#Usmi', $altHtmlSource, $matchesFromLinkTags, PREG_SET_ORDER );
|
||
|
|
||
|
// Needs to match at least one to carry on with the replacements
|
||
|
if ( isset( $matchesFromLinkTags[0] ) && ! empty( $matchesFromLinkTags[0] ) ) {
|
||
|
if ( Main::instance()->settings['google_fonts_combine'] ) {
|
||
|
/*
|
||
|
* "Combine Google Fonts" IS enabled
|
||
|
*/
|
||
|
$finalCombinableLinks = $preloadedLinks = array();
|
||
|
|
||
|
foreach ( $matchesFromLinkTags as $linkTagArray ) {
|
||
|
$linkTag = $finalLinkTag = trim( trim( $linkTagArray[0], '"\'' ) );
|
||
|
|
||
|
// Extra checks to make sure it's a valid LINK tag
|
||
|
if ( ( strpos( $linkTag, "'" ) !== false && ( substr_count( $linkTag, "'" ) % 2 ) )
|
||
|
|| ( strpos( $linkTag, '"' ) !== false && ( substr_count( $linkTag, '"' ) % 2 ) )
|
||
|
|| ( trim( strip_tags( $linkTag ) ) !== '' ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Check if the CSS has any 'data-wpacu-skip' attribute; if it does, do not continue and leave it as it is (non-combined)
|
||
|
if ( preg_match( '#data-wpacu-skip([=>/ ])#i', $linkTag ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$finalLinkHref = $linkHrefOriginal = Misc::getValueFromTag($linkTag);
|
||
|
|
||
|
// [START] Remove invalid requests with no font family
|
||
|
$urlParse = parse_url( str_replace( '&', '&', $linkHrefOriginal ), PHP_URL_QUERY );
|
||
|
parse_str( $urlParse, $qStr );
|
||
|
|
||
|
if ( isset( $qStr['family'] ) && ! $qStr['family'] ) {
|
||
|
$htmlSource = str_replace( $linkTag, '', $htmlSource );
|
||
|
continue;
|
||
|
}
|
||
|
// [END] Remove invalid requests with no font family
|
||
|
|
||
|
// If anything is set apart from '[none set]', proceed
|
||
|
if ( Main::instance()->settings['google_fonts_display'] ) {
|
||
|
$finalLinkHref = self::alterGoogleFontLink( $linkHrefOriginal );
|
||
|
|
||
|
if ( $finalLinkHref !== $linkHrefOriginal ) {
|
||
|
$finalLinkTag = str_replace( $linkHrefOriginal, $finalLinkHref, $linkTag );
|
||
|
|
||
|
// Finally, alter the HTML source
|
||
|
$htmlSource = str_replace( $linkTag, $finalLinkTag, $htmlSource );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( preg_match( '/rel=(["\'])preload(["\'])/i', $finalLinkTag )
|
||
|
|| strpos( $finalLinkTag, 'data-wpacu-to-be-preloaded-basic' ) ) {
|
||
|
$preloadedLinks[] = $finalLinkHref;
|
||
|
}
|
||
|
|
||
|
$finalCombinableLinks[] = array( 'href' => $finalLinkHref, 'tag' => $finalLinkTag );
|
||
|
}
|
||
|
|
||
|
$preloadedLinks = array_unique( $preloadedLinks );
|
||
|
|
||
|
// Remove data for preloaded LINKs
|
||
|
if ( ! empty( $preloadedLinks ) ) {
|
||
|
foreach ( $finalCombinableLinks as $fclIndex => $combinableLinkData ) {
|
||
|
if ( in_array( $combinableLinkData['href'], $preloadedLinks ) ) {
|
||
|
unset( $finalCombinableLinks[ $fclIndex ] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$finalCombinableLinks = array_values( $finalCombinableLinks );
|
||
|
|
||
|
// Only proceed with the optimization/combine if there's obviously at least 2 combinable URL requests to Google Fonts
|
||
|
// OR the loading type is different from render-blocking
|
||
|
if ( Main::instance()->settings['google_fonts_combine_type'] || count( $finalCombinableLinks ) > 1 ) {
|
||
|
$htmlSource = self::combineGoogleFontLinks( $finalCombinableLinks, $htmlSource );
|
||
|
}
|
||
|
} elseif (Main::instance()->settings['google_fonts_display']) {
|
||
|
/*
|
||
|
* "Combine Google Fonts" IS NOT enabled
|
||
|
* Go through the links and apply any "font-display"
|
||
|
*/
|
||
|
foreach ( $matchesFromLinkTags as $linkTagArray ) {
|
||
|
$linkTag = trim( trim( $linkTagArray[0], '"\'' ) );
|
||
|
|
||
|
// Extra checks to make sure it's a valid LINK tag
|
||
|
if ( ( strpos( $linkTag, "'" ) !== false && ( substr_count( $linkTag, "'" ) % 2 ) )
|
||
|
|| ( strpos( $linkTag, '"' ) !== false && ( substr_count( $linkTag, '"' ) % 2 ) )
|
||
|
|| ( trim( strip_tags( $linkTag ) ) !== '' ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Check if the CSS has any 'data-wpacu-skip' attribute; if it does, do not continue and leave it as it is (non-altered)
|
||
|
if ( preg_match( '#data-wpacu-skip([=>/ ])#i', $linkTag ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$linkHrefOriginal = Misc::getValueFromTag($linkTag);
|
||
|
|
||
|
// [START] Remove invalid requests with no font family
|
||
|
$urlParse = parse_url( str_replace( '&', '&', $linkHrefOriginal ), PHP_URL_QUERY );
|
||
|
parse_str( $urlParse, $qStr );
|
||
|
|
||
|
if ( isset( $qStr['family'] ) && ! $qStr['family'] ) {
|
||
|
$htmlSource = str_replace( $linkTag, '', $htmlSource );
|
||
|
continue;
|
||
|
}
|
||
|
// [END] Remove invalid requests with no font family
|
||
|
|
||
|
// If anything is set apart from '[none set]', proceed
|
||
|
$newLinkHref = self::alterGoogleFontLink( $linkHrefOriginal );
|
||
|
|
||
|
if ( $newLinkHref !== $linkHrefOriginal ) {
|
||
|
$finalLinkTag = str_replace( $linkHrefOriginal, $newLinkHref, $linkTag );
|
||
|
|
||
|
// Finally, alter the HTML source
|
||
|
$htmlSource = str_replace( $linkTag, $finalLinkTag, $htmlSource );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// "font-display: swap;" if enabled
|
||
|
$htmlSource = self::alterGoogleFontUrlFromInlineStyleTags($htmlSource);
|
||
|
|
||
|
// Clear any traces
|
||
|
return str_replace(self::NOSCRIPT_WEB_FONT_LOADER, '', $htmlSource);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $linkHrefOriginal
|
||
|
* @param bool $escHtml
|
||
|
* @param $alterFor
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function alterGoogleFontLink($linkHrefOriginal, $escHtml = true, $alterFor = 'css')
|
||
|
{
|
||
|
$isInVar = false; // The link is inside a variable with a JSON format
|
||
|
|
||
|
// Some special filtering here as some hosting environments (at least staging) behave funny with // inside SCRIPT tags
|
||
|
if ($alterFor === 'js') {
|
||
|
$containsStrNoSlashes = str_replace('/', '', self::$containsStr);
|
||
|
$conditionOne = stripos($linkHrefOriginal, $containsStrNoSlashes) === false;
|
||
|
|
||
|
if (strpos($linkHrefOriginal, '\/') !== false) {
|
||
|
$isInVar = true;
|
||
|
}
|
||
|
} else { // css (default)
|
||
|
$conditionOne = stripos($linkHrefOriginal, self::$containsStr) === false;
|
||
|
}
|
||
|
|
||
|
// Do not continue if it doesn't contain the right string, or it contains 'display=' or it does not contain 'family=' or there is no value set for "font-display"
|
||
|
if ($conditionOne ||
|
||
|
stripos($linkHrefOriginal, 'display=') !== false ||
|
||
|
stripos($linkHrefOriginal, 'family=') === false ||
|
||
|
! Main::instance()->settings['google_fonts_display']) {
|
||
|
// Return original source
|
||
|
return $linkHrefOriginal;
|
||
|
}
|
||
|
|
||
|
$altLinkHref = str_replace('&', '&', $linkHrefOriginal);
|
||
|
|
||
|
if ($isInVar) {
|
||
|
$altLinkHref = str_replace('\/', '/', $altLinkHref);
|
||
|
}
|
||
|
|
||
|
$urlQuery = parse_url($altLinkHref, PHP_URL_QUERY);
|
||
|
parse_str($urlQuery, $outputStr);
|
||
|
|
||
|
// Is there no "display" or there is, but it has an empty value? Append the one we have in the "Settings" - "Google Fonts"
|
||
|
if ( ! isset($outputStr['display']) || (isset($outputStr['display']) && $outputStr['display'] === '') ) {
|
||
|
$outputStr['display'] = Main::instance()->settings['google_fonts_display'];
|
||
|
|
||
|
list($linkHrefFirstPart) = explode('?', $linkHrefOriginal);
|
||
|
|
||
|
// Returned the updated source with the 'display' parameter appended to it
|
||
|
$afterQuestionMark = http_build_query($outputStr);
|
||
|
|
||
|
if ($escHtml) {
|
||
|
$afterQuestionMark = esc_attr($afterQuestionMark);
|
||
|
}
|
||
|
|
||
|
return $linkHrefFirstPart . '?' . $afterQuestionMark;
|
||
|
}
|
||
|
|
||
|
// Return original source
|
||
|
return $linkHrefOriginal;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $htmlSource
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public static function alterGoogleFontUrlFromInlineStyleTags($htmlSource)
|
||
|
{
|
||
|
if (! preg_match('/@import(\s+)url\(/i', $htmlSource)) {
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
preg_match_all('#<\s*?style\b[^>]*>(.*?)</style\b[^>]*>#s', $htmlSource, $styleMatches, PREG_SET_ORDER);
|
||
|
|
||
|
if (empty($styleMatches)) {
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
// Go through each STYLE tag
|
||
|
foreach ($styleMatches as $styleInlineArray) {
|
||
|
list($styleInlineTag, $styleInlineContent) = $styleInlineArray;
|
||
|
|
||
|
// Check if the STYLE tag has any 'data-wpacu-skip' attribute; if it does, do not continue
|
||
|
if (preg_match('#data-wpacu-skip([=>/ ])#i', $styleInlineTag)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Is the content relevant?
|
||
|
if (! preg_match('/@import(\s+|)(url|\(|\'|")/i', $styleInlineContent)
|
||
|
|| stripos($styleInlineContent, 'fonts.googleapis.com') === false) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Do any alteration to the URL of the Google Font
|
||
|
$newCssOutput = self::alterGoogleFontUrlFromCssContent($styleInlineTag);
|
||
|
|
||
|
$htmlSource = str_replace($styleInlineTag, $newCssOutput, $htmlSource);
|
||
|
}
|
||
|
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $cssContent
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public static function alterGoogleFontUrlFromCssContent($cssContent)
|
||
|
{
|
||
|
if (stripos($cssContent, 'fonts.googleapis.com') === false || ! Main::instance()->settings['google_fonts_display']) {
|
||
|
return $cssContent;
|
||
|
}
|
||
|
|
||
|
$regExps = array('/@import(\s+)url\((.*?)\)(|\s+)\;/i', '/@import(\s+|)(\(|\'|")(.*?)(\'|"|\))\;/i');
|
||
|
|
||
|
$newCssOutput = $cssContent;
|
||
|
|
||
|
foreach ($regExps as $regExpIndex => $regExpPattern) {
|
||
|
preg_match_all($regExpPattern, $cssContent, $matchesFromInlineCode, PREG_SET_ORDER);
|
||
|
|
||
|
if (! empty($matchesFromInlineCode)) {
|
||
|
foreach ($matchesFromInlineCode as $matchesFromInlineCodeArray) {
|
||
|
$cssImportRule = $matchesFromInlineCodeArray[0];
|
||
|
|
||
|
if ($regExpIndex === 0) {
|
||
|
$googleApisUrl = trim($matchesFromInlineCodeArray[2], '"\' ');
|
||
|
} else {
|
||
|
$googleApisUrl = trim($matchesFromInlineCodeArray[3], '"\' ');
|
||
|
}
|
||
|
|
||
|
// It has to be a Google Fonts API link
|
||
|
if (stripos($googleApisUrl, 'fonts.googleapis.com') === false) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$newGoogleApisUrl = self::alterGoogleFontLink($googleApisUrl, false);
|
||
|
|
||
|
if ($newGoogleApisUrl !== $googleApisUrl) {
|
||
|
$newCssImportRule = str_replace($googleApisUrl, $newGoogleApisUrl, $cssImportRule);
|
||
|
$newCssOutput = str_replace($cssImportRule, $newCssImportRule, $newCssOutput);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $newCssOutput;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $jsContent
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public static function alterGoogleFontUrlFromJsContent($jsContent)
|
||
|
{
|
||
|
if (stripos($jsContent, 'fonts.googleapis.com') === false) {
|
||
|
return $jsContent;
|
||
|
}
|
||
|
|
||
|
$newJsOutput = $jsContent;
|
||
|
|
||
|
preg_match_all('#fonts.googleapis.com(.*?)(["\'])#si', $jsContent, $matchesFromJsCode);
|
||
|
|
||
|
if (isset($matchesFromJsCode[0]) && ! empty($matchesFromJsCode)) {
|
||
|
foreach ($matchesFromJsCode[0] as $match) {
|
||
|
$matchRule = $match;
|
||
|
$googleApisUrl = trim($match, '"\' ');
|
||
|
|
||
|
$newGoogleApisUrl = self::alterGoogleFontLink($googleApisUrl, false, 'js');
|
||
|
if ($newGoogleApisUrl !== $googleApisUrl) {
|
||
|
$newJsMatchOutput = str_replace($googleApisUrl, $newGoogleApisUrl, $matchRule);
|
||
|
$newJsOutput = str_replace($matchRule, $newJsMatchOutput, $newJsOutput);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Look for any "WebFontConfig = { google: { families: ['font-one', 'font-two'] } }" patterns
|
||
|
if ( stripos( $jsContent, 'WebFontConfig' ) !== false
|
||
|
&& preg_match_all( '#WebFontConfig(.*?)google(\s+|):(\s+|){(\s+|)families(\s+|):(?<families>.*?)]#s', $jsContent, $webFontConfigMatches )
|
||
|
&& isset( $webFontConfigMatches['families'] ) && ! empty( $webFontConfigMatches['families'] )
|
||
|
) {
|
||
|
foreach ($webFontConfigMatches['families'] as $webFontConfigKey => $webFontConfigMatch) {
|
||
|
$originalWholeMatch = $webFontConfigMatches[0][$webFontConfigKey];
|
||
|
$familiesMatchOutput = trim($webFontConfigMatch);
|
||
|
|
||
|
// NO match or existing "display" parameter was found? Do not continue
|
||
|
if (! $familiesMatchOutput || strpos($familiesMatchOutput, 'display=')) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Alter the matched string
|
||
|
$familiesNewOutput = preg_replace('/([\'"])$/', '&display='.Main::instance()->settings['google_fonts_display'].'\\1', $familiesMatchOutput);
|
||
|
$newWebFontConfigOutput = str_replace($familiesMatchOutput, $familiesNewOutput, $originalWholeMatch);
|
||
|
|
||
|
// Finally, do the replacement
|
||
|
$newJsOutput = str_replace($originalWholeMatch, $newWebFontConfigOutput, $newJsOutput);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $newJsOutput;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $finalLinks
|
||
|
* @param $htmlSource
|
||
|
*
|
||
|
* @return false|mixed|string|void
|
||
|
*/
|
||
|
public static function combineGoogleFontLinks($finalLinks, $htmlSource)
|
||
|
{
|
||
|
$fontsArray = array();
|
||
|
|
||
|
foreach ($finalLinks as $finalLinkIndex => $finalLinkData) {
|
||
|
$finalLinkHref = $finalLinkData['href'];
|
||
|
$finalLinkHref = str_replace('&', '&', $finalLinkHref);
|
||
|
|
||
|
$queries = parse_url($finalLinkHref, PHP_URL_QUERY);
|
||
|
parse_str($queries, $fontQueries);
|
||
|
|
||
|
if (! array_key_exists('family', $fontQueries) || array_key_exists('text', $fontQueries)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Strip the existing tag, leave a mark where the final combined LINK will be placed
|
||
|
$stripTagWith = ($finalLinkIndex === 0) ? self::COMBINED_LINK_DEL : '';
|
||
|
$finalLinkTag = $finalLinkData['tag'];
|
||
|
|
||
|
$htmlSource = str_ireplace(array($finalLinkTag."\n", $finalLinkTag), $stripTagWith, $htmlSource);
|
||
|
|
||
|
$family = trim($fontQueries['family']);
|
||
|
$family = trim($family, '|');
|
||
|
|
||
|
if (! $family) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (strpos($family, '|') !== false) {
|
||
|
// More than one family per request?
|
||
|
foreach (explode('|', $family) as $familyOne) {
|
||
|
if (strpos($familyOne, ':') !== false) {
|
||
|
// They have types
|
||
|
list ($familyRaw, $familyTypes) = explode(':', $familyOne);
|
||
|
$fontsArray['families'][$familyRaw]['types'] = self::buildSortTypesList($familyTypes);
|
||
|
} else {
|
||
|
// They do not have types
|
||
|
$familyRaw = $familyOne;
|
||
|
$fontsArray['families'][$familyRaw]['types'] = false;
|
||
|
}
|
||
|
}
|
||
|
} elseif (strpos($family, ':') !== false) {
|
||
|
list ($familyRaw, $familyTypes) = explode(':', $family);
|
||
|
$fontsArray['families'][$familyRaw]['types'] = self::buildSortTypesList($familyTypes);
|
||
|
} else {
|
||
|
$familyRaw = $family;
|
||
|
$fontsArray['families'][$familyRaw]['types'] = false;
|
||
|
}
|
||
|
|
||
|
if (array_key_exists('subset', $fontQueries)) {
|
||
|
// More than one subset per request?
|
||
|
if (strpos($fontQueries['subset'], ',') !== false) {
|
||
|
$multipleSubsets = explode(',', trim($fontQueries['subset'], ','));
|
||
|
|
||
|
foreach ($multipleSubsets as $subset) {
|
||
|
$fontsArray['subsets'][] = trim($subset);
|
||
|
}
|
||
|
} else {
|
||
|
// Only one subset
|
||
|
$fontsArray['subsets'][] = $fontQueries['subset'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (array_key_exists('effect', $fontQueries)) {
|
||
|
// More than one subset per request?
|
||
|
if (strpos($fontQueries['effect'], '|') !== false) {
|
||
|
$multipleSubsets = explode('|', trim($fontQueries['effect'], '|'));
|
||
|
|
||
|
foreach ($multipleSubsets as $subset) {
|
||
|
$fontsArray['effects'][] = trim($subset);
|
||
|
}
|
||
|
} else {
|
||
|
// Only one subset
|
||
|
$fontsArray['effects'][] = $fontQueries['effect'];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (! empty($fontsArray)) {
|
||
|
$finalCombinedParameters = '';
|
||
|
ksort($fontsArray['families']);
|
||
|
|
||
|
// Families
|
||
|
foreach ($fontsArray['families'] as $familyRaw => $fontValues) {
|
||
|
$finalCombinedParameters .= str_replace(' ', '+', $familyRaw);
|
||
|
|
||
|
// Any types? e.g. 400, 400italic, bold, etc.
|
||
|
if (isset($fontValues['types']) && $fontValues['types'] !== false) {
|
||
|
$finalCombinedParameters .= ':' . $fontValues['types'];
|
||
|
}
|
||
|
|
||
|
$finalCombinedParameters .= '|';
|
||
|
}
|
||
|
|
||
|
$finalCombinedParameters = trim($finalCombinedParameters, '|');
|
||
|
|
||
|
// Subsets
|
||
|
if (isset($fontsArray['subsets']) && ! empty($fontsArray['subsets'])) {
|
||
|
sort($fontsArray['subsets']);
|
||
|
$finalCombinedParameters .= '&subset=' . implode(',', array_unique($fontsArray['subsets']));
|
||
|
}
|
||
|
|
||
|
// Effects
|
||
|
if (isset($fontsArray['effects']) && ! empty($fontsArray['effects'])) {
|
||
|
sort($fontsArray['effects']);
|
||
|
$finalCombinedParameters .= '&effect=' . implode('|', array_unique($fontsArray['effects']));
|
||
|
}
|
||
|
|
||
|
if ($fontDisplay = Main::instance()->settings['google_fonts_display']) {
|
||
|
$finalCombinedParameters .= '&display=' . $fontDisplay;
|
||
|
}
|
||
|
|
||
|
$finalCombinedParameters = esc_attr($finalCombinedParameters);
|
||
|
|
||
|
// This is needed for both render-blocking and async (within NOSCRIPT tag as a fallback)
|
||
|
$finalCombinedLink = <<<LINK
|
||
|
<link rel='stylesheet' id='wpacu-combined-google-fonts-css' href='https://fonts.googleapis.com/css?family={$finalCombinedParameters}' type='text/css' media='all' />
|
||
|
LINK;
|
||
|
/*
|
||
|
* Loading Type: Render-Blocking (Default)
|
||
|
*/
|
||
|
if (! Main::instance()->settings['google_fonts_combine_type']) {
|
||
|
$finalCombinedLink .= "\n";
|
||
|
$htmlSource = str_replace(self::COMBINED_LINK_DEL, apply_filters('wpacu_combined_google_fonts_link_tag', $finalCombinedLink), $htmlSource);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Loading Type: Asynchronous via LINK preload with fallback
|
||
|
*/
|
||
|
if (Main::instance()->settings['google_fonts_combine_type'] === 'async_preload') {
|
||
|
$finalPreloadCombinedLink = <<<LINK
|
||
|
<link rel='preload' as="style" onload="this.onload=null;this.rel='stylesheet'" data-wpacu-preload-it-async='1' id='wpacu-combined-google-fonts-css-async-preload' href='https://fonts.googleapis.com/css?family={$finalCombinedParameters}' type='text/css' media='all' />
|
||
|
LINK;
|
||
|
$finalPreloadCombinedLink .= "\n".Misc::preloadAsyncCssFallbackOutput();
|
||
|
|
||
|
$htmlSource = str_replace(self::COMBINED_LINK_DEL, apply_filters('wpacu_combined_google_fonts_async_preload_link_tag', $finalPreloadCombinedLink), $htmlSource);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Loading Type: Asynchronous via Web Font Loader (webfont.js) with fallback
|
||
|
*/
|
||
|
if (Main::instance()->settings['google_fonts_combine_type'] === 'async') { // Async via Web Font Loader
|
||
|
$subSetsStr = '';
|
||
|
|
||
|
if (isset($fontsArray['subsets']) && ! empty($fontsArray['subsets'])) {
|
||
|
sort($fontsArray['subsets']);
|
||
|
$subSetsStr = implode(',', array_unique($fontsArray['subsets']));
|
||
|
}
|
||
|
|
||
|
$wfConfigGoogleFamilies = array();
|
||
|
|
||
|
// Families
|
||
|
$iCount = 0;
|
||
|
|
||
|
foreach ($fontsArray['families'] as $familyRaw => $fontValues) {
|
||
|
$wfConfigGoogleFamily = str_replace(' ', '+', $familyRaw);
|
||
|
|
||
|
// Any types? e.g. 400, 400italic, bold, etc.
|
||
|
$hasTypes = false;
|
||
|
if (isset($fontValues['types']) && $fontValues['types']) {
|
||
|
$wfConfigGoogleFamily .= ':'.$fontValues['types'];
|
||
|
$hasTypes = true;
|
||
|
}
|
||
|
|
||
|
if ($subSetsStr) {
|
||
|
// If there are types, continue to use the comma delimiter
|
||
|
$wfConfigGoogleFamily .= ($hasTypes ? ',' : ':') . $subSetsStr;
|
||
|
}
|
||
|
|
||
|
// Append extra parameters to the last family from the list
|
||
|
if ($iCount === count($fontsArray['families']) - 1) {
|
||
|
// Effects
|
||
|
if (isset($fontsArray['effects']) && ! empty($fontsArray['effects'])) {
|
||
|
sort($fontsArray['effects']);
|
||
|
$wfConfigGoogleFamily .= '&effect=' . implode('|', array_unique($fontsArray['effects']));
|
||
|
}
|
||
|
|
||
|
if ($fontDisplay = Main::instance()->settings['google_fonts_display']) {
|
||
|
$wfConfigGoogleFamily .= '&display=' . $fontDisplay;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$wfConfigGoogleFamilies[] = "'".$wfConfigGoogleFamily."'";
|
||
|
|
||
|
$iCount++;
|
||
|
}
|
||
|
|
||
|
$wfConfigGoogleFamiliesStr = '['.implode(',', $wfConfigGoogleFamilies).']';
|
||
|
|
||
|
$finalInlineTagWebFontConfig = '<script id=\'wpacu-google-fonts-async-load\' type=\'text/javascript\'>'."\n".'WebFontConfig={google:{families:'.$wfConfigGoogleFamiliesStr.'}};(function(wpacuD){var wpacuWf=wpacuD.createElement(\'script\'),wpacuS=wpacuD.scripts[0];wpacuWf.src=(\'https:\'===document.location.protocol?\'https\':\'http\')+\'://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js\';wpacuWf.async=!0;wpacuS.parentNode.insertBefore(wpacuWf,wpacuS)})(document);'."\n".'</script>';
|
||
|
|
||
|
$htmlSource = str_replace(
|
||
|
array(
|
||
|
self::COMBINED_LINK_DEL,
|
||
|
self::NOSCRIPT_WEB_FONT_LOADER
|
||
|
),
|
||
|
array(
|
||
|
apply_filters( 'wpacu_combined_google_fonts_inline_script_tag', $finalInlineTagWebFontConfig ),
|
||
|
'<noscript>' . apply_filters( 'wpacu_combined_google_fonts_link_tag', $finalCombinedLink ) . '</noscript>' . "\n"
|
||
|
),
|
||
|
$htmlSource
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* e.g. 300, 400, 400italic, bold, etc.
|
||
|
*
|
||
|
* @param $types
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public static function buildSortTypesList($types)
|
||
|
{
|
||
|
$newTypes = array();
|
||
|
|
||
|
// More than one type per family?
|
||
|
if (strpos($types, ',') !== false) {
|
||
|
$multipleTypes = explode(',', trim($types, ','));
|
||
|
|
||
|
foreach ($multipleTypes as $type) {
|
||
|
if (trim($type)) {
|
||
|
$newTypes[] = trim($type);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// Only one type per family
|
||
|
$newTypes[] = $types;
|
||
|
}
|
||
|
|
||
|
$newTypes = array_unique($newTypes);
|
||
|
|
||
|
sort($newTypes);
|
||
|
|
||
|
return implode(',', $newTypes);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool
|
||
|
*/
|
||
|
public static function preventAnyChange()
|
||
|
{
|
||
|
return defined( 'WPACU_ALLOW_ONLY_UNLOAD_RULES' ) && WPACU_ALLOW_ONLY_UNLOAD_RULES;
|
||
|
}
|
||
|
}
|