Completed
Push — master ( 3e2977...670c70 )
by
unknown
02:48 queued 11s
created

Field::get_default_value()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
crap 1
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
	 * Key-value array of attribtues and their values
148
	 *
149
	 * @var array
150
	 */
151
	protected $attributes = array();
152
153
	/**
154
	 * Array of attributes the user is allowed to change
155
	 *
156
	 * @var array<string>
157
	 */
158
	protected $allowed_attributes = array();
159
160
	/**
161
	 * The width of the field.
162
	 *
163
	 * @see set_width()
164
	 * @var int
165
	 */
166
	protected $width = 0;
167
168
	/**
169
	 * Custom CSS classes.
170
	 *
171
	 * @see set_classes()
172
	 * @var array
173
	 */
174
	protected $classes = array();
175
176
	/**
177
	 * Whether or not this field is required.
178
	 *
179
	 * @see set_required()
180
	 * @var bool
181
	 */
182
	protected $required = false;
183
184
	/**
185
	 * Stores the field conditional logic rules.
186
	 *
187
	 * @var array
188
	 */
189
	protected $conditional_logic = array();
190
191
	/**
192
	 * Whether the field should be included in the response of the requests to the REST API
193
	 *
194
	 * @see  set_visible_in_rest_api
195
	 * @see  get_visible_in_rest_api
196
	 * @var boolean
197
	 */
198
	protected $visible_in_rest_api = false;
199
200
	/**
201
	 * Clone the Value_Set object as well
202
	 *
203
	 * @var array
204
	 */
205
	public function __clone() {
206
		$this->set_value_set( clone $this->get_value_set() );
207
	}
208
209
	/**
210
	 * Create a new field of type $raw_type and name $name and label $label.
211
	 *
212
	 * @param string $raw_type
213
	 * @param string $name lower case and underscore-delimited
214
	 * @param string $label (optional) Automatically generated from $name if not present
215
	 * @return Field
216
	 */
217 15
	public static function factory( $raw_type, $name, $label = null ) {
218 15
		$type = Helper::normalize_type( $raw_type );
219
220
		// stop hidden symbol support when the end user is creating fields ][
221
		// @see Field::set_name()
222 15
		if ( ! Helper::is_valid_entity_id( $name ) ) {
223 5
			Incorrect_Syntax_Exception::raise( 'Field names can only contain lowercase alphanumeric characters, dashes and underscores ("' . $name . '" passed).' );
224
			return null;
225
		}
226
227 10
		if ( Carbon_Fields::has( $type, 'fields' ) ) {
228
			return Carbon_Fields::resolve_with_arguments( $type, array(
229
				'type' => $type,
230
				'name' => $name,
231
				'label' => $label,
232
			), 'fields' );
233
		}
234
235
		// Fallback to class name-based resolution
236 10
		$class = Helper::type_to_class( $type, __NAMESPACE__, '_Field' );
237 10 View Code Duplication
		if ( ! class_exists( $class ) ) {
238 4
			Incorrect_Syntax_Exception::raise( 'Unknown field type "' . $raw_type . '".' );
239 1
			$class = __NAMESPACE__ . '\\Broken_Field';
240
		}
241
242 7
		$field = new $class( $type, $name, $label );
243 7
		return $field;
244
	}
245
246
	/**
247
	 * An alias of factory().
248
	 *
249
	 * @see    Field::factory()
250
	 * @return Field
251
	 */
252 15
	public static function make() {
253 15
		return call_user_func_array( array( get_class(), 'factory' ), func_get_args() );
254
	}
255
256
	/**
257
	 * Create a field from a certain type with the specified label.
258
	 *
259
	 * @param string $type  Field type
260
	 * @param string $name  Field name
261
	 * @param string $label Field label
262
	 */
