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 Container 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 Container, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | abstract class Container { |
||
14 | /** |
||
15 | * Where to put a particular tab -- at the head or the tail. Tail by default |
||
16 | */ |
||
17 | const TABS_TAIL = 1; |
||
18 | const TABS_HEAD = 2; |
||
19 | |||
20 | /** |
||
21 | * List of registered unique panel identificators |
||
22 | * |
||
23 | * @see verify_unique_panel_id() |
||
24 | * @var array |
||
25 | */ |
||
26 | public static $registered_panel_ids = array(); |
||
27 | |||
28 | /** |
||
29 | * List of registered unique field names |
||
30 | * |
||
31 | * @see verify_unique_field_name() |
||
32 | * @var array |
||
33 | */ |
||
34 | static protected $registered_field_names = array(); |
||
35 | |||
36 | /** |
||
37 | * List of containers created via factory that |
||
38 | * should be initialized |
||
39 | * |
||
40 | * @see verify_unique_field_name() |
||
41 | * @var array |
||
42 | */ |
||
43 | static protected $init_containers = array(); |
||
44 | |||
45 | /** |
||
46 | * List of containers attached to the current page view |
||
47 | * |
||
48 | * @see _attach() |
||
49 | * @var array |
||
50 | */ |
||
51 | public static $active_containers = array(); |
||
52 | |||
53 | /** |
||
54 | * List of fields attached to the current page view |
||
55 | * |
||
56 | * @see _attach() |
||
57 | * @var array |
||
58 | */ |
||
59 | static protected $active_fields = array(); |
||
60 | |||
61 | /** |
||
62 | * Stores all the container Backbone templates |
||
63 | * |
||
64 | * @see factory() |
||
65 | * @see add_template() |
||
66 | * @var array |
||
67 | */ |
||
68 | protected $templates = array(); |
||
69 | |||
70 | /** |
||
71 | * Tabs available |
||
72 | */ |
||
73 | protected $tabs = array(); |
||
74 | |||
75 | /** |
||
76 | * List of default container settings |
||
77 | * |
||
78 | * @see init() |
||
79 | * @var array |
||
80 | */ |
||
81 | public $settings = array(); |
||
82 | |||
83 | /** |
||
84 | * Title of the container |
||
85 | * |
||
86 | * @var string |
||
87 | */ |
||
88 | public $title = ''; |
||
89 | |||
90 | /** |
||
91 | * Whether the container was setup |
||
92 | * |
||
93 | * @var bool |
||
94 | */ |
||
95 | public $setup_ready = false; |
||
96 | |||
97 | /** |
||
98 | * List of notification messages to be displayed on the front-end |
||
99 | * |
||
100 | * @var array |
||
101 | */ |
||
102 | protected $notifications = array(); |
||
103 | |||
104 | /** |
||
105 | * List of error messages to be displayed on the front-end |
||
106 | * |
||
107 | * @var array |
||
108 | */ |
||
109 | protected $errors = array(); |
||
110 | |||
111 | /** |
||
112 | * List of container fields |
||
113 | * |
||
114 | * @see add_fields() |
||
115 | * @var array |
||
116 | */ |
||
117 | protected $fields = array(); |
||
118 | |||
119 | /** |
||
120 | * Container DataStore. Propagated to all container fields |
||
121 | * |
||
122 | * @see set_datastore() |
||
123 | * @see get_datastore() |
||
124 | * @var object |
||
125 | */ |
||
126 | protected $store; |
||
127 | |||
128 | /** |
||
129 | * Create a new container of type $type and name $name and label $label. |
||
130 | * |
||
131 | * @param string $type |
||
132 | * @param string $name Human-readable name of the container |
||
133 | * @return object $container |
||
134 | **/ |
||
135 | 11 | public static function factory( $type, $name ) { |
|
136 | // backward compatibility: post_meta container used to be called custom_fields |
||
137 | 11 | if ( $type === 'custom_fields' ) { |
|
138 | 1 | $type = 'post_meta'; |
|
139 | 1 | } |
|
140 | |||
141 | 11 | $type = str_replace( ' ', '_', ucwords( str_replace( '_', ' ', $type ) ) ); |
|
142 | |||
143 | 11 | $class = __NAMESPACE__ . '\\' . $type . '_Container'; |
|
144 | |||
145 | 11 | View Code Duplication | if ( ! class_exists( $class ) ) { |
1 ignored issue
–
show
|
|||
146 | 3 | Incorrect_Syntax_Exception::raise( 'Unknown container "' . $type . '".' ); |
|
147 | 1 | $class = __NAMESPACE__ . '\\Broken_Container'; |
|
148 | 1 | } |
|
149 | |||
150 | 9 | $container = new $class( $name ); |
|
151 | 8 | $container->type = $type; |
|
152 | 8 | $container->add_template( $type, array( $container, 'template' ) ); |
|
153 | |||
154 | 8 | self::$init_containers[] = $container; |
|
155 | |||
156 | 8 | return $container; |
|
157 | } |
||
158 | |||
159 | /** |
||
160 | * An alias of factory(). |
||
161 | * |
||
162 | * @see Container::factory() |
||
163 | **/ |
||
164 | 11 | public static function make( $type, $name ) { |
|
165 | 11 | return self::factory( $type, $name ); |
|
166 | } |
||
167 | |||
168 | /** |
||
169 | * Initialize containers created via factory |
||
170 | * |
||
171 | * @return object |
||
172 | **/ |
||
173 | public static function init_containers() { |
||
180 | |||
181 | /** |
||
182 | * Returns all the active containers created via factory |
||
183 | * |
||
184 | * @return array |
||
185 | **/ |
||
186 | public static function get_active_containers() { |
||
189 | |||
190 | /** |
||
191 | * Adds a container to the active containers array and triggers an action |
||
192 | **/ |
||
193 | public static function add_active_container( $container ) { |
||
198 | |||
199 | /** |
||
200 | * Returns all the active fields created via factory |
||
201 | * |
||
202 | * @return array |
||
203 | **/ |
||
204 | public static function get_active_fields() { |
||
207 | |||
208 | /** |
||
209 | * Adds a field to the active fields array and triggers an action |
||
210 | **/ |
||
211 | public static function add_active_field( $field ) { |
||
224 | |||
225 | /** |
||
226 | * Perform instance initialization after calling setup() |
||
227 | **/ |
||
228 | abstract public function init(); |
||
229 | |||
230 | /** |
||
231 | * Prints the container Underscore template |
||
232 | **/ |
||
233 | public function template() { |
||
260 | |||
261 | /** |
||
262 | * Create a new container |
||
263 | * |
||
264 | * @param string $title Unique title of the container |
||
265 | **/ |
||
266 | 9 | public function __construct( $title ) { |
|
278 | |||
279 | /** |
||
280 | * Load the admin scripts and styles. |
||
281 | **/ |
||
282 | public function load_scripts_styles() { |
||
286 | |||
287 | /** |
||
288 | * Update container settings and begin initialization |
||
289 | * |
||
290 | * @see init() |
||
291 | * @param array $settings |
||
292 | **/ |
||
293 | public function setup( $settings = array() ) { |
||
312 | |||
313 | /** |
||
314 | * Check if all required container settings have been specified |
||
315 | * |
||
316 | * @param array $settings Container settings |
||
317 | **/ |
||
318 | public function check_setup_settings( &$settings = array() ) { |
||
324 | |||
325 | /** |
||
326 | * Called first as part of the container save procedure. |
||
327 | * Responsible for checking the request validity and |
||
328 | * calling the container-specific save() method |
||
329 | * |
||
330 | * @see save() |
||
331 | * @see is_valid_save() |
||
332 | **/ |
||
333 | public function _save() { |
||
339 | |||
340 | /** |
||
341 | * Load submitted data and save each field in the container |
||
342 | * |
||
343 | * @see is_valid_save() |
||
344 | **/ |
||
345 | public function save( $user_data ) { |
||
351 | |||
352 | /** |
||
353 | * Checks whether the current request is valid |
||
354 | * |
||
355 | * @return bool |
||
356 | **/ |
||
357 | public function is_valid_save() { |
||
360 | |||
361 | /** |
||
362 | * Load the value for each field in the container. |
||
363 | * Could be used internally during container rendering |
||
364 | **/ |
||
365 | public function load() { |
||
370 | |||
371 | |||
372 | /** |
||
373 | * Called first as part of the container attachment procedure. |
||
374 | * Responsible for checking it's OK to attach the container |
||
375 | * and if it is, calling the container-specific attach() method |
||
376 | * |
||
377 | * @see attach() |
||
378 | * @see is_valid_attach() |
||
379 | **/ |
||
380 | public function _attach() { |
||
395 | |||
396 | /** |
||
397 | * Returns all the Backbone templates |
||
398 | * |
||
399 | * @return array |
||
400 | **/ |
||
401 | public function get_templates() { |
||
404 | |||
405 | /** |
||
406 | * Adds a new Backbone template |
||
407 | **/ |
||
408 | public function add_template( $name, $callback ) { |
||
411 | |||
412 | /** |
||
413 | * Attach the container rendering and helping methods |
||
414 | * to concrete WordPress Action hooks |
||
415 | **/ |
||
416 | public function attach() {} |
||
417 | |||
418 | /** |
||
419 | * Perform checks whether the container is active for current request |
||
420 | * |
||
421 | * @return bool True if the container is active |
||
422 | **/ |
||
423 | public function is_active() { |
||
426 | |||
427 | /** |
||
428 | * Perform checks whether the container should be attached during the current request |
||
429 | * |
||
430 | * @return bool True if the container is allowed to be attached |
||
431 | **/ |
||
432 | public function is_valid_attach() { |
||
435 | |||
436 | /** |
||
437 | * Revert the result of attach() |
||
438 | **/ |
||
439 | public function detach() { |
||
447 | |||
448 | /** |
||
449 | * Append array of fields to the current fields set. All items of the array |
||
450 | * must be instances of Field and their names should be unique for all |
||
451 | * Carbon containers. |
||
452 | * If a field does not have DataStore already, the container data store is |
||
453 | * assigned to them instead. |
||
454 | * |
||
455 | * @param array $fields |
||
456 | **/ |
||
457 | public function add_fields( $fields ) { |
||
475 | |||
476 | /** |
||
477 | * Configuration function for adding tab with fields |
||
478 | */ |
||
479 | public function add_tab( $tab_name, $fields ) { |
||
487 | |||
488 | /** |
||
489 | * Internal function that creates the tab and associates it with particular field set |
||
490 | */ |
||
491 | private function create_tab( $tab_name, $fields, $queue_end = self::TABS_TAIL ) { |
||
512 | |||
513 | /** |
||
514 | * Whether the container is tabbed or not |
||
515 | */ |
||
516 | public function is_tabbed() { |
||
519 | |||
520 | /** |
||
521 | * Retrieve all fields that are not defined under a specific tab |
||
522 | */ |
||
523 | public function get_untabbed_fields() { |
||
545 | |||
546 | /** |
||
547 | * Retrieve all tabs. |
||
548 | * Create a default tab if there are any untabbed fields. |
||
549 | */ |
||
550 | public function get_tabs() { |
||
559 | |||
560 | /** |
||
561 | * Build the tabs JSON |
||
562 | */ |
||
563 | public function get_tabs_json() { |
||
575 | |||
576 | /** |
||
577 | * Returns the private container array of fields. |
||
578 | * Use only if you are completely aware of what you are doing. |
||
579 | * |
||
580 | * @return array |
||
581 | **/ |
||
582 | public function get_fields() { |
||
585 | |||
586 | /** |
||
587 | * Perform a check whether the current container has fields |
||
588 | * |
||
589 | * @return bool |
||
590 | **/ |
||
591 | public function has_fields() { |
||
594 | |||
595 | /** |
||
596 | * Perform checks whether there is a container registered with identificator $id |
||
597 | */ |
||
598 | public static function verify_unique_panel_id( $id ) { |
||
605 | |||
606 | |||
607 | /** |
||
608 | * Remove container identificator $id from the list of unique container ids |
||
609 | * |
||
610 | * @param string $id |
||
611 | **/ |
||
612 | public static function drop_unique_panel_id( $id ) { |
||
617 | |||
618 | /** |
||
619 | * Perform checks whether there is a field registered with the name $name. |
||
620 | * If not, the field name is recorded. |
||
621 | * |
||
622 | * @param string $name |
||
623 | **/ |
||
624 | public function verify_unique_field_name( $name ) { |
||
631 | |||
632 | /** |
||
633 | * Remove field name $name from the list of unique field names |
||
634 | * |
||
635 | * @param string $name |
||
636 | **/ |
||
637 | public function drop_unique_field_name( $name ) { |
||
643 | |||
644 | /** |
||
645 | * Assign DataStore instance for use by the container fields |
||
646 | * |
||
647 | * @param object $store |
||
648 | **/ |
||
649 | public function set_datastore( $store ) { |
||
656 | |||
657 | /** |
||
658 | * Return the DataStore instance used by container fields |
||
659 | * |
||
660 | * @return object $store |
||
661 | **/ |
||
662 | public function get_datastore() { |
||
665 | |||
666 | /** |
||
667 | * Return WordPress nonce name used to identify the current container instance |
||
668 | * |
||
669 | * @return string |
||
670 | **/ |
||
671 | public function get_nonce_name() { |
||
674 | |||
675 | /** |
||
676 | * Return WordPress nonce field |
||
677 | * |
||
678 | * @return string |
||
679 | **/ |
||
680 | public function get_nonce_field() { |
||
683 | |||
684 | /** |
||
685 | * Returns an array that holds the container data, suitable for JSON representation. |
||
686 | * This data will be available in the Underscore template and the Backbone Model. |
||
687 | * |
||
688 | * @param bool $load Should the value be loaded from the database or use the value from the current instance. |
||
689 | * @return array |
||
690 | */ |
||
691 | public function to_json( $load ) { |
||
708 | |||
709 | /** |
||
710 | * Underscore template for tabs |
||
711 | */ |
||
712 | public function template_tabs() { |
||
731 | |||
732 | /** |
||
733 | * Enqueue admin scripts |
||
734 | */ |
||
735 | public function admin_hook_scripts() { |
||
745 | |||
746 | /** |
||
747 | * Enqueue admin styles |
||
748 | */ |
||
749 | public function admin_hook_styles() { |
||
752 | } // END Container |
||
753 | |||
754 |
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.