Completed
Push — develop ( 43cbe8...ffe0ee )
by Aristeides
06:31 queued 03:39
created

Kirki_Field::set_js_vars()   C

Complexity

Conditions 14
Paths 48

Size

Total Lines 53
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 21
nc 48
nop 0
dl 0
loc 53
rs 6.3132
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Creates and validates field parameters.
4
 *
5
 * @package     Kirki
6
 * @category    Core
7
 * @author      Aristeides Stathopoulos
8
 * @copyright   Copyright (c) 2017, Aristeides Stathopoulos
9
 * @license     http://opensource.org/licenses/https://opensource.org/licenses/MIT
10
 * @since       1.0
11
 */
12
13
/**
14
 * Please do not use this class directly.
15
 * You should instead extend it per-field-type.
16
 */
17
class Kirki_Field {
18
19
	/**
20
	 * An array of the field arguments.
21
	 *
22
	 * @access protected
23
	 * @var array
24
	 */
25
	protected $args = array();
26
27
	/**
28
	 * The ID of the kirki_config we're using.
29
	 *
30
	 * @see Kirki_Config
31
	 * @access protected
32
	 * @var string
33
	 */
34
	protected $kirki_config = 'global';
35
36
	/**
37
	 * Thje capability required so that users can edit this field.
38
	 *
39
	 * @access protected
40
	 * @var string
41
	 */
42
	protected $capability = 'edit_theme_options';
43
44
	/**
45
	 * If we're using options instead of theme_mods
46
	 * and we want them serialized, this is the option that
47
	 * will saved in the db.
48
	 *
49
	 * @access protected
50
	 * @var string
51
	 */
52
	protected $option_name = '';
53
54
	/**
55
	 * Custom input attributes (defined as an array).
56
	 *
57
	 * @access protected
58
	 * @var array
59
	 */
60
	protected $input_attrs = array();
61
62
	/**
63
	 * Use "theme_mod" or "option".
64
	 *
65
	 * @access protected
66
	 * @var string
67
	 */
68
	protected $option_type = 'theme_mod';
69
70
	/**
71
	 * The name of this setting (id for the db).
72
	 *
73
	 * @access protected
74
	 * @var string|array
75
	 */
76
	protected $settings = '';
77
78
	/**
79
	 * Set to true if you want to disable all CSS output for this field.
80
	 *
81
	 * @access protected
82
	 * @var bool
83
	 */
84
	protected $disable_output = false;
85
86
	/**
87
	 * The field type.
88
	 *
89
	 * @access protected
90
	 * @var string
91
	 */
92
	protected $type = 'kirki-generic';
93
94
	/**
95
	 * Some fields require options to be set.
96
	 * We're whitelisting the property here
97
	 * and suggest you validate this in a child class.
98
	 *
99
	 * @access protected
100
	 * @var array
101
	 */
102
	protected $choices = array();
103
104
	/**
105
	 * Assign this field to a section.
106
	 * Fields not assigned to a section will not be displayed in the customizer.
107
	 *
108
	 * @access protected
109
	 * @var string
110
	 */
111
	protected $section = '';
112
113
	/**
114
	 * The default value for this field.
115
	 *
116
	 * @access protected
117
	 * @var string|array|bool
118
	 */
119
	protected $default = '';
120
121
	/**
122
	 * Priority determines the position of a control inside a section.
123
	 * Lower priority numbers move the control to the top.
124
	 *
125
	 * @access protected
126
	 * @var int
127
	 */
128
	protected $priority = 10;
129
130
	/**
131
	 * Unique ID for this field.
132
	 * This is auto-calculated from the $settings argument.
133
	 *
134
	 * @access protected
135
	 * @var string
136
	 */
137
	protected $id = '';
138
139
	/**
140
	 * Use if you want to automatically generate CSS from this field's value.
141
	 *
142
	 * @see https://kirki.org/docs/arguments/output
143
	 * @access protected
144
	 * @var array
145
	 */
146
	protected $output = array();
147
148
	/**
149
	 * Use to automatically generate postMessage scripts.
150
	 * Not necessary to use if you use 'transport' => 'auto'
151
	 * and have already set an array for the 'output' argument.
152
	 *
153
	 * @see https://kirki.org/docs/arguments/js_vars
154
	 * @access protected
155
	 * @var array
156
	 */
157
	protected $js_vars = array();
158
159
	/**
160
	 * If you want to use a CSS compiler, then use this to set the variable names.
161
	 *
162
	 * @see https://kirki.org/docs/arguments/variables
163
	 * @access protected
164
	 * @var array
165
	 */
166
	protected $variables = array();
167
168
	/**
169
	 * Text that will be used in a tooltip to provide extra info for this field.
170
	 *
171
	 * @access protected
172
	 * @var string
173
	 */
174
	protected $tooltip = '';
175
176
	/**
177
	 * A custom callback to determine if the field should be visible or not.
178
	 *
179
	 * @access protected
180
	 * @var string|array
181
	 */
182
	protected $active_callback = '__return_true';
183
184
	/**
185
	 * A custom sanitize callback that will be used to properly save the values.
186
	 *
187
	 * @access protected
188
	 * @var string|array
189
	 */
190
	protected $sanitize_callback = '';
191
192
	/**
193
	 * Use 'refresh', 'postMessage' or 'auto'.
194
	 * 'auto' will automatically geberate any 'js_vars' from the 'output' argument.
195
	 *
196
	 * @access protected
197
	 * @var string
198
	 */
199
	protected $transport = 'refresh';
200
201
	/**
202
	 * Define dependencies to show/hide this field based on the values of other fields.
203
	 *
204
	 * @access protected
205
	 * @var array
206
	 */
207
	protected $required = array();
208
209
	/**
210
	 * Partial Refreshes array.
211
	 *
212
	 * @access protected
213
	 * @var array
214
	 */
215
	protected $partial_refresh = array();
216
217
	/**
218
	 * The class constructor.
219
	 * Parses and sanitizes all field arguments.
220
	 * Then it adds the field to Kirki::$fields.
221
	 *
222
	 * @access public
223
	 * @param string $config_id    The ID of the config we want to use.
224
	 *                             Defaults to "global".
225
	 *                             Configs are handled by the Kirki_Config class.
226
	 * @param array  $args         The arguments of the field.
227
	 */
228
	public function __construct( $config_id = 'global', $args = array() ) {
229
230
		if ( isset( $args['setting'] ) && ! empty( $args['setting'] ) && ( ! isset( $args['settings'] ) || empty( $args['settings'] ) ) ) {
231
			/* translators: %s represents the field ID where the error occurs. */
232
			_doing_it_wrong( __METHOD__, sprintf( esc_attr__( 'Typo found in field %s - setting instead of settings.', 'kirki' ), esc_attr( $args['settings'] ) ), '3.0.10' );
233
			$args['settings'] = $args['setting'];
234
			unset( $args['setting'] );
235
		}
236
237
		// In case the user only provides 1 argument,
238
		// assume that the provided argument is $args and set $config_id = 'global'.
239
		if ( is_array( $config_id ) && empty( $args ) ) {
240
			/* translators: %1$s represents the field ID where the error occurs. %2$s is the URL in the documentation site. */
241
			_doing_it_wrong( __METHOD__, sprintf( esc_attr__( 'Config not defined for field %1$s - See %2$s for details on how to properly add fields.', 'kirki' ), esc_attr( $args['settings'] ), 'https://aristath.github.io/kirki/docs/getting-started/fields.html' ), '3.0.10' );
242
			$args = $config_id;
243
			$config_id = 'global';
244
		}
245
246
		$args['kirki_config'] = $config_id;
247
248
		$this->kirki_config = trim( esc_attr( $config_id ) );
249
		if ( '' === $config_id ) {
250
			/* translators: %1$s represents the field ID where the error occurs. %2$s is the URL in the documentation site. */
251
			_doing_it_wrong( __METHOD__, sprintf( esc_attr__( 'Config not defined for field %1$s - See %2$s for details on how to properly add fields.', 'kirki' ), esc_attr( $args['settings'] ), 'https://aristath.github.io/kirki/docs/getting-started/fields.html' ), '3.0.10' );
252
			$this->kirki_config = 'global';
253
		}
254
255
		// Get defaults from the class.
256
		$defaults = get_class_vars( __CLASS__ );
257
258
		// Get the config arguments, and merge them with the defaults.
259
		$config_defaults = ( isset( Kirki::$config['global'] ) ) ? Kirki::$config['global'] : array();
260
		if ( 'global' !== $this->kirki_config && isset( Kirki::$config[ $this->kirki_config ] ) ) {
261
			$config_defaults = Kirki::$config[ $this->kirki_config ];
262
		}
263
		$config_defaults = ( is_array( $config_defaults ) ) ? $config_defaults : array();
264
		foreach ( $config_defaults as $key => $value ) {
265
			if ( isset( $defaults[ $key ] ) && ! empty( $value ) && $value !== $defaults[ $key ] ) {
266
				$defaults[ $key ] = $value;
267
			}
268
		}
269
270
		// Merge our args with the defaults.
271
		$args = wp_parse_args( $args, $defaults );
272
273
		// Set the class properties using the parsed args.
274
		foreach ( $args as $key => $value ) {
275
			$this->$key = $value;
276
		}
277
278
		$this->args = $args;
279
280
		$this->set_field();
281
	}
282
283
	/**
284
	 * Processes the field arguments
285
	 *
286
	 * @access protected
287
	 */
288
	protected function set_field() {
289
290
		$properties = get_class_vars( __CLASS__ );
291
292
		// Some things must run before the others.
293
		$this->set_option_name();
294
		$this->set_option_type();
295
		$this->set_settings();
296
297
		// Sanitize the properties, skipping the ones that have already run above.
298
		foreach ( $properties as $property => $value ) {
299
			if ( in_array( $property, array( 'option_name', 'option_type', 'settings' ), true ) ) {
300
				continue;
301
			}
302
			$property_class_name = $this->get_property_classname( $property );
303
			if ( class_exists( $property_class_name ) ) {
304
				$property_obj    = new $property_class_name( $this->args );
305
				$this->$property = $property_obj->get_property();
306
			}
307
			if ( method_exists( $this, 'set_' . $property ) ) {
308
				$method_name = 'set_' . $property;
309
				$this->$method_name();
310
			}
311
		}
312
313
		// Get all arguments with their values.
314
		$args = get_object_vars( $this );
315
		foreach ( array_keys( $args ) as $key ) {
316
			$args[ $key ] = $this->$key;
317
		}
318
319
		// Add the field to the static $fields variable properly indexed.
320
		Kirki::$fields[ $this->settings ] = $args;
321
322
	}
323
324
	/**
325
	 * Gets the classname from a property.
326
	 *
327
	 * @access private
328
	 * @since 3.0.10
329
	 * @param string $property The property.
330
	 * @return string          A classname derived from the property.
331
	 */
332
	private function get_property_classname( $property ) {
333
		$property_parts = (array) $property;
334
		if ( false !== strpos( $property, '-' ) ) {
335
			$property_parts = explode( '-', $property );
336
		}
337
		if ( false !== strpos( $property, '_' ) ) {
338
			$property_parts = explode( '_', $property );
339
		}
340
		foreach ( $property_parts as $property_part_k => $property_part_v ) {
341
			$property_parts[ $property_part_k ] = ucfirst( $property_part_v );
342
		}
343
		return 'Kirki_Field_Property_' . implode( '_', $property_parts );
344
	}
345
346
	/**
347
	 * Escape $kirki_config.
348
	 *
349
	 * @access protected
350
	 */
351
	protected function set_kirki_config() {
352
353
		$this->kirki_config = esc_attr( $this->kirki_config );
354
	}
355
356
	/**
357
	 * Escape $option_name.
358
	 *
359
	 * @access protected
360
	 */
361
	protected function set_option_name() {
362
363
		$this->option_name = esc_attr( $this->option_name );
364
	}
365
366
	/**
367
	 * Escape the $section.
368
	 *
369
	 * @access protected
370
	 */
371
	protected function set_section() {
372
373
		$this->section = sanitize_key( $this->section );
374
	}
375
376
	/**
377
	 * Escape the $section.
378
	 *
379
	 * @access protected
380
	 */
381
	protected function set_input_attrs() {
382
383
		if ( ! is_array( $this->input_attrs ) ) {
384
			$this->input_attrs = array();
385
		}
386
	}
387
388
	/**
389
	 * Checks the capability chosen is valid.
390
	 * If not, then falls back to 'edit_theme_options'
391
	 *
392
	 * @access protected
393
	 */
394
	protected function set_capability() {
395
396
		// Early exit if we're using 'edit_theme_options'.
397
		if ( 'edit_theme_options' === $this->capability ) {
398
			return;
399
		}
400
		// Escape & trim the capability.
401
		$this->capability = trim( esc_attr( $this->capability ) );
402
	}
403
404
	/**
405
	 * Make sure we're using the correct option_type
406
	 *
407
	 * @access protected
408
	 */
409
	protected function set_option_type() {
410
411
		// Take care of common typos.
412
		if ( 'options' === $this->option_type ) {
413
			$this->option_type = 'option';
414
		}
415
		// Take care of common typos.
416
		if ( 'theme_mods' === $this->option_type ) {
417
			/* translators: %1$s represents the field ID where the error occurs. */
418
			_doing_it_wrong( __METHOD__, sprintf( esc_attr__( 'Typo found in field %s - "theme_mods" vs "theme_mod"', 'kirki' ), esc_attr( $this->settings ) ), '3.0.10' );
419
			$this->option_type = 'theme_mod';
420
		}
421
	}
422
423
	/**
424
	 * Modifications for partial refreshes.
425
	 *
426
	 * @access protected
427
	 */
428
	protected function set_partial_refresh() {
429
430
		if ( ! is_array( $this->partial_refresh ) ) {
431
			$this->partial_refresh = array();
432
		}
433
		foreach ( $this->partial_refresh as $id => $args ) {
434
			if ( ! is_array( $args ) || ! isset( $args['selector'] ) || ! isset( $args['render_callback'] ) || ! is_callable( $args['render_callback'] ) ) {
435
				/* translators: %1$s represents the field ID where the error occurs. */
436
				_doing_it_wrong( __METHOD__, sprintf( esc_attr__( '"partial_refresh" invalid entry in field %s', 'kirki' ), esc_attr( $this->settings ) ), '3.0.10' );
437
				unset( $this->partial_refresh[ $id ] );
438
				continue;
439
			}
440
		}
441
		if ( ! empty( $this->partial_refresh ) ) {
442
			$this->transport = 'postMessage';
443
		}
444
	}
445
446
	/**
447
	 * Sets the settings.
448
	 * If we're using serialized options it makes sure that settings are properly formatted.
449
	 * We'll also be escaping all setting names here for consistency.
450
	 *
451
	 * @access protected
452
	 */
453
	protected function set_settings() {
454
455
		// If settings is not an array, temporarily convert it to an array.
456
		// This is just to allow us to process everything the same way and avoid code duplication.
457
		// if settings is not an array then it will not be set as an array in the end.
458
		if ( ! is_array( $this->settings ) ) {
459
			$this->settings = array(
460
				'kirki_placeholder_setting' => $this->settings,
461
			);
462
		}
463
		$settings = array();
464
		foreach ( $this->settings as $setting_key => $setting_value ) {
465
			$settings[ sanitize_key( $setting_key ) ] = esc_attr( $setting_value );
466
			// If we're using serialized options then we need to spice this up.
467
			if ( 'option' === $this->option_type && '' !== $this->option_name && ( false === strpos( $setting_key, '[' ) ) ) {
468
				$settings[ sanitize_key( $setting_key ) ] = esc_attr( $this->option_name ) . '[' . esc_attr( $setting_value ) . ']';
469
			}
470
		}
471
		$this->settings = $settings;
472
		if ( isset( $this->settings['kirki_placeholder_setting'] ) ) {
473
			$this->settings = $this->settings['kirki_placeholder_setting'];
474
		}
475
	}
476
477
	/**
478
	 * Escapes the tooltip messages.
479
	 *
480
	 * @access protected
481
	 */
482
	protected function set_tooltip() {
483
484
		if ( '' !== $this->tooltip ) {
485
			$this->tooltip = wp_strip_all_tags( $this->tooltip );
486
			return;
487
		}
488
	}
489
490
	/**
491
	 * Sets the active_callback
492
	 * If we're using the $required argument,
493
	 * Then this is where the switch is made to our evaluation method.
494
	 *
495
	 * @access protected
496
	 */
497
	protected function set_active_callback() {
498
499
		if ( is_array( $this->active_callback ) && ! is_callable( $this->active_callback ) ) {
500
			if ( isset( $this->active_callback[0] ) ) {
501
				$this->required = $this->active_callback;
502
			}
503
		}
504
505
		if ( ! empty( $this->required ) ) {
506
			$this->active_callback = array( 'Kirki_Active_Callback', 'evaluate' );
507
			return;
508
		}
509
		// No need to proceed any further if we're using the default value.
510
		if ( '__return_true' === $this->active_callback ) {
511
			return;
512
		}
513
		// Make sure the function is callable, otherwise fallback to __return_true.
514
		if ( ! is_callable( $this->active_callback ) ) {
515
			$this->active_callback = '__return_true';
516
		}
517
	}
518
519
	/**
520
	 * Sets the control type.
521
	 *
522
	 * @access protected
523
	 */
524
	protected function set_type() {
525
526
		// Escape the control type (it doesn't hurt to be sure).
527
		$this->type = esc_attr( $this->type );
528
	}
529
530
	/**
531
	 * Sets the $id.
532
	 * Setting the ID should happen after the 'settings' sanitization.
533
	 * This way we can also properly handle cases where the option_type is set to 'option'
534
	 * and we're using an array instead of individual options.
535
	 *
536
	 * @access protected
537
	 */
538
	protected function set_id() {
539
540
		$this->id = sanitize_key( str_replace( '[', '-', str_replace( ']', '', $this->settings ) ) );
541
	}
542
543
	/**
544
	 * Sets the $choices.
545
	 *
546
	 * @access protected
547
	 */
548
	protected function set_choices() {
549
550
		if ( ! is_array( $this->choices ) ) {
551
			$this->choices = array();
552
		}
553
	}
554
555
	/**
556
	 * Escapes the $disable_output.
557
	 *
558
	 * @access protected
559
	 */
560
	protected function set_disable_output() {
561
562
		$this->disable_output = (bool) $this->disable_output;
563
	}
564
565
	/**
566
	 * Sets the $js_vars
567
	 *
568
	 * @access protected
569
	 */
570
	protected function set_js_vars() {
571
572
		if ( ! is_array( $this->js_vars ) ) {
573
			$this->js_vars = array();
574
		}
575
576
		// Check if transport is set to auto.
577
		// If not, then skip the auto-calculations and exit early.
578
		if ( 'auto' !== $this->transport ) {
579
			return;
580
		}
581
582
		// Set transport to refresh initially.
583
		// Serves as a fallback in case we failt to auto-calculate js_vars.
584
		$this->transport = 'refresh';
585
586
		$js_vars = array();
587
588
		// Try to auto-generate js_vars.
589
		// First we need to check if js_vars are empty, and that output is not empty.
590
		if ( empty( $this->js_vars ) && ! empty( $this->output ) ) {
591
592
			// Start going through each item in the $output array.
593
			foreach ( $this->output as $output ) {
594
				$output['function'] = ( isset( $output['function'] ) ) ? $output['function'] : 'style';
595
596
				// If 'element' or 'property' are not defined, skip this.
597
				if ( ! isset( $output['element'] ) || ! isset( $output['property'] ) ) {
598
					continue;
599
				}
600
				if ( is_array( $output['element'] ) ) {
601
					$output['element'] = implode( ',', $output['element'] );
602
				}
603
604
				// If there's a sanitize_callback defined skip this, unless we also have a js_callback defined.
605
				if ( isset( $output['sanitize_callback'] ) && ! empty( $output['sanitize_callback'] ) && ! isset( $output['js_callback'] ) ) {
606
					continue;
607
				}
608
609
				// If we got this far, it's safe to add this.
610
				$js_vars[] = $output;
611
			}
612
613
			// Did we manage to get all the items from 'output'?
614
			// If not, then we're missing something so don't add this.
615
			if ( count( $js_vars ) !== count( $this->output ) ) {
616
				return;
617
			}
618
			$this->js_vars   = $js_vars;
619
			$this->transport = 'postMessage';
620
621
		}
622
	}
623
624
	/**
625
	 * Sets the $variables
626
	 *
627
	 * @access protected
628
	 */
629
	protected function set_variables() {
630
631
		if ( ! is_array( $this->variables ) ) {
632
			$variable = ( is_string( $this->variables ) && ! empty( $this->variables ) ) ? $this->variables : false;
633
			$this->variables = array();
634
			if ( $variable && empty( $this->variables ) ) {
635
				$this->variables[0]['name'] = $variable;
636
			}
637
		}
638
	}
639
640
	/**
641
	 * Sets the $transport
642
	 *
643
	 * @access protected
644
	 */
645
	protected function set_transport() {
646
647
		if ( 'postmessage' === trim( strtolower( $this->transport ) ) ) {
648
			$this->transport = 'postMessage';
649
		}
650
	}
651
652
	/**
653
	 * Sets the $required
654
	 *
655
	 * @access protected
656
	 */
657
	protected function set_required() {
658
659
		if ( ! is_array( $this->required ) ) {
660
			$this->required = array();
661
		}
662
	}
663
664
	/**
665
	 * Sets the $priority
666
	 *
667
	 * @access protected
668
	 */
669
	protected function set_priority() {
670
671
		$this->priority = absint( $this->priority );
672
	}
673
}
674