Completed
Push — milestone/2_0/react-ui ( c9a76c...950db8 )
by
unknown
02:58
created

Field   F

Complexity

Total Complexity 100

Size/Duplication

Total Lines 999
Duplicated Lines 1 %

Coupling/Cohesion

Components 5
Dependencies 5

Test Coverage

Coverage 39.18%

Importance

Changes 9
Bugs 2 Features 1
Metric Value
dl 10
loc 999
ccs 114
cts 291
cp 0.3918
rs 1.263
c 9
b 2
f 1
wmc 100
lcom 5
cbo 5

70 Methods

Rating   Name   Duplication   Size   Complexity  
A __clone() 0 3 1
A make() 0 3 1
A activate_field_type() 0 10 2
A field_type_activated() 0 1 1
A get_hierarchy() 0 3 1
A set_hierarchy() 0 3 1
A get_hierarchy_index() 0 3 1
A set_hierarchy_index() 0 4 2
A is_simple_root_field() 0 8 2
A init() 0 1 1
A admin_init() 0 1 1
A admin_enqueue_scripts() 0 1 1
A get_value_from_datastore() 0 9 3
A load() 0 3 1
A save() 0 12 3
A delete() 0 3 1
A set_value_from_input() 0 7 2
A has_default_datastore() 0 3 1
A get_datastore() 0 3 1
A __construct() 0 15 1
A get_type() 0 3 1
A activate() 0 9 1
A set_datastore() 0 8 3
A get_context() 0 3 1
A set_context() 0 4 1
A get_value_set() 0 6 2
A set_value_set() 0 3 1
A get_value() 0 6 2
A get_full_value() 0 6 2
A factory() 4 21 3
A get_formatted_value() 0 3 1
A set_value() 0 3 1
A clear_value() 0 3 1
A get_default_value() 0 3 1
A set_default_value() 0 4 1
A get_base_name() 0 3 1
A set_base_name() 0 3 1
A get_name() 0 3 1
A set_name() 0 18 4
A get_name_prefix() 0 3 1
A set_name_prefix() 0 9 1
A get_label() 0 3 1
A set_label() 0 8 2
A get_attributes() 0 3 1
A get_attribute() 0 3 2
A set_attribute() 0 8 2
A get_help_text() 0 3 1
A set_help_text() 0 4 1
A help_text() 0 3 1
A get_autoload() 0 3 1
A set_autoload() 0 4 1
A get_lazyload() 0 3 1
A set_lazyload() 0 4 1
A get_width() 0 3 1
A set_width() 0 4 1
A get_classes() 0 3 1
A set_classes() 0 4 1
A set_required() 0 4 1
A is_required() 0 3 1
A get_id() 0 3 1
A set_id() 0 3 1
A set_conditional_logic() 0 4 1
A get_conditional_logic() 0 3 1
C parse_conditional_rules() 6 46 9
A set_visible_in_rest_api() 0 3 1
A get_visible_in_rest_api() 0 3 1
A show_in_rest() 0 4 1
B to_json() 0 25 2
A admin_hook_scripts() 0 3 1
A admin_hook_styles() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Field often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Field, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Carbon_Fields\Field;
4
5
use Carbon_Fields\Carbon_Fields;
6
use Carbon_Fields\Pimple\Container as PimpleContainer;
7
use Carbon_Fields\Datastore\Datastore_Interface;
8
use Carbon_Fields\Datastore\Datastore_Holder_Interface;
9
use Carbon_Fields\Value_Set\Value_Set;
10
use Carbon_Fields\Helper\Helper;
11
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
12
13
/**
14
 * Base field class.
15
 * Defines the key container methods and their default implementations.
16
 * Implements factory design pattern.
17
 */
