Completed
Push — develop ( 5eb1e2...3c302f )
by Aristeides
02:12
created

My_Theme_Kirki::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
// Exit if accessed directly
4
if ( ! defined( 'ABSPATH' ) ) {
5
	exit;
6
}
7
8
/**
9
 * This is a wrapper class for Kirki.
10
 * If the Kirki plugin is installed, then all CSS & Google fonts
11
 * will be handled by the plugin.
12
 * In case the plugin is not installed, this acts as a fallback
13
 * ensuring that all CSS & fonts still work.
14
 * It does not handle the customizer options, simply the frontend CSS.
15
 */
16
class My_Theme_Kirki {
17
18
	/**
19
	 * @static
20
	 * @access protected
21
	 * @var array
22
	 */
23
	protected static $config = array();
24
25
	/**
26
	 * @static
27
	 * @access protected
28
	 * @var array
29
	 */
30
	protected static $fields = array();
31
32
	/**
33
	 * The class constructor
34
	 */
35
	public function __construct() {
36
		// If Kirki exists then there's no reason to procedd
37
		if ( class_exists( 'Kirki' ) ) {
38
			return;
39
		}
40
		// Add our CSS
41
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ), 20 );
42
		// Add google fonts
43
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_fonts' ) );
44
	}
45
46
	/**
47
	 * Get the value of an option from the db.
48
	 *
49
	 * @param    string    $config_id    The ID of the configuration corresponding to this field
50
	 * @param    string    $field_id     The field_id (defined as 'settings' in the field arguments)
51
	 *
52
	 * @return 	mixed 	the saved value of the field.
53
	 */
54
	public static function get_option( $config_id = '', $field_id = '' ) {
55
		// if Kirki exists, use it.
56
		if ( class_exists( 'Kirki' ) ) {
57
			return Kirki::get_option( $config_id, $field_id );
58
		}
59
		// Kirki does not exist, continue with our custom implementation.
60
		// Get the default value of the field
61
		$default = '';
62
		if ( isset( self::$fields[ $field_id ] ) && isset( self::$fields[ $field_id ]['default'] ) ) {
63
			$default = self::$fields[ $field_id ]['default'];
64
		}
65
		// Make sure the config is defined
66
		if ( isset( self::$config[ $config_id ] ) ) {
67
			if ( 'option' == self::$config[ $config_id ]['option_type'] ) {
68
				// check if we're using serialized options
69
				if ( isset( self::$config[ $config_id ]['option_name'] ) && ! empty( self::$config[ $config_id ]['option_name'] ) ) {
70
					// Get all our options
71
					$all_options = get_option( self::$config[ $config_id ]['option_name'], array() );
72
					// If our option is not saved, return the default value.
73
					if ( ! isset( $all_options[ $field_id ] ) ) {
74
						return $default;
75
					}
76
					// Option was set, return its value unserialized.
77
					return maybe_unserialize( $all_options[ $field_id ] );
78
				}
79
				// If we're not using serialized options, get the value and return it.
80
				// We'll be using a dummy default here to check if the option has been set or not.
81
				// We'll be using md5 to make sure it's randomish and impossible to be actually set by a user.
82
				$dummy = md5( $config_id . '_UNDEFINED_VALUE' );
83
				$value = get_option( $field_id, $dummy );
84
				// setting has not been set, return default.
85
				if ( $dummy == $value ) {
86
					return $default;
87
				}
88
				return $value;
89
			}
90
			// We're not using options so fallback to theme_mod
91
			return get_theme_mod( $field_id, $default );
92
		}
93
	}
94
95
	/**
96
	 * Create a new panel
97
	 *
98
	 * @param   string      the ID for this panel
99
	 * @param   array       the panel arguments
100
	 */
101
	public static function add_panel( $id = '', $args = array() ) {
102
		if ( class_exists( 'Kirki' ) ) {
103
			Kirki::add_panel( $id, $args );
104
		}
105
		// If Kirki does not exist then there's no reason to add any panels.
106
	}
107
108
	/**
109
	 * Create a new section
110
	 *
111
	 * @param   string      the ID for this section
112
	 * @param   array       the section arguments
113
	 */
114
	public static function add_section( $id, $args ) {
115
		if ( class_exists( 'Kirki' ) ) {
116
			Kirki::add_section( $id, $args );
117
		}
118
		// If Kirki does not exist then there's no reason to add any sections.
119
	}
120
121
122
	/**
123
	 * Sets the configuration options.
124
	 *
125
	 * @param    string    $config_id    The configuration ID
126
	 * @param    array     $args         The configuration arguments
127
	 */
128
	public static function add_config( $config_id, $args = array() ) {
129
		// if Kirki exists, use it.
130
		if ( class_exists( 'Kirki' ) ) {
131
			Kirki::add_config( $config_id, $args );
132
			return;
133
		}
134
		// Kirki does not exist, set the config arguments
135
		$config[ $config_id ] = $args;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$config was never initialized. Although not strictly required by PHP, it is generally a good practice to add $config = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
136
		// Make sure an option_type is defined
137
		if ( ! isset( self::$config[ $config_id ]['option_type'] ) ) {
138
			self::$config[ $config_id ]['option_type'] = 'theme_mod';
139
		}
140
	}
