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 Tabify_Edit_Screen_Edit_Screen 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 Tabify_Edit_Screen_Edit_Screen, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
5 | class Tabify_Edit_Screen_Edit_Screen { |
||
6 | private $tab_location = 'default'; |
||
7 | private $all_metaboxes = array(); |
||
8 | |||
9 | private $editscreen_tabs; |
||
10 | private $settings; |
||
11 | |||
12 | /** |
||
13 | * Set hooks for redirection and showing tabs |
||
14 | * |
||
15 | * @since 0.9.0 |
||
16 | */ |
||
17 | public function __construct() { |
||
18 | add_filter( 'redirect_post_location', array( $this, 'redirect_add_current_tab' ), 10 ); |
||
19 | add_action( 'admin_head', array( $this, 'show_tabs' ), 100 ); |
||
20 | } |
||
21 | |||
22 | /** |
||
23 | * When a post is saved let it return to the current selected tab |
||
24 | * |
||
25 | * @param string $location The location the user will be sent to |
||
26 | * |
||
27 | * @return string $location The new location the user will be sent to |
||
28 | * |
||
29 | * @since 0.2.0 |
||
30 | */ |
||
31 | public function redirect_add_current_tab( $location ) { |
||
32 | if ( isset( $_REQUEST['tab'] ) ) { |
||
33 | $location = esc_url_raw( add_query_arg( 'tab', $_REQUEST['tab'], $location ) ); |
||
34 | } |
||
35 | |||
36 | return $location; |
||
37 | } |
||
38 | |||
39 | /** |
||
40 | * Show the tabs on the edit screens |
||
41 | * This will load the tab class, tab options and actions |
||
42 | * It will also will add the required classes to all the metaboxes |
||
43 | * |
||
44 | * @since 0.1.0 |
||
45 | */ |
||
46 | public function show_tabs() { |
||
47 | $screen = get_current_screen(); |
||
48 | |||
49 | if ( ! $screen || 'post' != $screen->base ) { |
||
50 | return; |
||
51 | } |
||
52 | |||
53 | $this->tab_location = apply_filters( 'tabify_tab_location', $this->tab_location, 'posttype' ); |
||
54 | |||
55 | $post_type = $screen->post_type; |
||
56 | $options = get_option( 'tabify-edit-screen', array() ); |
||
57 | |||
58 | if ( ! isset( $options['posttypes'][ $post_type ] ) ) { |
||
59 | return; |
||
60 | } |
||
61 | |||
62 | // Ability to change if the tabs should be showed or not. |
||
63 | $display_tabs = apply_filters( 'tabify_tab_posttype_show', (bool) $options['posttypes'][ $post_type ]['show'] ); |
||
64 | |||
65 | // Check if this post type is enabled. |
||
66 | if ( ! $display_tabs ) { |
||
67 | return; |
||
68 | } |
||
69 | |||
70 | add_filter( 'admin_body_class', array( $this, 'add_admin_body_class' ) ); |
||
71 | add_action( 'admin_print_footer_scripts', array( $this, 'generate_javascript' ), 9 ); |
||
72 | |||
73 | $default_metaboxes = $this->get_default_items( $post_type ); |
||
74 | $this->all_metaboxes = $this->get_meta_boxes( $post_type ); |
||
75 | |||
76 | // Filter the tabs |
||
77 | $tabs = apply_filters( 'tabify_tab_posttype_tabs', $options['posttypes'][ $post_type ]['tabs'], $post_type ); |
||
78 | |||
79 | // Filter empty tabs |
||
80 | $tabs = array_filter( $tabs, array( $this, 'filter_empty_tabs' ) ); |
||
81 | |||
82 | // Create Tabify_Edit_Screen_Tabs that is for displaying the UI. |
||
83 | $this->editscreen_tabs = new Tabify_Edit_Screen_Tabs( $tabs ); |
||
84 | |||
85 | // Load the tabs on the edit screen. |
||
86 | $this->load_tabs(); |
||
87 | |||
88 | $tab_index = 0; |
||
89 | foreach ( $tabs as $tab_index => $tab ) { |
||
90 | $class = 'tabifybox tabifybox-' . $tab_index; |
||
91 | |||
92 | if ( $this->editscreen_tabs->get_current_tab() != $tab_index ) { |
||
93 | $class .= ' tabifybox-hide'; |
||
94 | } |
||
95 | |||
96 | if ( isset( $tab['items'] ) ) { |
||
97 | foreach ( $tab['items'] as $metabox_id_fallback => $metabox_id ) { |
||
98 | if ( intval( $metabox_id_fallback ) == 0 && $metabox_id_fallback !== 0 ) { |
||
99 | $metabox_id = $metabox_id_fallback; |
||
100 | } |
||
101 | |||
102 | if ( ! in_array( $metabox_id, $default_metaboxes ) ) { |
||
103 | if ( $metabox_id == 'titlediv' || $metabox_id == 'postdivrich' ) { |
||
104 | add_action( 'tabify_custom_javascript', function() use ( $class, $metabox_id ) { |
||
105 | echo 'jQuery(\'#' . $metabox_id . '\').addClass(\'' . $class . '\');'; |
||
106 | } ); |
||
107 | } |
||
108 | else { |
||
109 | add_action( 'postbox_classes_' . $post_type . '_' . $metabox_id, function( $args ) use ( $class ) { |
||
110 | array_push( $args, $class ); |
||
111 | return $args; |
||
112 | } ); |
||
113 | |||
114 | if ( isset( $this->all_metaboxes[ $metabox_id ] ) ) { |
||
115 | unset( $this->all_metaboxes[ $metabox_id ] ); |
||
116 | } |
||
117 | } |
||
118 | } |
||
119 | } |
||
120 | } |
||
121 | } |
||
122 | |||
123 | $this->show_unattached_metaboxes( $tab_index ); |
||
124 | } |
||
125 | |||
126 | /** |
||
127 | * Show unattached metaboxes |
||
128 | * |
||
129 | * @since 1.0.0 |
||
130 | */ |
||
131 | private function show_unattached_metaboxes( $tab_index ) { |
||
161 | |||
162 | |||
163 | /** |
||
164 | * Get meta boxes from a post type |
||
165 | * |
||
166 | * @param string $post_type Post type name |
||
167 | * |
||
168 | * @return array $metaboxes List of metaboxes |
||
169 | * |
||
170 | * @since 1.0.0 |
||
171 | */ |
||
172 | private function get_meta_boxes( $post_type ) { |
||
190 | |||
191 | /** |
||
192 | * Adds tabity location class |
||
193 | * |
||
194 | * @param string $body List of classes |
||
195 | * |
||
196 | * @return string $body List of classes with addition of the tabify locatin class |
||
197 | * |
||
198 | * @since 0.5.0 |
||
199 | */ |
||
200 | public function add_admin_body_class( $body ) { |
||
201 | if ( $this->tab_location ) { |
||
202 | $body .= ' tabify_tab' . $this->tab_location; |
||
203 | } |
||
204 | |||
205 | return $body; |
||
206 | } |
||
207 | |||
208 | /** |
||
209 | * Check where tabs should be loaded and fire the right action and callback for it |
||
210 | * |
||
211 | * @since 0.5.0 |
||
212 | */ |
||
213 | private function load_tabs() { |
||
214 | if ( 'after_title' == $this->tab_location ) { |
||
215 | add_action( 'edit_form_after_title', array( $this, 'output_tabs' ), 9 ); |
||
216 | } |
||
217 | else { //default |
||
218 | $tabs = $this->submit_button(); |
||
219 | $tabs .= $this->editscreen_tabs->get_tabs_with_container(); |
||
220 | |||
221 | add_action( 'tabify_custom_javascript', function() use ( $tabs ) { |
||
222 | echo '$(\'#post\').prepend(\'' . addslashes( $tabs ) . '\');'; |
||
223 | } ); |
||
224 | } |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * Outputs the tabs |
||
229 | * |
||
230 | * @since 0.5.0 |
||
231 | */ |
||
232 | public function output_tabs() { |
||
233 | echo $this->submit_button(); |
||
234 | echo $this->editscreen_tabs->get_tabs_with_container(); |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * Add submit button when the submitbox isn't showed on every tab |
||
239 | * |
||
240 | * @return string $text Return custom submit button |
||
241 | * |
||
242 | * @since 0.7.0 |
||
243 | */ |
||
244 | private function submit_button() { |
||
245 | $post = get_post(); |
||
246 | $default = $this->get_default_items( $post->post_type ); |
||
247 | |||
248 | if ( in_array( 'submitdiv', $default ) ) { |
||
249 | return; |
||
250 | } |
||
251 | |||
252 | $post_type_object = get_post_type_object( $post->post_type ); |
||
253 | $can_publish = current_user_can( $post_type_object->cap->publish_posts ); |
||
254 | |||
255 | if ( ! in_array( $post->post_status, array( 'publish', 'future', 'private' ) ) || 0 == $post->ID ) { |
||
256 | if ( $can_publish ) { |
||
257 | if ( ! empty( $post->post_date_gmt ) && time() < strtotime( $post->post_date_gmt . ' +0000' ) ) { |
||
258 | $text = __( 'Schedule' ); |
||
259 | } |
||
260 | else { |
||
261 | $text = __( 'Publish' ); |
||
262 | } |
||
263 | } |
||
264 | else { |
||
265 | $text = __( 'Submit for Review' ); |
||
266 | } |
||
267 | } |
||
268 | else { |
||
269 | $text = __('Update'); |
||
270 | } |
||
271 | |||
272 | return get_submit_button( $text, 'secondary', 'second-submit', false ); |
||
273 | } |
||
274 | |||
275 | /** |
||
276 | * Generate the javascript for the edit screen |
||
277 | * |
||
278 | * @since 0.1.0 |
||
279 | */ |
||
280 | public function generate_javascript() { |
||
281 | echo '<script type="text/javascript">'; |
||
282 | echo 'jQuery(function($) {'; |
||
283 | do_action( 'tabify_custom_javascript' ); |
||
284 | echo '});'; |
||
285 | echo '</script>'; |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Filter out tabs that don't have any meta boxes to show |
||
290 | * |
||
291 | * @param string $tab Tab information |
||
292 | * |
||
293 | * @since 0.9.6 |
||
294 | */ |
||
295 | public function filter_empty_tabs( $tab ) { |
||
296 | if ( isset( $tab['items'] ) ) { |
||
297 | $tab['items'] = array_intersect( $tab['items'], $this->all_metaboxes ); |
||
298 | |||
299 | return $tab['items']; |
||
300 | } |
||
301 | |||
302 | return false; |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * Get list of items that are always displayed |
||
307 | * |
||
308 | * @param string $post_type The post type |
||
309 | * |
||
310 | * @return array List of default items |
||
311 | * |
||
312 | * @since 0.9.6 |
||
313 | */ |
||
314 | private function get_default_items( $post_type ) { |
||
321 | |||
322 | } |
||
323 |
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.