263 7
	public function __construct( $type, $name, $label ) {
264 7
		Carbon_Fields::verify_boot();
265
266 7
		$this->type = $type;
267 7
		$this->set_base_name( $name );
268 7
		$this->set_name( $name );
269 7
		$this->set_label( $label );
270
271
		// Pick random ID
272 7
		$random_string = md5( mt_rand() . $this->get_name() . $this->get_label() );
273 7
		$random_string = substr( $random_string, 0, 5 ); // 5 chars should be enough
274 7
		$this->id = 'carbon-' . $random_string;
275
276 7
		$this->init();
277 7
	}
278
279
	/**
280
	 * Returns the type of the field based on the class.
281
	 * The class is stripped by the "CarbonFields" prefix.
282
	 * Also the "Field" suffix is removed.
283
	 * Then underscores and backslashes are removed.
284
	 *
285
	 * @return string
286
	 */
287
	public function get_type() {
288
		return Helper::class_to_type( get_class( $this ), '_Field' );
289
	}
290
291
	/**
292
	 * Activate the field once the container is attached.
293
	 */
294
	public function activate() {
295
		$this->admin_init();
296
297
		add_action( 'admin_print_footer_scripts', array( get_class(), 'admin_hook_scripts' ), 5 );
298
		add_action( 'admin_print_footer_scripts', array( get_class(), 'admin_hook_styles' ), 5 );
299
		static::activate_field_type( get_class( $this ) );
300
301
		do_action( 'carbon_fields_field_activated', $this );
302
	}
303
304
	/**
305
	 * Activate a field type
306
	 *
307
	 * @param string $class_name
308
	 */
309
	public static function activate_field_type( $class_name ) {
310
		if ( in_array( $class_name, static::$activated_field_types ) ) {
311
			return;
312
		}
313
314
		add_action( 'admin_print_footer_scripts', array( $class_name, 'admin_enqueue_scripts' ), 5 );
315
		call_user_func( array( $class_name, 'field_type_activated' ) );
316
317
		static::$activated_field_types[] = $class_name;
318
	}
319
320
	/**
321
	 * Prepare the field type for use
322
	 * Called once per field type when activated
323
	 */
324
	public static function field_type_activated() {}
325
326
	/**
327
	 * Get array of hierarchy field names
328
	 *
329
	 * @return array
330
	 */
331
	public function get_hierarchy() {
332
		return $this->hierarchy;
333
	}
334
335
	/**
336
	 * Set array of hierarchy field names
337
	 *
338
	 * @return self  $this
339
	 */
340
	public function set_hierarchy( $hierarchy ) {
341
		$this->hierarchy = $hierarchy;
342
		return $this;
343
	}
344
345
	/**
346
	 * Get array of hierarchy indexes
347
	 *
348
	 * @return array
349
	 */
350
	public function get_hierarchy_index() {
351
		return $this->hierarchy_index;
352
	}
353
354
	/**
355
	 * Set array of hierarchy indexes
356
	 *
357
	 * @return self  $this
358
	 */
359
	public function set_hierarchy_index( $hierarchy_index ) {
360
		$hierarchy_index = ( ! empty( $hierarchy_index ) ) ? $hierarchy_index : array();
361
		$this->hierarchy_index = $hierarchy_index;
362
		return $this;
363
	}
364
365
	/**
366
	 * Return whether the field is a root field and holds a single value
367
	 *
368
	 * @return bool
369
	 */
370 3
	public function is_simple_root_field() {
371 3
		$hierarchy = $this->get_hierarchy();
372
		return (
373 3
			empty( $hierarchy )
374
			&&
375 3
			in_array( $this->get_value_set()->get_type(), array( Value_Set::TYPE_SINGLE_VALUE, Value_Set::TYPE_MULTIPLE_PROPERTIES ) )
376
		);
377
	}
378
379
	/**
380
	 * Perform instance initialization
381
	 */
382
	public function init() {}
383
384
	/**
385
	 * Instance initialization when in the admin area
386
	 * Called during field boot
387
	 */
388
	public function admin_init() {}
389
390
	/**
391
	 * Enqueue scripts and styles in admin
392
	 * Called once per field type
393
	 */
394
	public static function admin_enqueue_scripts() {}