141
142
	/**
143
	 * Create a new field
144
	 *
145
	 * @param    string    $config_id    The configuration ID
146
	 * @param    array     $args         The field's arguments
147
	 */
148
	public static function add_field( $config_id, $args ) {
149
		// if Kirki exists, use it.
150
		if ( class_exists( 'Kirki' ) ) {
151
			Kirki::add_field( $config_id, $args );
152
			return;
153
		}
154
		// Kirki was not located, so we'll need to add our fields here.
155
		// check that the "settings" & "type" arguments have been defined
156
		if ( isset( $args['settings'] ) && isset( $args['type'] ) ) {
157
			// Make sure we add the config_id to the field itself.
158
			// This will make it easier to get the value when generating the CSS later.
159
			if ( ! isset( $args['kirki_config'] ) ) {
160
				$args['kirki_config'] = $config_id;
161
			}
162
			self::$fields[ $args['settings'] ] = $args;
163
		}
164
	}
165
166
	/**
167
	 * Enqueues the stylesheet
168
	 */
169
	public function enqueue_styles() {
170
		// If Kirki exists there's no need to proceed any further
171
		if ( class_exists( 'Kirki' ) ) {
172
			return;
173
		}
174
		// Get our inline styles
175
		$styles = $this->get_styles();
176
		// If we have some styles to add, add them now.
177
		if ( ! empty( $styles ) ) {
178
			// enqueue the theme's style.css file
179
			$current_theme = ( wp_get_theme() );
180
			wp_enqueue_style( $current_theme->stylesheet . '_no-kirki', get_stylesheet_uri(), null, null );
181
			wp_add_inline_style( $current_theme->stylesheet . '_no-kirki', $styles );
182
		}
183
	}
184
185
	/**
186
	 * Gets all our styles and returns them as a string.
187
	 */
