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;
 | |
| 	}
 | |
| }
 |