395
396
	/**
397
	 * Get value from datastore
398
	 *
399
	 * @param bool $fallback_to_default
400
	 * @return mixed
401
	 */
402
	protected function get_value_from_datastore( $fallback_to_default = true ) {
403
		$value = $this->get_datastore()->load( $this );
404
405
		if ( $value === null && $fallback_to_default ) {
406
			$value = $this->get_default_value();
407
		}
408
409
		return $value;
410
	}
411
412
	/**
413
	 * Load value from datastore
414
	 */
415 2
	public function load() {
416 2
		$this->set_value( $this->get_value_from_datastore() );
417 2
	}
418
419
	/**
420
	 * Save value to storage
421
	 */
422 1
	public function save() {
423 1
		$delete_on_save = apply_filters( 'carbon_fields_should_delete_field_value_on_save', true, $this );
424 1
		if ( $delete_on_save ) {
425 1
			$this->delete();
426
		}
427
428 1
		$save = apply_filters( 'carbon_fields_should_save_field_value', true, $this->get_value(), $this );
429 1
		if ( $save ) {
430 1
			$this->get_datastore()->save( apply_filters( 'carbon_fields_before_field_save', $this ) );
431
		}
432 1
	}
433
434
	/**
435
	 * Delete value from storage
436
	 */
437 1
	public function delete() {
438 1
		$this->get_datastore()->delete( apply_filters( 'carbon_fields_before_field_delete', $this ) );
439 1
	}
440
441
	/**
442
	 * Load the field value from an input array based on its name
443
	 *
444
	 * @param  array $input Array of field names and values.
445
	 * @return self  $this
446
	 */
447 2
	public function set_value_from_input( $input ) {
448 2
		if ( isset( $input[ $this->get_name() ] ) ) {
449 1
			$this->set_value( $input[ $this->get_name() ] );
450
		} else {
451 1
			$this->clear_value();
452
		}
453 2
		return $this;
454
	}
455
456
	/**
457
	 * Return whether the datastore instance is the default one or has been overriden
458
	 *
459
	 * @return boolean
460
	 */
461
	public function has_default_datastore() {
462
		return $this->has_default_datastore;
463
	}
464
465
	/**
466
	 * Get the DataStore instance
467
	 *
468
	 * @return Datastore_Interface $datastore
469
	 */
470 1
	public function get_datastore() {
471 1
		return $this->datastore;
472
	}
473
474
	/**
475
	 * Set datastore instance
476
	 *
477
	 * @param  Datastore_Interface $datastore
478
	 * @param  boolean             $set_as_default
479
	 * @return self                $this
480
	 */
481 1
	public function set_datastore( Datastore_Interface $datastore, $set_as_default = false ) {
482 1
		if ( $set_as_default && ! $this->has_default_datastore() ) {
483
			return $this; // datastore has been overriden with a custom one - abort changing to a default one
484
		}
485 1
		$this->datastore = $datastore;
486 1
		$this->has_default_datastore = $set_as_default;
487 1
		return $this;
488
	}
489
490
	/**
491
	 * Return the type of the container this field is in
492
	 *
493
	 * @return string
494
	 */
495
	public function get_context() {
496
		return $this->context;
497
	}
498
499
	/**
500
	 * Assign the type of the container this field is in
501
	 *
502
	 * @param  string $context
503
	 * @return self   $this
504
	 */
505
	public function set_context( $context ) {
506
		$this->context = $context;
507
		return $this;
508
	}
509
510
	/**
511
	 * Get the Value_Set object
512
	 *
513
	 * @return Value_Set
514
	 */
515 2
	public function get_value_set() {
516 2
		if ( $this->value_set === null ) {
517 1
			$this->set_value_set( new Value_Set() );
518
		}
519 2
		return $this->value_set;
520
	}
521
522
	/**
523
	 * Set the Value_Set object
524
	 *
525
	 * @param  Value_Set $value_set
526
	 * @return self      $this
527
	 */
528 1
	public function set_value_set( $value_set ) {
529 1
		$this->value_set = $value_set;
530 1
		return $this;
531
	}
