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 WP_Customize_Setting 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 WP_Customize_Setting, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
19 | class WP_Customize_Setting { |
||
20 | /** |
||
21 | * @access public |
||
22 | * @var WP_Customize_Manager |
||
23 | */ |
||
24 | public $manager; |
||
25 | |||
26 | /** |
||
27 | * Unique string identifier for the setting. |
||
28 | * |
||
29 | * @access public |
||
30 | * @var string |
||
31 | */ |
||
32 | public $id; |
||
33 | |||
34 | /** |
||
35 | * @access public |
||
36 | * @var string |
||
37 | */ |
||
38 | public $type = 'theme_mod'; |
||
39 | |||
40 | /** |
||
41 | * Capability required to edit this setting. |
||
42 | * |
||
43 | * @var string |
||
44 | */ |
||
45 | public $capability = 'edit_theme_options'; |
||
46 | |||
47 | /** |
||
48 | * Feature a theme is required to support to enable this setting. |
||
49 | * |
||
50 | * @access public |
||
51 | * @var string |
||
52 | */ |
||
53 | public $theme_supports = ''; |
||
54 | public $default = ''; |
||
55 | public $transport = 'refresh'; |
||
56 | |||
57 | /** |
||
58 | * Server-side sanitization callback for the setting's value. |
||
59 | * |
||
60 | * @var callback |
||
61 | */ |
||
62 | public $sanitize_callback = ''; |
||
63 | public $sanitize_js_callback = ''; |
||
64 | |||
65 | /** |
||
66 | * Whether or not the setting is initially dirty when created. |
||
67 | * |
||
68 | * This is used to ensure that a setting will be sent from the pane to the |
||
69 | * preview when loading the Customizer. Normally a setting only is synced to |
||
70 | * the preview if it has been changed. This allows the setting to be sent |
||
71 | * from the start. |
||
72 | * |
||
73 | * @since 4.2.0 |
||
74 | * @access public |
||
75 | * @var bool |
||
76 | */ |
||
77 | public $dirty = false; |
||
78 | |||
79 | /** |
||
80 | * @var array |
||
81 | */ |
||
82 | protected $id_data = array(); |
||
83 | |||
84 | /** |
||
85 | * Whether or not preview() was called. |
||
86 | * |
||
87 | * @since 4.4.0 |
||
88 | * @access protected |
||
89 | * @var bool |
||
90 | */ |
||
91 | protected $is_previewed = false; |
||
92 | |||
93 | /** |
||
94 | * Cache of multidimensional values to improve performance. |
||
95 | * |
||
96 | * @since 4.4.0 |
||
97 | * @access protected |
||
98 | * @var array |
||
99 | * @static |
||
100 | */ |
||
101 | protected static $aggregated_multidimensionals = array(); |
||
102 | |||
103 | /** |
||
104 | * Whether the multidimensional setting is aggregated. |
||
105 | * |
||
106 | * @since 4.4.0 |
||
107 | * @access protected |
||
108 | * @var bool |
||
109 | */ |
||
110 | protected $is_multidimensional_aggregated = false; |
||
111 | |||
112 | /** |
||
113 | * Constructor. |
||
114 | * |
||
115 | * Any supplied $args override class property defaults. |
||
116 | * |
||
117 | * @since 3.4.0 |
||
118 | * |
||
119 | * @param WP_Customize_Manager $manager |
||
120 | * @param string $id An specific ID of the setting. Can be a |
||
121 | * theme mod or option name. |
||
122 | * @param array $args Setting arguments. |
||
123 | */ |
||
124 | public function __construct( $manager, $id, $args = array() ) { |
||
162 | |||
163 | /** |
||
164 | * Get parsed ID data for multidimensional setting. |
||
165 | * |
||
166 | * @since 4.4.0 |
||
167 | * @access public |
||
168 | * |
||
169 | * @return array { |
||
170 | * ID data for multidimensional setting. |
||
171 | * |
||
172 | * @type string $base ID base |
||
173 | * @type array $keys Keys for multidimensional array. |
||
174 | * } |
||
175 | */ |
||
176 | final public function id_data() { |
||
179 | |||
180 | /** |
||
181 | * Set up the setting for aggregated multidimensional values. |
||
182 | * |
||
183 | * When a multidimensional setting gets aggregated, all of its preview and update |
||
184 | * calls get combined into one call, greatly improving performance. |
||
185 | * |
||
186 | * @since 4.4.0 |
||
187 | * @access protected |
||
188 | */ |
||
189 | protected function aggregate_multidimensional() { |
||
208 | |||
209 | /** |
||
210 | * The ID for the current site when the preview() method was called. |
||
211 | * |
||
212 | * @since 4.2.0 |
||
213 | * @access protected |
||
214 | * @var int |
||
215 | */ |
||
216 | protected $_previewed_blog_id; |
||
217 | |||
218 | /** |
||
219 | * Return true if the current site is not the same as the previewed site. |
||
220 | * |
||
221 | * @since 4.2.0 |
||
222 | * @access public |
||
223 | * |
||
224 | * @return bool If preview() has been called. |
||
225 | */ |
||
226 | public function is_current_blog_previewed() { |
||
232 | |||
233 | /** |
||
234 | * Original non-previewed value stored by the preview method. |
||
235 | * |
||
236 | * @see WP_Customize_Setting::preview() |
||
237 | * @since 4.1.1 |
||
238 | * @var mixed |
||
239 | */ |
||
240 | protected $_original_value; |
||
241 | |||
242 | /** |
||
243 | * Add filters to supply the setting's value when accessed. |
||
244 | * |
||
245 | * If the setting already has a pre-existing value and there is no incoming |
||
246 | * post value for the setting, then this method will short-circuit since |
||
247 | * there is no change to preview. |
||
248 | * |
||
249 | * @since 3.4.0 |
||
250 | * @since 4.4.0 Added boolean return value. |
||
251 | * @access public |
||
252 | * |
||
253 | * @return bool False when preview short-circuits due no change needing to be previewed. |
||
254 | */ |
||
255 | public function preview() { |
||
356 | |||
357 | /** |
||
358 | * Clear out the previewed-applied flag for a multidimensional-aggregated value whenever its post value is updated. |
||
359 | * |
||
360 | * This ensures that the new value will get sanitized and used the next time |
||
361 | * that `WP_Customize_Setting::_multidimensional_preview_filter()` |
||
362 | * is called for this setting. |
||
363 | * |
||
364 | * @since 4.4.0 |
||
365 | * @access private |
||
366 | * @see WP_Customize_Manager::set_post_value() |
||
367 | * @see WP_Customize_Setting::_multidimensional_preview_filter() |
||
368 | */ |
||
369 | final public function _clear_aggregated_multidimensional_preview_applied_flag() { |
||
372 | |||
373 | /** |
||
374 | * Callback function to filter non-multidimensional theme mods and options. |
||
375 | * |
||
376 | * If switch_to_blog() was called after the preview() method, and the current |
||
377 | * site is now not the same site, then this method does a no-op and returns |
||
378 | * the original value. |
||
379 | * |
||
380 | * @since 3.4.0 |
||
381 | * |
||
382 | * @param mixed $original Old value. |
||
383 | * @return mixed New or old value. |
||
384 | */ |
||
385 | public function _preview_filter( $original ) { |
||
404 | |||
405 | /** |
||
406 | * Callback function to filter multidimensional theme mods and options. |
||
407 | * |
||
408 | * For all multidimensional settings of a given type, the preview filter for |
||
409 | * the first setting previewed will be used to apply the values for the others. |
||
410 | * |
||
411 | * @since 4.4.0 |
||
412 | * @access private |
||
413 | * |
||
414 | * @see WP_Customize_Setting::$aggregated_multidimensionals |
||
415 | * @param mixed $original Original root value. |
||
416 | * @return mixed New or old value. |
||
417 | */ |
||
418 | final public function _multidimensional_preview_filter( $original ) { |
||
448 | |||
449 | /** |
||
450 | * Check user capabilities and theme supports, and then save |
||
451 | * the value of the setting. |
||
452 | * |
||
453 | * @since 3.4.0 |
||
454 | * |
||
455 | * @return false|void False if cap check fails or value isn't set. |
||
456 | */ |
||
457 | final public function save() { |
||
477 | |||
478 | /** |
||
479 | * Fetch and sanitize the $_POST value for the setting. |
||
480 | * |
||
481 | * @since 3.4.0 |
||
482 | * |
||
483 | * @param mixed $default A default value which is used as a fallback. Default is null. |
||
484 | * @return mixed The default value on failure, otherwise the sanitized value. |
||
485 | */ |
||
486 | final public function post_value( $default = null ) { |
||
489 | |||
490 | /** |
||
491 | * Sanitize an input. |
||
492 | * |
||
493 | * @since 3.4.0 |
||
494 | * |
||
495 | * @param string|array $value The value to sanitize. |
||
496 | * @return string|array|null Null if an input isn't valid, otherwise the sanitized value. |
||
497 | */ |
||
498 | public function sanitize( $value ) { |
||
510 | |||
511 | /** |
||
512 | * Get the root value for a setting, especially for multidimensional ones. |
||
513 | * |
||
514 | * @since 4.4.0 |
||
515 | * @access protected |
||
516 | * |
||
517 | * @param mixed $default Value to return if root does not exist. |
||
518 | * @return mixed |
||
519 | */ |
||
520 | protected function get_root_value( $default = null ) { |
||
535 | |||
536 | /** |
||
537 | * Set the root value for a setting, especially for multidimensional ones. |
||
538 | * |
||
539 | * @since 4.4.0 |
||
540 | * @access protected |
||
541 | * |
||
542 | * @param mixed $value Value to set as root of multidimensional setting. |
||
543 | * @return bool Whether the multidimensional root was updated successfully. |
||
544 | */ |
||
545 | protected function set_root_value( $value ) { |
||
565 | |||
566 | /** |
||
567 | * Save the value of the setting, using the related API. |
||
568 | * |
||
569 | * @since 3.4.0 |
||
570 | * |
||
571 | * @param mixed $value The value to update. |
||
572 | * @return bool The result of saving the value. |
||
573 | */ |
||
574 | protected function update( $value ) { |
||
602 | |||
603 | /** |
||
604 | * Deprecated method. |
||
605 | * |
||
606 | * @since 3.4.0 |
||
607 | * @deprecated 4.4.0 Deprecated in favor of update() method. |
||
608 | */ |
||
609 | protected function _update_theme_mod() { |
||
612 | |||
613 | /** |
||
614 | * Deprecated method. |
||
615 | * |
||
616 | * @since 3.4.0 |
||
617 | * @deprecated 4.4.0 Deprecated in favor of update() method. |
||
618 | */ |
||
619 | protected function _update_option() { |
||
622 | |||
623 | /** |
||
624 | * Fetch the value of the setting. |
||
625 | * |
||
626 | * @since 3.4.0 |
||
627 | * |
||
628 | * @return mixed The value. |
||
629 | */ |
||
630 | public function value() { |
||
659 | |||
660 | /** |
||
661 | * Sanitize the setting's value for use in JavaScript. |
||
662 | * |
||
663 | * @since 3.4.0 |
||
664 | * |
||
665 | * @return mixed The requested escaped value. |
||
666 | */ |
||
667 | public function js_value() { |
||
686 | |||
687 | /** |
||
688 | * Validate user capabilities whether the theme supports the setting. |
||
689 | * |
||
690 | * @since 3.4.0 |
||
691 | * |
||
692 | * @return bool False if theme doesn't support the setting or user can't change setting, otherwise true. |
||
693 | */ |
||
694 | View Code Duplication | final public function check_capabilities() { |
|
703 | |||
704 | /** |
||
705 | * Multidimensional helper function. |
||
706 | * |
||
707 | * @since 3.4.0 |
||
708 | * |
||
709 | * @param $root |
||
710 | * @param $keys |
||
711 | * @param bool $create Default is false. |
||
712 | * @return array|void Keys are 'root', 'node', and 'key'. |
||
713 | */ |
||
714 | final protected function multidimensional( &$root, $keys, $create = false ) { |
||
753 | |||
754 | /** |
||
755 | * Will attempt to replace a specific value in a multidimensional array. |
||
756 | * |
||
757 | * @since 3.4.0 |
||
758 | * |
||
759 | * @param $root |
||
760 | * @param $keys |
||
761 | * @param mixed $value The value to update. |
||
762 | * @return mixed |
||
763 | */ |
||
764 | final protected function multidimensional_replace( $root, $keys, $value ) { |
||
777 | |||
778 | /** |
||
779 | * Will attempt to fetch a specific value from a multidimensional array. |
||
780 | * |
||
781 | * @since 3.4.0 |
||
782 | * |
||
783 | * @param $root |
||
784 | * @param $keys |
||
785 | * @param mixed $default A default value which is used as a fallback. Default is null. |
||
786 | * @return mixed The requested value or the default value. |
||
787 | */ |
||
788 | final protected function multidimensional_get( $root, $keys, $default = null ) { |
||
795 | |||
796 | /** |
||
797 | * Will attempt to check if a specific value in a multidimensional array is set. |
||
798 | * |
||
799 | * @since 3.4.0 |
||
800 | * |
||
801 | * @param $root |
||
802 | * @param $keys |
||
803 | * @return bool True if value is set, false if not. |
||
804 | */ |
||
805 | final protected function multidimensional_isset( $root, $keys ) { |
||
809 | } |
||
810 | |||
825 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.