Completed
Pull Request — develop (#1301)
by Aristeides
07:59 queued 02:09
created

Kirki_Fonts_Google::set_method()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 1
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Processes typography-related fields
4
 * and generates the google-font link.
5
 *
6
 * @package     Kirki
7
 * @category    Core
8
 * @author      Aristeides Stathopoulos
9
 * @copyright   Copyright (c) 2016, Aristeides Stathopoulos
10
 * @license     http://opensource.org/licenses/https://opensource.org/licenses/MIT
11
 * @since       1.0
12
 */
13
14
/**
15
 * Manages the way Google Fonts are enqueued.
16
 */
17
final class Kirki_Fonts_Google {
18
19
	/**
20
	 * The Kirki_Fonts_Google instance.
21
	 * We use the singleton pattern here to avoid loading the google-font array multiple times.
22
	 * This is mostly a performance tweak.
23
	 *
24
	 * @access private
25
	 * @var null|object
26
	 */
27
	private static $instance = null;
28
29
	/**
30
	 * If set to true, forces loading ALL variants.
31
	 *
32
	 * @static
33
	 * @access public
34
	 * @var bool
35
	 */
36
	public static $force_load_all_variants = false;
37
38
	/**
39
	 * If set to true, forces loading ALL subsets.
40
	 *
41
	 * @static
42
	 * @access public
43
	 * @var bool
44
	 */
45
	public static $force_load_all_subsets = false;
46
47
	/**
48
	 * The array of fonts
49
	 *
50
	 * @access private
51
	 * @var array
52
	 */
53
	private $fonts = array();
54
55
	/**
56
	 * An array of all google fonts.
57
	 *
58
	 * @access private
59
	 * @var array
60
	 */
61
	private $google_fonts = array();
62
63
	/**
64
	 * The array of subsets
65
	 *
66
	 * @access private
67
	 * @var array
68
	 */
69
	private $subsets = array();
70
71
	/**
72
	 * The google link
73
	 *
74
	 * @access private
75
	 * @var string
76
	 */
77
	private $link = '';
78
79
	/**
80
	 * Which method to use when loading googlefonts.
81
	 * Available options: link, js, embed.
82
	 *
83
	 * @static
84
	 * @access private
85
	 * @since 3.0.0
86
	 * @var string
87
	 */
88
	private static $method = 'link';
89
90
	/**
91
	 * The class constructor.
92
	 */
93
	private function __construct() {
94
95
		$config = apply_filters( 'kirki/config', array() );
96
97
		// If we have set $config['disable_google_fonts'] to true then do not proceed any further.
98
		if ( isset( $config['disable_google_fonts'] ) && true === $config['disable_google_fonts'] ) {
99
			return;
100
		}
101
102
		// Populate the array of google fonts.
103
		$this->google_fonts = Kirki_Fonts::get_google_fonts();
104
105
		switch ( self::$method ) {
106
107
			case 'embed':
108
				// TODO: Build a method for embeds.
109
				break;
110
			case 'js':
111
				// TODO: Build a JS method.
112
				break;
113
			default:
114
				// Enqueue link.
115
				add_action( 'wp_enqueue_scripts', array( $this, 'enqueue' ), 105 );
116
				break;
117
		}
118
119
	}
120
121
	/**
122
	 * Get the one, true instance of this class.
123
	 * Prevents performance issues since this is only loaded once.
124
	 *
125
	 * @return object Kirki_Fonts_Google
126
	 */
127
	public static function get_instance() {
128
		if ( null === self::$instance ) {
129
			self::$instance = new Kirki_Fonts_Google();
130
		}
131
		return self::$instance;
132
	}
133
134
	/**
135
	 * Calls all the other necessary methods to populate and create the link.
136
	 */
137
	public function enqueue() {
138
139
		// Go through our fields and populate $this->fonts.
140
		$this->loop_fields();
141
142
		$this->fonts = apply_filters( 'kirki/enqueue_google_fonts', $this->fonts );
143
144
		// Goes through $this->fonts and adds or removes things as needed.
145
		$this->process_fonts();
146
147
		// Go through $this->fonts and populate $this->link.
148
		$this->create_link();
149
150
		// If $this->link is not empty then enqueue it.
151
		if ( '' !== $this->link ) {
152
			wp_enqueue_style( 'kirki_google_fonts', $this->link, array(), null );
153
		}
154
	}
155
156
	/**
157
	 * Goes through all our fields and then populates the $this->fonts property.
158
	 */
159
	private function loop_fields() {
160
		foreach ( Kirki::$fields as $field ) {
161
			$this->generate_google_font( $field );
162
		}
163
	}
164
165
	/**
166
	 * Processes the arguments of a field
167
	 * determines if it's a typography field
168
	 * and if it is, then takes appropriate actions.
169
	 *
170
	 * @param array $args The field arguments.
171
	 */
172
	private function generate_google_font( $args ) {
173
174
		// Process typography fields.
175
		if ( isset( $args['type'] ) && 'kirki-typography' === $args['type'] ) {
176
177
			// Get the value.
178
			$value = Kirki_Values::get_sanitized_field_value( $args );
179
180
			// If we don't have a font-family then we can skip this.
181
			if ( ! isset( $value['font-family'] ) ) {
182
				return;
183
			}
184
185
			// Add support for older formats of the typography control.
186
			// We used to have font-weight instead of variant.
187
			if ( isset( $value['font-weight'] ) && ( ! isset( $value['variant'] ) || empty( $value['variant'] ) ) ) {
188
				$value['variant'] = $value['font-weight'];
189
			}
190
191
			// Set a default value for variants.
192
			if ( ! isset( $value['variant'] ) ) {
193
				$value['variant'] = 'regular';
194
			}
195
			if ( isset( $value['subsets'] ) ) {
196
197
				// Add the subset directly to the array of subsets in the Kirki_GoogleFonts_Manager object.
198
				// Subsets must be applied to ALL fonts if possible.
199
				if ( ! is_array( $value['subsets'] ) ) {
200
					$this->subsets[] = $value['subsets'];
201
				} else {
202
					foreach ( $value['subsets'] as $subset ) {
203
						$this->subsets[] = $subset;
204
					}
205
				}
206
			}
207
208
			// Add the requested google-font.
209
			if ( ! isset( $this->fonts[ $value['font-family'] ] ) ) {
210
				$this->fonts[ $value['font-family'] ] = array();
211
			}
212
			if ( ! in_array( $value['variant'], $this->fonts[ $value['font-family'] ], true ) ) {
213
				$this->fonts[ $value['font-family'] ][] = $value['variant'];
214
			}
215
		} else {
216
217
			// Process non-typography fields.
218
			if ( isset( $args['output'] ) && is_array( $args['output'] ) ) {
219
				foreach ( $args['output'] as $output ) {
220
221
					// If we don't have a typography-related output argument we can skip this.
222
					if ( ! isset( $output['property'] ) || ! in_array( $output['property'], array( 'font-family', 'font-weight', 'font-subset', 'subset', 'subsets' ), true ) ) {
223
						continue;
224
					}
225
226
					// Get the value.
227
					$value = Kirki_Values::get_sanitized_field_value( $args );
228
229
					if ( 'font-family' === $output['property'] ) {
230
						if ( ! array_key_exists( $value, $this->fonts ) ) {
231
							$this->fonts[ $value ] = array();
232
						}
233
					} elseif ( 'font-weight' === $output['property'] ) {
234
						foreach ( $this->fonts as $font => $variants ) {
235
							if ( ! in_array( $value, $variants, true ) ) {
236
								$this->fonts[ $font ][] = $value;
237
							}
238
						}
239
					} elseif ( 'font-subset' === $output['property'] || 'subset' === $output['property'] || 'subsets' === $output['property'] ) {
240
						if ( ! is_array( $value ) ) {
241
							if ( ! in_array( $value, $this->subsets, true ) ) {
242
								$this->subsets[] = $value;
243
							}
244
						} else {
245
							foreach ( $value as $subset ) {
246
								if ( ! in_array( $subset, $this->subsets, true ) ) {
247
									$this->subsets[] = $subset;
248
								}
249
							}
250
						}
251
					}
252
				}
253
			} // End if().
254
		} // End if().
255
	}
256
257
	/**
258
	 * Determines the vbalidity of the selected font as well as its properties.
259
	 * This is vital to make sure that the google-font script that we'll generate later
260
	 * does not contain any invalid options.
261
	 */
262
	private function process_fonts() {
263
264
		// Early exit if font-family is empty.
265
		if ( empty( $this->fonts ) ) {
266
			return;
267
		}
268
269
		$valid_subsets = array();
270
		foreach ( $this->fonts as $font => $variants ) {
271
272
			// Determine if this is indeed a google font or not.
273
			// If it's not, then just remove it from the array.
274
			if ( ! array_key_exists( $font, $this->google_fonts ) ) {
275
				unset( $this->fonts[ $font ] );
276
				continue;
277
			}
278
279
			// Get all valid font variants for this font.
280
			$font_variants = array();
281
			if ( isset( $this->google_fonts[ $font ]['variants'] ) ) {
282
				$font_variants = $this->google_fonts[ $font ]['variants'];
283
			}
284
			foreach ( $variants as $variant ) {
285
286
				// If this is not a valid variant for this font-family
287
				// then unset it and move on to the next one.
288
				if ( ! in_array( $variant, $font_variants, true ) ) {
289
					$variant_key = array_search( $variant, $this->fonts[ $font ] );
290
					unset( $this->fonts[ $font ][ $variant_key ] );
291
					continue;
292
				}
293
			}
294
295
			// Check if the selected subsets exist, even in one of the selected fonts.
296
			// If they don't, then they have to be removed otherwise the link will fail.
297
			if ( isset( $this->google_fonts[ $font ]['subsets'] ) ) {
298
				foreach ( $this->subsets as $subset ) {
299
					if ( in_array( $subset, $this->google_fonts[ $font ]['subsets'], true ) ) {
300
						$valid_subsets[] = $subset;
301
					}
302
				}
303
			}
304
		}
305
		$this->subsets = $valid_subsets;
306
	}
307
308
	/**
309
	 * Creates the google-fonts link.
310
	 */
311
	private function create_link() {
312
313
		// If we don't have any fonts then we can exit.
314
		if ( empty( $this->fonts ) ) {
315
			return;
316
		}
317
318
		// Add a fallback to Roboto.
319
		$font = 'Roboto';
320
321
		// Get font-family + subsets.
322
		$link_fonts = array();
323
		foreach ( $this->fonts as $font => $variants ) {
324
325
			// Are we force-loading all variants?
326
			if ( true === self::$force_load_all_variants ) {
327
				if ( isset( $this->google_fonts[ $font ]['variants'] ) ) {
328
					$variants = $this->google_fonts[ $font ]['variants'];
329
				}
330
			}
331
			$variants = implode( ',', $variants );
332
333
			$link_font = str_replace( ' ', '+', $font );
334
			if ( ! empty( $variants ) ) {
335
				$link_font .= ':' . $variants;
336
			}
337
			$link_fonts[] = $link_font;
338
		}
339
340
		// Are we force-loading all subsets?
341
		if ( true === self::$force_load_all_subsets ) {
342
343
			if ( isset( $this->google_fonts[ $font ]['subsets'] ) ) {
344
				foreach ( $this->google_fonts[ $font ]['subsets'] as $subset ) {
345
					$this->subsets[] = $subset;
346
				}
347
			}
348
		}
349
350
		if ( ! empty( $this->subsets ) ) {
351
			$this->subsets = array_unique( $this->subsets );
352
		}
353
354
		$this->link = add_query_arg( array(
355
			'family' => str_replace( '%2B', '+', urlencode( implode( '|', $link_fonts ) ) ),
356
			'subset' => urlencode( implode( ',', $this->subsets ) ),
357
		), 'https://fonts.googleapis.com/css' );
358
359
	}
360
361
	/**
362
	 * Sets the method to use for loading the fonts.
363
	 *
364
	 * @static
365
	 * @access public
366
	 * @since 3.0.0
367
	 * @param string $method The method to use.
368
	 */
369
	public function set_method( $method = 'link' ) {
370
371
		$valid_methods = array(
372
			'link',
373
			'js',
374
			'embed',
375
		);
376
		// Early exit if the defined method is invalid.
377
		if ( ! in_array( $method, $valid_methods ) ) {
378
			return;
379
		}
380
		self::$method = $method;
381
	}
382
}
383