532
533
	/**
534
	 * Alias for $this->get_value_set()->get(); with fallback to default value
535
	 *
536
	 * @return mixed
537
	 */
538 3
	public function get_value() {
539 3
		if ( $this->get_value_set()->get() === null ) {
540 1
			$this->set_value( $this->get_default_value() );
541
		}
542 3
		return $this->get_value_set()->get();
543
	}
544
545
	/**
546
	 * Alias for $this->get_value_set()->get_set(); with fallback to default value
547
	 *
548
	 * @return array<array>
549
	 */
550
	public function get_full_value() {
551
		if ( $this->get_value_set()->get_set() === null ) {
552
			$this->set_value( $this->get_default_value() );
553
		}
554
		return $this->get_value_set()->get_set();
555
	}
556
557
	/**
558
	 * Return a differently formatted value for end-users
559
	 *
560
	 * @return mixed
561
	 */
562 2
	public function get_formatted_value() {
563 2
		return $this->get_value();
564
	}
565
566
	/**
567
	 * Alias for $this->get_value_set()->set( $value );
568
	 */
569 1
	public function set_value( $value ) {
570 1
		$this->get_value_set()->set( $value );
571 1
		return $this;
572
	}
573
574
	/**
575
	 * Clear the field value to a blank one (but not the default one)
576
	 */
577
	public function clear_value() {
578
		$this->get_value_set()->clear();
579
	}
580
581
	/**
582
	 * Get default field value
583
	 *
584
	 * @return mixed
585
	 */
586 1
	public function get_default_value() {
587 1
		return $this->default_value;
588
	}
589
590
	/**
591
	 * Set default field value
592
	 *
593
	 * @param  mixed $default_value
594
	 * @return self  $this
595
	 */
596 1
	public function set_default_value( $default_value ) {
597 1
		$this->default_value = $default_value;
598 1
		return $this;
599
	}
600
601
	/**
602
	 * Return the field base name.
603
	 *
604
	 * @return string
605
	 */
606
	public function get_base_name() {
607
		return $this->base_name;
608
	}
609
610
	/**
611
	 * Set field base name as defined in the container.
612
	 *
613
	 * @return self  $this
614
	 */
615
	public function set_base_name( $name ) {
616
		$this->base_name = $name;
617
		return $this;
618
	}
619
620
	/**
621
	 * Return the field name
622
	 *
623
	 * @return string
624
	 */
625 2
	public function get_name() {
626 2
		return $this->name;
627
	}
628
629
	/**
630
	 * Set field name.
631
	 * Use only if you are completely aware of what you are doing.
632
	 *
633
	 * @param  string $name Field name, either sanitized or not
634
	 * @return self   $this
635
	 */
636 2
	public function set_name( $name ) {
637 2
		if ( empty( $name ) ) {
638
			Incorrect_Syntax_Exception::raise( 'Field name can\'t be empty' );
639
			return $this;
640
		}
641
642
		// symbols ][ are supported in a hidden way - required for widgets to work (WP imposes dashes and square brackets on field names)
643 2
		$field_name_characters = Helper::get_field_name_characters_pattern();
644 2
		$regex = '/\A[' . $field_name_characters . '\[\]]+\z/';
645 2
		if ( ! preg_match( $regex, $name ) ) {
646
			Incorrect_Syntax_Exception::raise( 'Field names  can only contain lowercase alphanumeric characters, dashes and underscores ("' . $name . '" passed).' );
647
			return $this;
648
		}
649
650 2
		$name_prefix = $this->get_name_prefix();
651 2
		$name = ( substr( $name, 0, strlen( $name_prefix ) ) !== $name_prefix ? $name_prefix . $name : $name );
652
653 2
		$this->name = $name;
654 2
		return $this;
655
	}
656
657
	/**
658
	 * Return the field name prefix
659
	 *
660
	 * @return string
661
	 */
662 3
	public function get_name_prefix() {
663 3
		return $this->name_prefix;
664
	}