18
class Field implements Datastore_Holder_Interface {
19
	
20
	/**
21
	 * Array of field class names that have had their activation method called
22
	 *
23
	 * @var array<string>
24
	 */
25
	protected static $activated_field_types = array();
26
27
	/**
28
	 * Globally unique field identificator. Generated randomly
29
	 *
30
	 * @var string
31
	 */
32
	protected $id;
33
34
	/**
35
	 * Stores the initial <kbd>$type</kbd> variable passed to the <code>factory()</code> method
36
	 *
37
	 * @see factory
38
	 * @var string
39
	 */
40
	public $type;
41
42
	/**
43
	 * Array of ancestor field names
44
	 *
45
	 * @var array
46
	 */
47
	protected $hierarchy = array();
48
49
	/**
50
	 * Array of complex entry ids
51
	 *
52
	 * @var array
53
	 */
54
	protected $hierarchy_index = array();
55
56
	/**
57
	 * Field value
58
	 *
59
	 * @var Value_Set
60
	 */
61
	protected $value_set;
62
63
	/**
64
	 * Default field value
65
	 *
66
	 * @var mixed
67
	 */
68
	protected $default_value = '';
69
70
	/**
71
	 * Sanitized field name used as input name attribute during field render
72
	 *
73
	 * @see factory()
74
	 * @see set_name()
75
	 * @var string
76
	 */
77
	protected $name;
78
79
	/**
80
	 * Field name prefix
81
	 *
82
	 * @see set_name()
83
	 * @var string
84
	 */
85
	protected $name_prefix = '_';
86
87
	/**
88
	 * The base field name which is used in the container.
89
	 *
90
	 * @see set_base_name()
91
	 * @var string
92
	 */
93
	protected $base_name;
94
95
	/**
96
	 * Field name used as label during field render
97
	 *
98
	 * @see factory()
99
	 * @see set_label()
100
	 * @var string
101
	 */
102
	protected $label;
103
104
	/**
105
	 * Additional text containing information and guidance for the user
106
	 *
107
	 * @see help_text()
108
	 * @var string
109
	 */
110
	protected $help_text;
111
112
	/**
113
	 * Field DataStore instance to which save, load and delete calls are delegated
114
	 *
115
	 * @see set_datastore()
116
	 * @see get_datastore()
117
	 * @var Datastore_Interface
118
	 */
119
	protected $datastore;
120
121
	/**
122
	 * Flag whether the datastore is the default one or replaced with a custom one
123
	 *
124
	 * @see set_datastore()
125
	 * @see get_datastore()
126
	 * @var boolean
127
	 */
128
	protected $has_default_datastore = true;
129
130
	/**
131
	 * The type of the container this field is in
132
	 *
133
	 * @see get_context()
134
	 * @var string
135
	 */
136
	protected $context;
137
138
	/**
139
	 * Whether or not this value should be auto loaded. Applicable to theme options only.
140
	 *
141
	 * @see set_autoload()
142
	 * @var bool
143
	 */
144
	protected $autoload = false;
145
146
	/**
147
	 * Whether or not this field will be initialized when the field is in the viewport (visible).
148
	 *
149
	 * @see set_lazyload()
150
	 * @var bool
151
	 */
152
	protected $lazyload = false;
153
154
	/**
155
	 * Key-value array of attribtues and their values
156
	 * 
157
	 * @var array
158
	 */
159
	protected $attributes = array();
160
161
	/**
162
	 * Array of attributes the user is allowed to change
163
	 * 
164
	 * @var array<string>
165
	 */
166
	protected $allowed_attributes = array( 'max', 'maxlength', 'min', 'pattern', 'placeholder', 'readonly', 'step', 'type' );
167
168
	/**
169
	 * The width of the field.
170
	 *
171
	 * @see set_width()
172
	 * @var int
173
	 */
174
	protected $width = 0;
175
176
	/**
177
	 * Custom CSS classes.
178
	 *
179
	 * @see set_classes()
180
	 * @var array
181
	 */
182
	protected $classes = array();
183
184
	/**
185
	 * Whether or not this field is required.
186
	 *
187
	 * @see set_required()
188
	 * @var bool
189
	 */
190
	protected $required = false;
191
192
	/**
193
	 * Stores the field conditional logic rules.
194
	 *
195
	 * @var array
196
	 */
197
	protected $conditional_logic = array();
198
199
	/**
200
	 * Whether the field should be included in the response of the requests to the REST API
201
	 *
202
	 * @see  set_visible_in_rest_api
203
	 * @see  get_visible_in_rest_api
204
	 * @var boolean
205
	 */
206
	protected $visible_in_rest_api = false;
207
208
	/**
209
	 * Clone the Value_Set object as well
210
	 *
211
	 * @var array
212
	 */
213
	public function __clone() {
214
		$this->set_value_set( clone $this->get_value_set() );
215
	}
216
217
	/**
218
	 * Create a new field of type $raw_type and name $name and label $label.
219
	 *
220
	 * @param string $raw_type
221
	 * @param string $name lower case and underscore-delimited
222
	 * @param string $label (optional) Automatically generated from $name if not present
223
	 * @return Field
224
	 */
225 14
	public static function factory( $raw_type, $name, $label = null ) {
226 14
		$type = Helper::normalize_type( $raw_type );
227
228 14
		if ( Carbon_Fields::has( $type, 'fields' ) ) {
229
			return Carbon_Fields::resolve_with_arguments( $type, array(
230
				'type' => $type,
231
				'name' => $name,
232
				'label' => $label,
233
			), 'fields' );
234
		}
235
236
		// Fallback to class name-based resolution
237 14
		$class = Helper::type_to_class( $type, __NAMESPACE__, '_Field' );
238 14 View Code Duplication
		if ( ! class_exists( $class ) ) {
239 4
			Incorrect_Syntax_Exception::raise( 'Unknown field "' . $raw_type . '".' );
240 1
			$class = __NAMESPACE__ . '\\Broken_Field';
241 1
		}
242
243 11
		$field = new $class( $type, $name, $label );
244 7
		return $field;
245
	}
246
247
	/**
248
	 * An alias of factory().
249
	 *
250
	 * @see Field::factory()
251
	 * @return Field
252
	 */
253 14
	public static function make( $type, $name, $label = null ) {
254 14
		return static::factory( $type, $name, $label );
255
	}
256
257
	/**
258
	 * Create a field from a certain type with the specified label.
259
	 * 
260
	 * @param string $type  Field type
261
	 * @param string $name  Field name
262
	 * @param string $label Field label
263
	 */
264 11
	public function __construct( $type, $name, $label ) {
265 11
		Carbon_Fields::verify_boot();
266
		
267 11
		$this->type = $type;
268 11
		$this->set_base_name( $name );
269 11
		$this->set_name( $name );
270 7
		$this->set_label( $label );
271
272
		// Pick random ID
273 7
		$random_string = md5( mt_rand() . $this->get_name() . $this->get_label() );
274 7
		$random_string = substr( $random_string, 0, 5 ); // 5 chars should be enough
275 7
		$this->id = 'carbon-' . $random_string;
276
277 7
		$this->init();
278 7
	}
279
280
	/**
281
	 * Returns the type of the field based on the class.
282
	 * The class is stripped by the "CarbonFields" prefix.
283
	 * Also the "Field" suffix is removed.
284
	 * Then underscores and backslashes are removed.
285
	 *
286
	 * @return string
287
	 */
288
	public function get_type() {
289
		return Helper::class_to_type( get_class( $this ), '_Field' );
290
	}
291
292
	/**
293
	 * Activate the field once the container is attached.
294
	 */
295
	public function activate() {
296
		$this->admin_init();
297
298
		add_action( 'admin_print_footer_scripts', array( get_class(), 'admin_hook_scripts' ), 5 );
299
		add_action( 'admin_print_footer_scripts', array( get_class(), 'admin_hook_styles' ), 5 );
300
		static::activate_field_type( get_class( $this ) );
301
302
		do_action( 'carbon_fields_field_activated', $this );
303
	}
304
305
	/**
306
	 * Activate a field type
307
	 * 
308
	 * @param string $class_name
309
	 */
310
	public static function activate_field_type( $class_name ) {
311
		if ( in_array( $class_name, static::$activated_field_types ) ) {
312
			return;
313
		}
314
315
		add_action( 'admin_print_footer_scripts', array( $class_name, 'admin_enqueue_scripts' ), 5 );
316
		call_user_func( array( $class_name, 'field_type_activated' ) );
317
318
		static::$activated_field_types[] = $class_name;
319
	}
320
321
	/**
322
	 * Prepare the field type for use
323
	 * Called once per field type when activated
324
	 */
325
	public static function field_type_activated() {}
326
327
	/**
328
	 * Get array of hierarchy field names
329
	 *
330
	 * @return array
331
	 */
332
	public function get_hierarchy() {
333
		return $this->hierarchy;
334
	}
335
336
	/**
337
	 * Set array of hierarchy field names
338
	 */
339
	public function set_hierarchy( $hierarchy ) {
340
		$this->hierarchy = $hierarchy;
341
	}
342
343
	/**
344
	 * Get array of hierarchy indexes
345
	 *
346
	 * @return array
347
	 */
348
	public function get_hierarchy_index() {
349
		return $this->hierarchy_index;
350
	}
351
352
	/**
353
	 * Set array of hierarchy indexes
354
	 */
355
	public function set_hierarchy_index( $hierarchy_index ) {
356
		$hierarchy_index = ( ! empty( $hierarchy_index ) ) ? $hierarchy_index : array();
357
		$this->hierarchy_index = $hierarchy_index;
358
	}
359
360
	/**
361
	 * Return whether the field is a root field and holds a single value
362
	 *
363
	 * @return bool
364
	 */
365 3
	public function is_simple_root_field() {
366 3
		$hierarchy = $this->get_hierarchy();
367
		return (
368 3
			empty( $hierarchy )
369 3
			&&
370 2
			in_array( $this->get_value_set()->get_type(), array( Value_Set::TYPE_SINGLE_VALUE, Value_Set::TYPE_MULTIPLE_PROPERTIES ) )
371 3
		);
372
	}
373
374
	/**
375
	 * Perform instance initialization
376
	 */
377
	public function init() {}
378
379
	/**
380
	 * Instance initialization when in the admin area
381
	 * Called during field boot
382
	 */
383
	public function admin_init() {}
384
385
	/**
386
	 * Enqueue scripts and styles in admin
387
	 * Called once per field type
388
	 */
389
	public static function admin_enqueue_scripts() {}
390
391
	/**
392
	 * Get value from datastore
393
	 *
394
	 * @param bool $fallback_to_default
395
	 * @return mixed
396
	 */
397
	protected function get_value_from_datastore( $fallback_to_default = true ) {
398
		$value = $this->get_datastore()->load( $this );
399
400
		if ( $value === null && $fallback_to_default ) {
401
			$value = $this->get_default_value();
402
		}
403
404
		return $value;
405
	}
406
407
	/**
408
	 * Load value from datastore
409
	 */
410 2
	public function load() {
411 2
		$this->set_value( $this->get_value_from_datastore() );
412 2
	}
413
414
	/**
415
	 * Save value to storage
416
	 */
417 5
	public function save() {
418 5
		$delete_on_save = ! in_array( $this->get_value_set()->get_type(), array( Value_Set::TYPE_SINGLE_VALUE, Value_Set::TYPE_MULTIPLE_PROPERTIES ) );
419 5
		$delete_on_save = apply_filters( 'carbon_fields_should_delete_field_value_on_save', $delete_on_save, $this );
420 5
		if ( $delete_on_save ) {
421 2
			$this->delete();
422 2
		}
423
424 5
		$save = apply_filters( 'carbon_fields_should_save_field_value', true, $this->get_value(), $this );
425 5
		if ( $save ) {
426 5
			$this->get_datastore()->save( $this );
427 5
		}
428 5
	}
429
430
	/**
431
	 * Delete value from storage
432
	 */
433 1
	public function delete() {
434 1
		$this->get_datastore()->delete( $this );
435 1
	}
436
437
	/**
438
	 * Load the field value from an input array based on it's name
439
	 *
440
	 * @param array $input Array of field names and values.
441
	 */
442 2
	public function set_value_from_input( $input ) {
443 2
		if ( isset( $input[ $this->get_name() ] ) ) {
444 1
			$this->set_value( $input[ $this->get_name() ] );
445 1
		} else {
446 1
			$this->clear_value();
447
		}
448 2
	}
449
450
	/**
451
	 * Return whether the datastore instance is the default one or has been overriden
452
	 *
453
	 * @return boolean
454
	 */
455
	public function has_default_datastore() {
456
		return $this->has_default_datastore;
457
	}
458
459
	/**
460
	 * Get the DataStore instance
461
	 *
462
	 * @return Datastore_Interface $datastore
463
	 */
464 1
	public function get_datastore() {
465 1
		return $this->datastore;
466
	}
467
468
	/**
469
	 * Set datastore instance
470
	 *
471
	 * @param  Datastore_Interface $datastore
472
	 * @param  boolean             $set_as_default
473
	 * @return object              $this
474
	 */
475 1
	public function set_datastore( Datastore_Interface $datastore, $set_as_default = false ) {
476 1
		if ( $set_as_default && ! $this->has_default_datastore() ) {
477
			return $this; // datastore has been overriden with a custom one - abort changing to a default one
478
		}
479 1
		$this->datastore = $datastore;
480 1
		$this->has_default_datastore = $set_as_default;
481 1
		return $this;
482
	}
483
484
	/**
485
	 * Return the type of the container this field is in
486
	 *
487
	 * @return string
488
	 */
489
	public function get_context() {
490
		return $this->context;
491
	}
492
493
	/**
494
	 * Assign the type of the container this field is in
495
	 *
496
	 * @param string
497
	 * @return object $this
498
	 */
499
	public function set_context( $context ) {
500
		$this->context = $context;
501
		return $this;
502
	}
503
504
	/**
505
	 * Get the Value_Set object
506
	 *
507
	 * @return Value_Set
508
	 */
509 2
	public function get_value_set() {
510 2
		if ( $this->value_set === null ) {
511 1
			$this->set_value_set( new Value_Set() );
512 1
		}
513 2
		return $this->value_set;
514
	}
515
516
	/**
517
	 * Set the Value_Set object
518
	 *
519
	 * @param Value_Set $value_set
520
	 */
521 1
	public function set_value_set( $value_set ) {
522 1
		$this->value_set = $value_set;
523 1
	}
524
525
	/**
526
	 * Alias for $this->get_value_set()->get(); with fallback to default value
527
	 *
528
	 * @return mixed
529
	 */
530 3
	public function get_value() {
531 3
		if ( $this->get_value_set()->get() === null ) {
532 1
			$this->set_value( $this->get_default_value() );
533 1
		}
534 3
		return $this->get_value_set()->get();
535
	}
536
537
	/**
538
	 * Alias for $this->get_value_set()->get_set(); with fallback to default value
539
	 *
540
	 * @return array<array>
541
	 */
542
	public function get_full_value() {
543
		if ( $this->get_value_set()->get_set() === null ) {
544
			$this->set_value( $this->get_default_value() );
545
		}
546
		return $this->get_value_set()->get_set();
547
	}
548
549
	/**
550
	 * Return a differently formatted value for end-users
551
	 *
552
	 * @return mixed
553
	 */
554 2
	public function get_formatted_value() {
555 2
		return $this->get_value();
556
	}
557
558
	/**
559
	 * Alias for $this->get_value_set()->set( $value );
560
	 */
561 1
	public function set_value( $value ) {
562 1
		$this->get_value_set()->set( $value );
563 1
	}
564
565
	/**
566
	 * Clear the field value to a blank one (but not the default one)
567
	 */
568
	public function clear_value() {
569
		$this->get_value_set()->set( array() );
570
	}
571
572
	/**
573
	 * Get default field value
574
	 *
575
	 * @return mixed
576
	 */
577 1
	public function get_default_value() {
578 1
		return $this->default_value;
579
	}
580
581
	/**
582
	 * Set default field value
583
	 *
584
	 * @param mixed $default_value
585
	 */
586 1
	public function set_default_value( $default_value ) {
587 1
		$this->default_value = $default_value;
588 1
		return $this;
589
	}
590
591
	/**
592
	 * Return the field base name.
593
	 *
594
	 * @return string
595
	 */
596
	public function get_base_name() {
597
		return $this->base_name;
598
	}
599
600
	/**
601
	 * Set field base name as defined in the container.
602
	 */
603
	public function set_base_name( $name ) {
604
		$this->base_name = $name;
605
	}
606
607
	/**
608
	 * Return the field name
609
	 *
610
	 * @return string
611
	 */
612 2
	public function get_name() {
613 2
		return $this->name;
614
	}
615
616
	/**
617
	 * Set field name.
618
	 * Use only if you are completely aware of what you are doing.
619
	 *
620
	 * @param string $name Field name, either sanitized or not
621
	 */
622 6
	public function set_name( $name ) {
623 6
		if ( empty( $name ) ) {
624 1
			Incorrect_Syntax_Exception::raise( 'Field name can\'t be empty' );
625
			return;
626
		}
627
628
		// symbols ]-[ are supported in a hidden way - required for widgets to work (WP imposes dashes and square brackets on field names)
629 5
		$regex = '/\A[a-z0-9_\-\[\]]+\z/';
630 5
		if ( ! preg_match( $regex, $name ) ) {
631 3
			Incorrect_Syntax_Exception::raise( 'Field name can only contain lowercase alphanumeric characters and underscores ("' . $name . '" passed).' );
632
			return;
633
		}
634
635 2
		$name_prefix = $this->get_name_prefix();
636 2
		$name = ( substr( $name, 0, strlen( $name_prefix ) ) !== $name_prefix ? $name_prefix . $name : $name );
637
638 2
		$this->name = $name;
639 2
	}
640
641
	/**
642
	 * Return the field name prefix
643
	 *
644
	 * @return string
645
	 */
646 3
	public function get_name_prefix() {
647 3
		return $this->name_prefix;
648
	}
649
650
	/**
651
	 * Set field name prefix
652
	 * Use only if you are completely aware of what you are doing.
653
	 *
654
	 * @param string $name_prefix
655
	 */
656 3
	public function set_name_prefix( $name_prefix ) {
657 3
		$name_prefix = strval( $name_prefix );
658 3
		$old_prefix_length = strlen( $this->name_prefix );
659 3
		$this->name_prefix = '';
660 3
		$this->set_name( substr( $this->get_name(), $old_prefix_length ) );
661
662 3
		$this->name_prefix = $name_prefix;
663 3
		$this->set_name( $this->name_prefix . $this->get_name() );
664 3
	}
665
666
	/**
667
	 * Return field label.
668
	 *
669
	 * @return string
670
	 */
671
	public function get_label() {
672
		return $this->label;
673
	}
674
675
	/**
676
	 * Set field label.
677
	 *
678
	 * @param string $label If null, the label will be generated from the field name
679
	 */
680
	public function set_label( $label ) {
681
		if ( is_null( $label ) ) {
682
			// Try to guess field label from it's name
683
			$label = Helper::normalize_label( $this->get_name() );
684
		}
685
686
		$this->label = $label;
687
	}
688
689
	/**
690
	 * Get a key-value array of attributes
691
	 * 
692
	 * @return array
693
	 */
694
	public function get_attributes() {
695
		return $this->attributes;
696
	}
697
698
	/**
699
	 * Get an attribute value
700
	 * 
701
	 * @param  string $name
702
	 * @return string
703
	 */
704
	public function get_attribute( $name ) {
705
		return isset( $this->attributes[ $name ] ) ? $this->attributes[ $name ] : '';
706
	}
707
708
	/**
709
	 * Set an attribute and it's value
710
	 * 
711
	 * @param  string $name
712
	 * @param  string $value
713
	 * @return Field  $this
714
	 */
715
	public function set_attribute( $name, $value = '' ) {
716
		if ( ! in_array( $name, $this->allowed_attributes ) ) {
717
			Incorrect_Syntax_Exception::raise( 'Only the following attributes are allowed: ' . implode( ', ', $this->allowed_attributes ) . '.' );
718
			return $this;
719
		}
720
		$this->attributes[ $name ] = $value;
721
		return $this;
722
	}
723
724
	/**
725
	 * Return the field help text
726
	 *
727
	 * @return object $this
728
	 */
729
	public function get_help_text() {
730
		return $this->help_text;
731
	}
732
733
	/**
734
	 * Set additional text to be displayed during field render,
735
	 * containing information and guidance for the user
736
	 *
737
	 * @return object $this
738
	 */
739
	public function set_help_text( $help_text ) {
740
		$this->help_text = $help_text;
741
		return $this;
742
	}
743
744
	/**
745
	 * Alias for set_help_text()
746
	 *
747
	 * @see set_help_text()
748
	 * @return object $this
749
	 */
750
	public function help_text( $help_text ) {
751
		return $this->set_help_text( $help_text );
752
	}
753
754
	/**
755
	 * Return whether or not this value should be auto loaded.
756
	 *
757
	 * @return bool
758
	 */
759
	public function get_autoload() {
760
		return $this->autoload;
761
	}
762
763
	/**
764
	 * Whether or not this value should be auto loaded. Applicable to theme options only.
765
	 *
766
	 * @param bool $autoload
767
	 * @return object $this
768
	 */
769
	public function set_autoload( $autoload ) {
770
		$this->autoload = $autoload;
771
		return $this;
772
	}
773
774
	/**
775
	 * Return whether or not this field should be lazyloaded.
776
	 *
777
	 * @return bool
778
	 */
779
	public function get_lazyload() {
780
		return $this->lazyload;
781
	}
782
783
	/**
784
	 * Whether or not this field will be initialized when the field is in the viewport (visible).
785
	 *
786
	 * @param bool $lazyload
787
	 * @return object $this
788
	 */
789
	public function set_lazyload( $lazyload ) {
790
		$this->lazyload = $lazyload;
791
		return $this;
792
	}
793
794
	/**
795
	 * Get the field width.
796
	 *
797
	 * @return int $width
798
	 */
799
	public function get_width() {
800
		return $this->width;
801
	}
802
803
	/**
804
	 * Set the field width.
805
	 *
806
	 * @param int $width
807
	 * @return object $this
808
	 */
809
	public function set_width( $width ) {
810
		$this->width = (int) $width;
811
		return $this;
812
	}
813
814
	/**
815
	 * Get custom CSS classes.
816
	 *
817
	 * @return array<string>
818
	 */
819
	public function get_classes() {
820
		return $this->classes;
821
	}
822
823
	/**
824
	 * Set CSS classes that the container should use.
825
	 *
826
	 * @param string|array<string> $classes
827
	 * @return object $this
828
	 */
829
	public function set_classes( $classes ) {
830
		$this->classes = Helper::sanitize_classes( $classes );
831
		return $this;
832
	}
833
834
	/**
835
	 * Whether this field is mandatory for the user
836
	 *
837
	 * @param bool $required
838
	 * @return object $this
839
	 */
840
	public function set_required( $required = true ) {
841
		$this->required = $required;
842
		return $this;
843
	}
844
845
	/**
846
	 * Return whether this field is mandatory for the user
847
	 *
848
	 * @return bool
849
	 */
850
	public function is_required() {
851
		return $this->required;
852
	}
853
854
	/**
855
	 * HTML id attribute getter.
856
	 * @return string
857
	 */
858 1
	public function get_id() {
859 1
		return $this->id;
860
	}
861
862
	/**
863
	 * HTML id attribute setter
864
	 * @param string $id
865
	 */
866 1
	public function set_id( $id ) {
867 1
		$this->id = $id;
868 1
	}
869
870
	/**
871
	 * Set the field visibility conditional logic.
872
	 *
873
	 * @param array
874
	 */
875 8
	public function set_conditional_logic( $rules ) {
876 8
		$this->conditional_logic = $this->parse_conditional_rules( $rules );
877 3
		return $this;
878
	}
879
880
	/**
881
	 * Get the conditional logic rules
882
	 *
883
	 * @return array
884
	 */
885 3
	public function get_conditional_logic() {
886 3
		return $this->conditional_logic;
887
	}
888
889
	/**
890
	 * Validate and parse the conditional logic rules.
891
	 *
892
	 * @param array $rules
893
	 * @return array
894
	 */
895
	protected function parse_conditional_rules( $rules ) {
896
		if ( ! is_array( $rules ) ) {
897
			Incorrect_Syntax_Exception::raise( 'Conditional logic rules argument should be an array.' );
898
			return array();
899
		}
900
901
		$allowed_operators = array( '=', '!=', '>', '>=', '<', '<=', 'IN', 'NOT IN', 'INCLUDES', 'EXCLUDES' );
902
903
		$parsed_rules = array(
904
			'relation' => Helper::get_relation_type_from_array( $rules ),
905
			'rules' => array(),
906
		);
907
908
		foreach ( $rules as $key => $rule ) {
909
			if ( $key === 'relation' ) {
910
				continue; // Skip the relation key as it is already handled above
911
			}
912
913
			// Check if the rule is valid
914
			if ( ! is_array( $rule ) || empty( $rule['field'] ) ) {
915
				Incorrect_Syntax_Exception::raise( 'Invalid conditional logic rule format. The rule should be an array with the "field" key set.' );
916
				return array();
917
			}
918
919
			// Fill in optional keys with defaults
920
			$rule = array_merge( array(
921
				'compare' => '=',
922
				'value' => '',
923
			), $rule );
924
925 View Code Duplication
			if ( ! in_array( $rule['compare'], $allowed_operators ) ) {
926
				Incorrect_Syntax_Exception::raise( 'Invalid conditional logic compare operator: <code>' . $rule['compare'] . '</code><br>Allowed operators are: <code>' .
927
				implode( ', ', $allowed_operators ) . '</code>' );
928
				return array();
929
			}
930
931
			if ( in_array( $rule['compare'], array( 'IN', 'NOT IN' ) ) && ! is_array( $rule['value'] ) ) {
932
				Incorrect_Syntax_Exception::raise( 'Invalid conditional logic value format. An array is expected, when using the "' . $rule['compare'] . '" operator.' );
933
				return array();
934
			}
935
936
			$parsed_rules['rules'][] = $rule;
937
		}
938
939
		return $parsed_rules;
940
	}
941
942
	/**
943
	 * Set the REST visibility of the field
944
	 * 
945
	 * @param bool $visible
946
	 */
947
	public function set_visible_in_rest_api( $visible ) {
948
		$this->visible_in_rest_api = $visible;
949
	}
950
	
951
	/**
952
	 * Get the REST visibility of the field
953
	 * 
954
	 * @return bool
955
	 */
956
	public function get_visible_in_rest_api() {
957
		return $this->visible_in_rest_api;
958
	}
959
960
	/**
961
	 * Configuration function for setting the field visibility in the response of the requests to the REST API
962
	 * 
963
	 * @param bool $visible
964
	 * @return Field $this
965
	 */
966
	public function show_in_rest( $visible = true ) {
967
		$this->set_visible_in_rest_api( $visible );
968
		return $this;
969
	}
970
971
	/**
972
	 * Returns an array that holds the field data, suitable for JSON representation.
973
	 *
974
	 * @param bool $load  Should the value be loaded from the database or use the value from the current instance.
975
	 * @return array
976
	 */
977
	public function to_json( $load ) {
978
		if ( $load ) {
979
			$this->load();
980
		}
981
982
		$field_data = array(
983
			'id' => $this->get_id(),
984
			'type' => $this->get_type(),
985
			'label' => $this->get_label(),
986
			'name' => $this->get_name(),
987
			'base_name' => $this->get_base_name(),
988
			'value' => $this->get_formatted_value(),
989
			'default_value' => $this->get_default_value(),
990
			'attributes' => (object) $this->get_attributes(),
991
			'help_text' => $this->get_help_text(),
992
			'context' => $this->get_context(),
993
			'required' => $this->is_required(),
994
			'lazyload' => $this->get_lazyload(),
995
			'width' => $this->get_width(),
996
			'classes' => $this->get_classes(),
997
			'conditional_logic' => $this->get_conditional_logic(),
998
		);
999
1000
		return $field_data;
1001
	}
1002
1003
	/**
1004
	 * Hook administration scripts.
1005
	 */
1006
	public static function admin_hook_scripts() {
1007
		wp_enqueue_media();
1008
	}
1009
1010
	/**
1011
	 * Hook administration styles.
1012
	 */
1013
	public static function admin_hook_styles() {
1014
		wp_enqueue_style( 'thickbox' );
1015
	}
1016
}
1017