188
	public function get_styles() {
189
		// Get an array of all our fields
190
		$fields = self::$fields;
191
		// Check if we need to exit early
192
		if ( empty( self::$fields ) || ! is_array( $fields ) ) {
193
			return;
194
		}
195
		// initially we're going to format our styles as an array.
196
		// This is going to make processing them a lot easier
197
		// and make sure there are no duplicate styles etc.
198
		$css = array();
199
		// start parsing our fields
200
		foreach ( $fields as $field ) {
201
			// No need to process fields without an output, or an improperly-formatted output
202
			if ( ! isset( $field['output'] ) || empty( $field['output'] ) || ! is_array( $field['output'] ) ) {
203
				continue;
204
			}
205
			// Get the value of this field
206
			$value = self::get_option( $field['kirki_config'], $field['settings'] );
207
			// start parsing the output arguments of the field
208
			foreach ( $field['output'] as $output ) {
209
				$defaults = array(
210
					'element'       => '',
211
					'property'      => '',
212
					'media_query'   => 'global',
213
					'prefix'        => '',
214
					'units'         => '',
215
					'suffix'        => '',
216
					'value_pattern' => '$',
217
					'choice'        => '',
218
				);
219
				$output = wp_parse_args( $output, $defaults );
220
				// If element is an array, convert it to a string
221
				if ( is_array( $output['element'] ) ) {
222
					$output['element'] = array_unique( $output['element'] );
223
					sort( $output['element'] );
224
					$output['element'] = implode( ',', $output['element'] );
225
				}
226
				// Simple fields
227
				if ( ! is_array( $value ) ) {
228
					$value = str_replace( '$', $value, $output['value_pattern'] );
229
					if ( ! empty( $output['element'] ) && ! empty( $output['property'] ) ) {
230
						$css[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $value . $output['units'] . $output['suffix'];
231
					}
232
				} else {
233
					if ( 'typography' == $field['type'] ) {
234
						foreach ( $value as $key => $subvalue ) {
235
							// exclude subsets as a property
236
							if ( 'subsets' == $key ) {
237
								continue;
238
							}
239
							// add double quotes if needed to font-families
240
							if ( 'font-family' == $key && false !== strpos( $subvalue, ' ' ) && false === strpos( $subvalue, '"' ) ) {
241
								$css[ $output['media_query'] ][ $output['element'] ]['font-family'] = '"' . $subvalue . '"';
242
							}
243
							// variants contain both font-weight & italics
244
							if ( 'variant' == $key ) {
245
								$font_weight = str_replace( 'italic', '', $subvalue );
246
								$font_weight = ( in_array( $font_weight, array( '', 'regular' ) ) ) ? '400' : $font_weight;
247
								$css[ $output['media_query'] ][ $output['element'] ]['font-weight'] = $font_weight;
248
								// Is this italic?
249
								$is_italic = ( false !== strpos( $subvalue, 'italic' ) );
250
								if ( $is_italic ) {
251
									$css[ $output['media_query'] ][ $output['element'] ]['font-style'] = 'italic';
252
								}
253
							} else {
254
								$css[ $output['media_query'] ][ $output['element'] ][ $key ] = $subvalue;
255
							}
256
						}
257
					} elseif ( 'multicolor' == $field['type'] ) {
258
						if ( ! empty( $output['element'] ) && ! empty( $output['property'] ) && ! empty( $output['choice'] ) ) {
259
							$css[ $output['media_query'] ][ $output['element'] ][ $output['property'] ] = $output['prefix'] . $value[ $output['choice'] ] . $output['units'] . $output['suffix'];
260
						}
261
					} else {
262
						foreach ( $value as $key => $subvalue ) {
263
							$property = $key;
264
							if ( false !== strpos( $output['property'], '%%' ) ) {
265
								$property = str_replace( '%%', $key, $output['property'] );
266
							} elseif ( ! empty( $output['property'] ) ) {
267
								$output['property'] = $output['property'] . '-' . $key;
268
							}
269
							if ( 'background-image' === $output['property'] && false === strpos( $subvalue, 'url(' ) ) {
270
								$subvalue = 'url("' . $subvalue . '")';
271
							}
272
							if ( $subvalue ) {
273
								$css[ $output['media_query'] ][ $output['element'] ][ $property ] = $subvalue;
274
							}
275
						}
276
					}
277
				}
278
			}
279
		}
280
		// Process the array of CSS properties and produce the final CSS
281
		$final_css = '';
282
		if ( ! is_array( $css ) || empty( $css ) ) {
283
			return '';
284
		}
285
		// Parse the generated CSS array and create the CSS string for the output.
286
		foreach ( $css as $media_query => $styles ) {
287
			// Handle the media queries
288
			$final_css .= ( 'global' != $media_query ) ? $media_query . '{' : '';
289
			foreach ( $styles as $style => $style_array ) {
290
				$final_css .= $style . '{';
291
					foreach ( $style_array as $property => $value ) {
292
						$value = ( is_string( $value ) ) ? $value : '';
293
						// Make sure background-images are properly formatted
294
						if ( 'background-image' == $property ) {
295
							if ( false === strrpos( $value, 'url(' ) ) {
296
								$value = 'url("' . esc_url_raw( $value ) . '")';
297
							}
298
						} else {
299
							$value = esc_textarea( $value );
300
						}
301
						$final_css .= $property . ':' . $value . ';';
302
					}
303
				$final_css .= '}';
304
			}
305
			$final_css .= ( 'global' != $media_query ) ? '}' : '';
306
		}
307
		return $final_css;
308
	}
309
310
	public function enqueue_fonts() {
311
		// Check if we need to exit early
312
		if ( empty( self::$fields ) || ! is_array( self::$fields ) ) {
313
			return;
314
		}
315
		foreach ( self::$fields as $field ) {
316
			// Process typography fields
317
			if ( isset( $field['type'] ) && 'typography' == $field['type'] ) {
318
				// Check if we've got everything we need
319
				if ( ! isset( $field['kirki_config'] ) || ! isset( $field['settings'] ) ) {
320
					continue;
321
				}
322
				$value = self::get_option( $field['kirki_config'], $field['settings'] );
323
				if ( isset( $value['font-family'] ) ) {
324
					$url = '//fonts.googleapis.com/css?family=' . str_replace( ' ', '+', $value['font-family'] );
325
					if ( ! isset( $value['variant'] ) ) {
326
						$value['variant'] = '';
327
					}
328
					if ( ! empty( $value['variant'] ) ) {
329
						$url .= ':' . $value['variant'];
330
					}
331
					if ( ! isset( $value['subset'] ) ) {
332
						$value['subset'] = '';
333
					}
334
					if ( ! empty( $value['subset'] ) ) {
335
						if ( is_array( $value['subset'] ) ) {
336
							$value['subset'] = implode( ',', $value['subsets'] );
337
						}
338
						$url .= '&subset=' . $value['subset'];
339
					}
340
					$key = md5( $value['font-family'] . $value['variant'] . $value['subset'] );
341
					// check that the URL is valid. we're going to use transients to make this faster.
342
					$url_is_valid = get_transient( $key );
343
					if ( false === $url_is_valid ) { // transient does not exist
344
						$response = wp_remote_get( 'https:' . $url );
0 ignored issues
show
introduced by
wp_remote_get is highly discouraged, please use vip_safe_wp_remote_get() instead.
Loading history...
345
						if ( ! is_array( $response ) ) {
346
							// the url was not properly formatted,
347
							// cache for 12 hours and continue to the next field
348
							set_transient( $key, null, 12 * HOUR_IN_SECONDS );
349
							continue;
350
						}
351
						// check the response headers.
352
						if ( isset( $response['response'] ) && isset( $response['response']['code'] ) ) {
353
							if ( 200 == $response['response']['code'] ) {
354
								// URL was ok
355
								// set transient to true and cache for a week
356
								set_transient( $key, true, 7 * 24 * HOUR_IN_SECONDS );
357
								$url_is_valid = true;
358
							}
359
						}
360
					}
361
					// If the font-link is valid, enqueue it.
362
					if ( $url_is_valid ) {
363
						wp_enqueue_style( $key, $url, null, null );
364
					}
365
				}
366
			}
367
		}
368
	}
369
}
370
new My_Theme_Kirki();
371