665
666
	/**
667
	 * Set field name prefix
668
	 * Use only if you are completely aware of what you are doing.
669
	 *
670
	 * @param  string $name_prefix
671
	 * @return self   $this
672
	 */
673 3
	public function set_name_prefix( $name_prefix ) {
674 3
		$name_prefix = strval( $name_prefix );
675 3
		$old_prefix_length = strlen( $this->name_prefix );
676 3
		$this->name_prefix = '';
677 3
		$this->set_name( substr( $this->get_name(), $old_prefix_length ) );
678
679 3
		$this->name_prefix = $name_prefix;
680 3
		$this->set_name( $this->name_prefix . $this->get_name() );
681 3
		return $this;
682
	}
683
684
	/**
685
	 * Return field label.
686
	 *
687
	 * @return string
688
	 */
689
	public function get_label() {
690
		return $this->label;
691
	}
692
693
	/**
694
	 * Set field label.
695
	 *
696
	 * @param  string $label If null, the label will be generated from the field name
697
	 * @return self   $this
698
	 */
699 View Code Duplication
	public function set_label( $label ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
700
		if ( is_null( $label ) ) {
701
			// Try to guess field label from its name
702
			$label = Helper::normalize_label( $this->get_name() );
703
		}
704
705
		$this->label = $label;
706
		return $this;
707
	}
708
709
	/**
710
	 * Get a key-value array of attributes
711
	 *
712
	 * @return array
713
	 */
714
	public function get_attributes() {
715
		return $this->attributes;
716
	}
717
718
	/**
719
	 * Get an attribute value
720
	 *
721
	 * @param  string $name
722
	 * @return string
723
	 */
724
	public function get_attribute( $name ) {
725
		return isset( $this->attributes[ $name ] ) ? $this->attributes[ $name ] : '';
726
	}
727
728
	/**
729
	 * Set an attribute and its value
730
	 *
731
	 * @param  string $name
732
	 * @param  string $value
733
	 * @return self   $this
734
	 */
735
	public function set_attribute( $name, $value = '' ) {
736
		$is_data_attribute = substr( strtolower( $name ), 0, 5 ) === 'data-';
737
		if ( $is_data_attribute ) {
738
			$name = strtolower( $name );
739
			$name = preg_replace( '/[^a-z\-]/', '-', $name );
740
			$name = preg_replace( '/\-{2,}/', '-', $name );
741
			$name = preg_replace( '/^\-+|\-+$/', '', $name );
742
		}
743
744
		if ( ! $is_data_attribute && ! in_array( $name, $this->allowed_attributes ) ) {
745
			Incorrect_Syntax_Exception::raise( 'Only the following attributes are allowed: ' . implode( ', ', array_merge( $this->allowed_attributes, array( 'data-*' ) ) ) );
746
			return $this;
747
		}
748
749
		$this->attributes[ $name ] = $value;
750
		return $this;
751
	}
752
753
	/**
754
	 * Set a key=>value array of attributes
755
	 *
756
	 * @param  array $attributes
757
	 * @return self  $this
758
	 */
759
	public function set_attributes( $attributes ) {
760
		if ( ! is_array( $attributes ) ) {
761
			Incorrect_Syntax_Exception::raise( 'An array must be passed for the $attributes parameter of Field::set_attributes().' );
762
			return $this;
763
		}
764
765
		foreach ( $attributes as $name => $value ) {
766
			$this->set_attribute( $name, $value );
767
		}
768
769
		return $this;
770
	}
771
772
	/**
773
	 * Return the field help text
774
	 *
775
	 * @return object $this
776
	 */
777
	public function get_help_text() {
778
		return $this->help_text;
779
	}
780
781
	/**
782
	 * Set additional text to be displayed during field render,
783
	 * containing information and guidance for the user
784
	 *
785
	 * @return self  $this
786
	 */
787
	public function set_help_text( $help_text ) {
788
		$this->help_text = $help_text;
789
		return $this;
790
	}
