PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / modules / google-fonts / current / class-jetpack-google-font-face.php

class-jetpack-google-font-face.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at modules/google-fonts/current/class-jetpack-google-font-face.php

260 lines 7.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Jetpack_Google_Font_Face class
4 *
5 * @package automattic/jetpack
6 */
7
8 /**
9 * Jetpack Google Font Face disables Font Face hooks in Core that prints **ALL** font faces.
10 * Instead, it collects fonts that are used in global styles or block-level settings and
11 * print those fonts in use.
12 *
13 * @phan-constructor-used-for-side-effects
14 */
15 class Jetpack_Google_Font_Face {
16 /**
17 * The fonts that are used in global styles or block-level settings.
18 *
19 * @var array
20 */
21 private $fonts_in_use = array();
22
23 /**
24 * The constructor.
25 */
26 public function __construct() {
27 // Turns off hooks to print fonts
28 add_action( 'wp_loaded', array( $this, 'wp_loaded' ) );
29 add_action( 'current_screen', array( $this, 'current_screen' ), 10 );
30
31 // Collect and print fonts in use
32 if ( wp_is_block_theme() ) {
33 add_action( 'wp_head', array( $this, 'print_font_faces' ), 50 );
34 } else {
35 // In classic themes wp_head runs before the blocks are processed to collect the block fonts.
36 add_action( 'wp_footer', array( $this, 'print_font_faces' ), 50 );
37 }
38 add_filter( 'pre_render_block', array( $this, 'collect_block_fonts' ), 10, 2 );
39 }
40
41 /**
42 * Turn off hooks to print fonts on frontend
43 */
44 public function wp_loaded() {
45 remove_action( 'wp_head', 'wp_print_fonts', 50 );
46 remove_action( 'wp_head', 'wp_print_font_faces', 50 );
47 // Gutenberg 22.4+ overrides Core's font printing with its own function
48 // for classic theme support. Remove it so we can print only fonts in use.
49 remove_action( 'wp_head', 'gutenberg_print_font_faces', 50 );
50 }
51
52 /**
53 * Turn off hooks to print fonts on wp-admin, except for GB editor pages.
54 */
55 public function current_screen() {
56 remove_action( 'admin_print_styles', 'wp_print_fonts', 50 );
57
58 if ( ! $this->is_block_editor() ) {
59 remove_action( 'admin_print_styles', 'wp_print_font_faces', 50 );
60 }
61 }
62
63 /**
64 * Print fonts that are used in global styles or block-level settings.
65 */
66 public function print_font_faces() {
67 $fonts = WP_Font_Face_Resolver::get_fonts_from_theme_json();
68 $font_slug_aliases = $this->get_font_slug_aliases();
69 $fonts_to_print = array();
70
71 $this->collect_global_styles_fonts();
72 $fonts_in_use = array_values( array_unique( $this->fonts_in_use, SORT_STRING ) );
73 $fonts_in_use = array_map(
74 function ( $font_slug ) use ( $font_slug_aliases ) {
75 return $font_slug_aliases[ $font_slug ] ?? $font_slug;
76 },
77 $fonts_in_use
78 );
79
80 foreach ( $fonts as $font_faces ) {
81 $font_family = $font_faces[0]['font-family'] ?? '';
82 if ( in_array( $this->format_font( $font_family ), $fonts_in_use, true ) ) {
83 $fonts_to_print[] = $font_faces;
84 }
85 }
86
87 if ( ! empty( $fonts_to_print ) ) {
88 wp_print_font_faces( $fonts_to_print );
89 }
90 }
91
92 /**
93 * Collect fonts used for global styles settings.
94 */
95 public function collect_global_styles_fonts() {
96 $global_styles = wp_get_global_styles();
97
98 $global_styles_font_slug = $this->get_font_slug_from_setting( $global_styles );
99 if ( $global_styles_font_slug ) {
100 $this->add_font( $global_styles_font_slug );
101 }
102
103 if ( isset( $global_styles['blocks'] ) ) {
104 foreach ( $global_styles['blocks'] as $setting ) {
105 $font_slug = $this->get_font_slug_from_setting( $setting );
106
107 if ( $font_slug ) {
108 $this->add_font( $font_slug );
109 }
110 }
111 }
112
113 if ( isset( $global_styles['elements'] ) ) {
114 foreach ( $global_styles['elements'] as $setting ) {
115 $font_slug = $this->get_font_slug_from_setting( $setting );
116
117 if ( $font_slug ) {
118 $this->add_font( $font_slug );
119 }
120 }
121 }
122 }
123
124 /**
125 * Collect fonts used for block-level settings.
126 *
127 * @filter pre_render_block
128 *
129 * @param string|null $content The pre-rendered content. Default null.
130 * @param array $parsed_block The block being rendered.
131 */
132 public function collect_block_fonts( $content, $parsed_block ) {
133 if ( ! is_admin() && isset( $parsed_block['attrs']['fontFamily'] ) ) {
134 $block_font_family = $parsed_block['attrs']['fontFamily'];
135 $this->add_font( $block_font_family );
136 }
137
138 return $content;
139 }
140
141 /**
142 * Add the specify font to the fonts_in_use list.
143 *
144 * @param string $font_slug The font slug.
145 */
146 public function add_font( $font_slug ) {
147 if ( is_string( $font_slug ) ) {
148 $this->fonts_in_use[] = $this->format_font( $font_slug );
149 }
150 }
151
152 /**
153 * Format the given font slug.
154 *
155 * @example "ABeeZee" formats to "abeezee"
156 * @example "ADLaM Display" formats to "adlam-display"
157 * @param string $font_slug The font slug.
158 * @return string The formatted font slug.
159 */
160 public function format_font( $font_slug ) {
161 return _wp_to_kebab_case( strtolower( $font_slug ) );
162 }
163
164 /**
165 * Get the font slug aliases that maps the font slug to the font family if they are different.
166 *
167 * The font definition may define an alias slug name, so we have to add the map from the slug name to the font family.
168 * See https://github.com/WordPress/twentytwentyfour/blob/df92472089ede6fae5924c124a93c843b84e8cbd/theme.json#L215.
169 */
170 public function get_font_slug_aliases() {
171 $font_slug_aliases = array();
172
173 $theme_json = WP_Theme_JSON_Resolver::get_theme_data();
174 $raw_data = $theme_json->get_data();
175 if ( ! empty( $raw_data['settings']['typography']['fontFamilies'] ) ) {
176 foreach ( $raw_data['settings']['typography']['fontFamilies'] as $font ) {
177 if ( ! isset( $font['fontFamily'] ) ) {
178 continue;
179 }
180 $font_family_name = $this->format_font( $this->get_font_family_name( $font ) );
181 $font_slug = $font['slug'] ?? '';
182 if ( $font_slug && $font_slug !== $font_family_name && ! array_key_exists( $font_slug, $font_slug_aliases ) ) {
183 $font_slug_aliases[ $font_slug ] = $font_family_name;
184 }
185 }
186 }
187
188 return $font_slug_aliases;
189 }
190
191 /**
192 * Get the font family name from a font.
193 *
194 * @param array $font The font definition object.
195 */
196 public static function get_font_family_name( $font ) {
197 $font_family = $font['fontFamily'];
198 if ( str_contains( $font_family, ',' ) ) {
199 $font_family = explode( ',', $font_family )[0];
200 }
201
202 return trim( $font_family, "\"'" );
203 }
204
205 /**
206 * Get the font family slug from a settings array.
207 *
208 * @param array $setting The settings object.
209 *
210 * @return string|null
211 */
212 public function get_font_slug_from_setting( $setting ) {
213 if ( ! isset( $setting['typography']['fontFamily'] ) ) {
214 return null;
215 }
216
217 $font_family = $setting['typography']['fontFamily'];
218
219 // The font family may be a reference to a path to the value stored at that location,
220 // e.g.: { "ref": "styles.elements.heading.typography.fontFamily" }.
221 // Ignore it as we also get the value stored at that location from the setting.
222 if ( ! is_string( $font_family ) ) {
223 return null;
224 }
225
226 // A preset reference can include a var() fallback and a trailing font stack.
227 preg_match( '/^\s*var\(\s*--wp--preset--font-family--(?P<slug>[^\s,()]+)\s*[,)]/', $font_family, $matches );
228
229 if ( isset( $matches['slug'] ) ) {
230 return $matches['slug'];
231 }
232
233 // Full string: var:preset|font-family|slug
234 // We do not care about the origin of the font, only its slug.
235 preg_match( '/^\s*var:preset\|font-family\|(?P<slug>[^\s,]+)/', $font_family, $matches );
236
237 if ( isset( $matches['slug'] ) ) {
238 return $matches['slug'];
239 }
240
241 return self::get_font_family_name( $setting['typography'] );
242 }
243
244 /**
245 * Check if the current screen is the block editor.
246 *
247 * @return bool
248 */
249 public function is_block_editor() {
250 if ( function_exists( 'get_current_screen' ) ) {
251 $current_screen = get_current_screen();
252 if ( ! empty( $current_screen ) && method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() ) {
253 return true;
254 }
255 }
256
257 return false;
258 }
259 }
260