| 1 |
<?php |
| 2 |
|
| 3 |
/* |
| 4 |
* This file is part of the Jooosi Fon package. |
| 5 |
* |
| 6 |
* (c) Joshua Gugun Siagian <suabahasa@gmail.com> |
| 7 |
* |
| 8 |
* For the full copyright and license information, please view the LICENSE |
| 9 |
* file that was distributed with this source code. |
| 10 |
*/ |
| 11 |
declare (strict_types=1); |
| 12 |
namespace JooosiFon\Core; |
| 13 |
|
| 14 |
use JooosiFonDeps\JOOOSI_FON; |
| 15 |
use JooosiFon\Utils\Config; |
| 16 |
use JooosiFon\Utils\Debug; |
| 17 |
/** |
| 18 |
* Serve the font on the frontpage. |
| 19 |
* |
| 20 |
* @author Joshua Gugun Siagian <suabahasa@gmail.com> |
| 21 |
* @todo Remove the legacy Yabe Webfont hook shim completely in Jooosi Fon 3.0.0. |
| 22 |
*/ |
| 23 |
final class Frontpage |
| 24 |
{ |
| 25 |
/** |
| 26 |
* @var string[] |
| 27 |
*/ |
| 28 |
private array $preload_script_handles = []; |
| 29 |
/** |
| 30 |
* @var array<int, array<string, string>> |
| 31 |
*/ |
| 32 |
private array $preload_resources = []; |
| 33 |
private bool $print_preload_resources = \false; |
| 34 |
public function __construct() |
| 35 |
{ |
| 36 |
/** |
| 37 |
* @see wp-includes\default-filters.php for the priority. |
| 38 |
*/ |
| 39 |
add_action('wp_head', fn() => $this->append_header(), 1000001); |
| 40 |
add_filter('wp_preload_resources', fn($resources) => $this->preload($resources)); |
| 41 |
add_action('wp_enqueue_scripts', fn() => $this->disable_user_google_fonts(), 1000001); |
| 42 |
} |
| 43 |
public static function enqueue_css_cache() |
| 44 |
{ |
| 45 |
if (defined('JooosiFonDeps\JOOOSI_FON_CSS_CACHE_WAS_LOADED')) { |
| 46 |
return; |
| 47 |
} |
| 48 |
Debug::stopwatch()->start(sprintf('%s::%s', self::class, __FUNCTION__)); |
| 49 |
if (file_exists(\JooosiFon\Core\Cache::get_cache_path(\JooosiFon\Core\Cache::CSS_CACHE_FILE))) { |
| 50 |
$handle = JOOOSI_FON::WP_OPTION . '-cache'; |
| 51 |
$version = \JooosiFon\Core\Cache::get_cache_version(\JooosiFon\Core\Cache::CSS_CACHE_FILE) ?? JOOOSI_FON::VERSION; |
| 52 |
if (Config::get('cache.inline_print', \false)) { |
| 53 |
$css = file_get_contents(\JooosiFon\Core\Cache::get_cache_path(\JooosiFon\Core\Cache::CSS_CACHE_FILE)); |
| 54 |
if ($css !== \false) { |
| 55 |
wp_register_style($handle, \false, [], null); |
| 56 |
wp_add_inline_style($handle, $css); |
| 57 |
} |
| 58 |
} else { |
| 59 |
wp_register_style($handle, \JooosiFon\Core\Cache::get_cache_url(\JooosiFon\Core\Cache::CSS_CACHE_FILE), [], $version); |
| 60 |
} |
| 61 |
do_action('a!jooosi/fon/core/frontpage:before_print_style'); |
| 62 |
do_action_deprecated('a!yabe/webfont/core/frontpage:before_print_style', [], '2.1.0', 'a!jooosi/fon/core/frontpage:before_print_style'); |
| 63 |
wp_enqueue_style($handle); |
| 64 |
wp_print_styles($handle); |
| 65 |
} |
| 66 |
define('JooosiFonDeps\JOOOSI_FON_CSS_CACHE_WAS_LOADED', \true); |
| 67 |
Debug::stopwatch()->stop(sprintf('%s::%s', self::class, __FUNCTION__)); |
| 68 |
} |
| 69 |
/** |
| 70 |
* Append the header to the frontpage. |
| 71 |
*/ |
| 72 |
private function append_header() |
| 73 |
{ |
| 74 |
if (function_exists('wp_preload_resources')) { |
| 75 |
$this->print_preload_resources = \true; |
| 76 |
wp_preload_resources(); |
| 77 |
$this->print_preload_resources = \false; |
| 78 |
} else { |
| 79 |
$this->preload([]); |
| 80 |
} |
| 81 |
self::enqueue_css_cache(); |
| 82 |
if ($this->preload_script_handles !== []) { |
| 83 |
wp_print_scripts($this->preload_script_handles); |
| 84 |
} |
| 85 |
} |
| 86 |
/** |
| 87 |
* Preload the fonts file on the frontpage. |
| 88 |
*/ |
| 89 |
private function preload($resources) |
| 90 |
{ |
| 91 |
if (defined('JooosiFonDeps\JOOOSI_FON_PRELOAD_HTML_WAS_LOADED') && !$this->print_preload_resources) { |
| 92 |
return $resources; |
| 93 |
} |
| 94 |
if (!defined('JooosiFonDeps\JOOOSI_FON_PRELOAD_HTML_WAS_LOADED')) { |
| 95 |
Debug::stopwatch()->start(sprintf('%s::%s', self::class, __FUNCTION__)); |
| 96 |
if (file_exists(\JooosiFon\Core\Cache::get_cache_path(\JooosiFon\Core\Cache::PRELOAD_HTML_FILE))) { |
| 97 |
$preload_html = file_get_contents(\JooosiFon\Core\Cache::get_cache_path(\JooosiFon\Core\Cache::PRELOAD_HTML_FILE)); |
| 98 |
if ($preload_html !== \false) { |
| 99 |
preg_match_all('/<link\b[^>]*>/i', $preload_html, $link_tags); |
| 100 |
foreach ($link_tags[0] as $link_tag) { |
| 101 |
$rel = $this->get_attribute($link_tag, 'rel'); |
| 102 |
$href = $this->get_attribute($link_tag, 'href'); |
| 103 |
if (strtolower((string) $rel) !== 'preload' || $href === null) { |
| 104 |
continue; |
| 105 |
} |
| 106 |
$resource = ['href' => $href]; |
| 107 |
foreach (['as', 'type', 'media', 'fetchpriority'] as $attribute) { |
| 108 |
$value = $this->get_attribute($link_tag, $attribute); |
| 109 |
if ($value !== null) { |
| 110 |
$resource[$attribute] = $value; |
| 111 |
} |
| 112 |
} |
| 113 |
if (stripos($link_tag, ' crossorigin') !== \false) { |
| 114 |
$resource['crossorigin'] = ''; |
| 115 |
} |
| 116 |
$this->preload_resources[] = $resource; |
| 117 |
} |
| 118 |
preg_match_all('/<script\b[^>]*>(.*?)<\/script>/is', $preload_html, $script_tags); |
| 119 |
foreach ($script_tags[1] as $script) { |
| 120 |
$script = trim($script); |
| 121 |
if ($script === '') { |
| 122 |
continue; |
| 123 |
} |
| 124 |
$handle = sprintf('%s-preload-%s', JOOOSI_FON::WP_OPTION, md5($script)); |
| 125 |
wp_register_script($handle, \false, [], null, \false); |
| 126 |
wp_add_inline_script($handle, $script); |
| 127 |
$this->preload_script_handles[] = $handle; |
| 128 |
} |
| 129 |
} |
| 130 |
} |
| 131 |
define('JooosiFonDeps\JOOOSI_FON_PRELOAD_HTML_WAS_LOADED', \true); |
| 132 |
Debug::stopwatch()->stop(sprintf('%s::%s', self::class, __FUNCTION__)); |
| 133 |
} |
| 134 |
return $this->print_preload_resources ? $this->preload_resources : $resources; |
| 135 |
} |
| 136 |
private function get_attribute(string $tag, string $attribute): ?string |
| 137 |
{ |
| 138 |
$pattern = sprintf('/\b%s\s*=\s*([\'\"])(.*?)\1/is', preg_quote($attribute, '/')); |
| 139 |
if (preg_match($pattern, $tag, $matches)) { |
| 140 |
return html_entity_decode($matches[2], \ENT_QUOTES, 'UTF-8'); |
| 141 |
} |
| 142 |
return null; |
| 143 |
} |
| 144 |
/** |
| 145 |
* Scan and disable Google Fonts API that loaded manually by the theme or plugin through `wp_enqueue_style` function. |
| 146 |
*/ |
| 147 |
private function disable_user_google_fonts() |
| 148 |
{ |
| 149 |
$is_disable = Config::get('misc.disable_user_google_fonts', \false); |
| 150 |
if (!$is_disable) { |
| 151 |
return; |
| 152 |
} |
| 153 |
global $wp_styles; |
| 154 |
foreach ($wp_styles->queue as $q) { |
| 155 |
if ($wp_styles->registered[$q]->src && strpos($wp_styles->registered[$q]->src, 'fonts.googleapis.com') !== \false) { |
| 156 |
wp_dequeue_style($q); |
| 157 |
wp_deregister_style($q); |
| 158 |
} |
| 159 |
} |
| 160 |
} |
| 161 |
} |
| 162 |
|