791
792
	/**
793
	 * Alias for set_help_text()
794
	 *
795
	 * @see set_help_text()
796
	 * @return object $this
797
	 */
798
	public function help_text( $help_text ) {
799
		return $this->set_help_text( $help_text );
800
	}
801
802
	/**
803
	 * Return whether or not this value should be auto loaded.
804
	 *
805
	 * @return bool
806
	 */
807
	public function get_autoload() {
808
		return $this->autoload;
809
	}
810
811
	/**
812
	 * Whether or not this value should be auto loaded. Applicable to theme options only.
813
	 *
814
	 * @param  bool  $autoload
815
	 * @return self  $this
816
	 */
817
	public function set_autoload( $autoload ) {
818
		$this->autoload = $autoload;
819
		return $this;
820
	}
821
822
	/**
823
	 * Get the field width.
824
	 *
825
	 * @return int $width
826
	 */
827
	public function get_width() {
828
		return $this->width;
829
	}
830
831
	/**
832
	 * Set the field width.
833
	 *
834
	 * @param  int   $width
835
	 * @return self  $this
836
	 */
837
	public function set_width( $width ) {
838
		$this->width = (int) $width;
839
		return $this;
840
	}
841
842
	/**
843
	 * Get custom CSS classes.
844
	 *
845
	 * @return array<string>
846
	 */
847
	public function get_classes() {
848
		return $this->classes;
849
	}
850
851
	/**
852
	 * Set CSS classes that the container should use.
853
	 *
854
	 * @param  string|array<string> $classes
855
	 * @return self                 $this
856
	 */
857
	public function set_classes( $classes ) {
858
		$this->classes = Helper::sanitize_classes( $classes );
859
		return $this;
860
	}
861
862
	/**
863
	 * Whether this field is mandatory for the user
864
	 *
865
	 * @param  bool  $required
866
	 * @return self  $this
867
	 */
868
	public function set_required( $required = true ) {
869
		$this->required = $required;
870
		return $this;
871
	}
872
873
	/**
874
	 * Return whether this field is mandatory for the user
875
	 *
876
	 * @return bool
877
	 */
878
	public function is_required() {
879
		return $this->required;
880
	}
881
882
	/**
883
	 * HTML id attribute getter.
884
	 * @return string
885
	 */
886 1
	public function get_id() {
887 1
		return $this->id;
888
	}
889
890
	/**
891
	 * HTML id attribute setter
892
	 *
893
	 * @param  string $id
894
	 * @return self   $this
895
	 */
896 1
	public function set_id( $id ) {
897 1
		$this->id = $id;
898 1
		return $this;
899
	}
900
901
	/**
902
	 * Set the field visibility conditional logic.
903
	 *
904
	 * @param  array
905
	 * @return self  $this
906
	 */
907 8
	public function set_conditional_logic( $rules ) {
908 8
		$this->conditional_logic = $this->parse_conditional_rules( $rules );
909 3
		return $this;
910
	}
911
912
	/**
913
	 * Get the conditional logic rules
914
	 *
915
	 * @return array
916
	 */
917 3
	public function get_conditional_logic() {
918 3
		return $this->conditional_logic;
919
	}
920
921
	/**
922
	 * Validate and parse a conditional logic rule.
923
	 *
924
	 * @param  array $rule
925
	 * @return array
926
	 */
