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