927
	protected function parse_conditional_rule( $rule ) {
928
		$allowed_operators = array( '=', '!=', '>', '>=', '<', '<=', 'IN', 'NOT IN', 'INCLUDES', 'EXCLUDES' );
929
		$array_operators = array( 'IN', 'NOT IN' );
930
931
		// Check if the rule is valid
932
		if ( ! is_array( $rule ) || empty( $rule['field'] ) ) {
933
			Incorrect_Syntax_Exception::raise( 'Invalid conditional logic rule format. The rule should be an array with the "field" key set.' );
934
			return null;
935
		}
936
937
		// Fill in optional keys with defaults
938
		$rule = array_merge( array(
939
			'compare' => '=',
940
			'value' => '',
941
		), $rule );
942
943 View Code Duplication
		if ( ! in_array( $rule['compare'], $allowed_operators ) ) {
944
			Incorrect_Syntax_Exception::raise( 'Invalid conditional logic compare operator: <code>' . $rule['compare'] . '</code><br>Allowed operators are: <code>' .
945
			implode( ', ', $allowed_operators ) . '</code>' );
946
			return null;
947
		}
948
949 View Code Duplication
		if ( in_array( $rule['compare'], $array_operators ) && ! is_array( $rule['value'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
950
			Incorrect_Syntax_Exception::raise( 'Invalid conditional logic value format. An array is expected, when using the "' . $rule['compare'] . '" operator.' );
951
			return null;
952
		}
953
954
		return $rule;
955
	}
956
957
	/**
958
	 * Validate and parse conditional logic rules.
959
	 *
960
	 * @param  array $rules
961
	 * @return array
962
	 */
963
	protected function parse_conditional_rules( $rules ) {
964
		if ( ! is_array( $rules ) ) {
965
			Incorrect_Syntax_Exception::raise( 'Conditional logic rules argument should be an array.' );
966
			return array();
967
		}
968
969
		$parsed_rules = array(
970
			'relation' => Helper::get_relation_type_from_array( $rules ),
971
			'rules' => array(),
972
		);
973
974
		$rules_only = $rules;
975
		unset( $rules_only['relation'] ); // Skip the relation key as it is already handled above
976
977
		foreach ( $rules_only as $key => $rule ) {
978
			$rule = $this->parse_conditional_rule( $rule );
979
980
			if ( $rule === null ) {
981
				return array();
982
			}
983
984
			$parsed_rules['rules'][] = $rule;
985
		}
986
987
		return $parsed_rules;
988
	}
989
990
	/**
991
	 * Set the REST visibility of the field
992
	 *
993
	 * @param  bool  $visible
994
	 * @return self  $this
995
	 */
996
	public function set_visible_in_rest_api( $visible = true ) {
997
		$this->visible_in_rest_api = $visible;
998
		return $this;
999
	}
1000
1001
	/**
1002
	 * Get the REST visibility of the field
1003
	 *
1004
	 * @return bool
1005
	 */
1006
	public function get_visible_in_rest_api() {
1007
		return $this->visible_in_rest_api;
1008
	}
1009
1010
	/**
1011
	 * Returns an array that holds the field data, suitable for JSON representation.
1012
	 *
1013
	 * @param bool $load  Should the value be loaded from the database or use the value from the current instance.
1014
	 * @return array
1015
	 */
1016
	public function to_json( $load ) {
1017
		if ( $load ) {
1018
			$this->load();
1019
		}
1020
1021
		$field_data = array(
1022
			'id' => $this->get_id(),
1023
			'type' => $this->get_type(),
1024
			'label' => $this->get_label(),
1025
			'name' => $this->get_name(),
1026
			'base_name' => $this->get_base_name(),
1027
			'value' => $this->get_formatted_value(),
1028
			'default_value' => $this->get_default_value(),
1029
			'attributes' => (object) $this->get_attributes(),
1030
			'help_text' => $this->get_help_text(),
1031
			'context' => $this->get_context(),
1032
			'required' => $this->is_required(),
1033
			'width' => $this->get_width(),
1034
			'classes' => $this->get_classes(),
1035
			'conditional_logic' => $this->get_conditional_logic(),
1036
		);
1037
1038
		return $field_data;
1039
	}
1040
1041
	/**
1042
	 * Hook administration scripts.
1043
	 */
1044
	public static function admin_hook_scripts() {
1045
		wp_enqueue_media();
1046
		wp_enqueue_script( 'thickbox' );
1047
		wp_enqueue_script( 'media-upload' );
1048
	}
1049
1050
	/**
1051
	 * Hook administration styles.
1052
	 */
1053
	public static function admin_hook_styles() {
1054
		wp_enqueue_style( 'thickbox' );
1055
	}
1056
}
1057