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 Post_Meta_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 Post_Meta_Container, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
14 | class Post_Meta_Container extends Container { |
||
15 | /** |
||
16 | * ID of the post the container is working with |
||
17 | * |
||
18 | * @see init() |
||
19 | * @var int |
||
20 | */ |
||
21 | protected $post_id; |
||
22 | |||
23 | /** |
||
24 | * Determines whether revisions are disabled for this container |
||
25 | * |
||
26 | * @var bool |
||
27 | */ |
||
28 | protected $revisions_disabled = false; |
||
29 | |||
30 | /** |
||
31 | * List of default container settings |
||
32 | * |
||
33 | * @see init() |
||
34 | * @var array |
||
35 | */ |
||
36 | public $settings = array( |
||
37 | 'meta_box_context' => 'normal', |
||
38 | 'meta_box_priority' => 'high', |
||
39 | ); |
||
40 | |||
41 | /** |
||
42 | * {@inheritDoc} |
||
43 | */ |
||
44 | View Code Duplication | public function __construct( $id, $title, $type, $condition_collection, $condition_translator ) { |
|
|
|||
45 | parent::__construct( $id, $title, $type, $condition_collection, $condition_translator ); |
||
46 | |||
47 | if ( ! $this->get_datastore() ) { |
||
48 | $this->set_datastore( Datastore::make( 'post_meta' ), $this->has_default_datastore() ); |
||
49 | } |
||
50 | } |
||
51 | |||
52 | /** |
||
53 | * Create DataStore instance, set post ID to operate with (if such exists). |
||
54 | * Bind attach() and save() to the appropriate WordPress actions. |
||
55 | */ |
||
56 | public function init() { |
||
57 | $input = stripslashes_deep( $_GET ); |
||
58 | $request_post_id = isset( $input['post'] ) ? intval( $input['post'] ) : 0; |
||
59 | if ( $request_post_id > 0 ) { |
||
60 | $this->set_post_id( $request_post_id ); |
||
61 | } |
||
62 | |||
63 | add_action( 'admin_init', array( $this, '_attach' ) ); |
||
64 | add_action( 'save_post', array( $this, '_save' ) ); |
||
65 | |||
66 | // support for attachments |
||
67 | add_action( 'add_attachment', array( $this, '_save' ) ); |
||
68 | add_action( 'edit_attachment', array( $this, '_save' ) ); |
||
69 | } |
||
70 | |||
71 | /** |
||
72 | * Checks whether the current save request is valid |
||
73 | * Possible errors are triggering save() for autosave requests |
||
74 | * or performing post save outside of the post edit page (like Quick Edit) |
||
75 | * |
||
76 | * @return bool |
||
77 | */ |
||
78 | public function is_valid_save() { |
||
98 | |||
99 | /** |
||
100 | * Perform save operation after successful is_valid_save() check. |
||
101 | * The call is propagated to all fields in the container. |
||
102 | * |
||
103 | * @param int $post_id ID of the post against which save() is ran |
||
104 | */ |
||
105 | View Code Duplication | public function save( $post_id = null ) { |
|
106 | // Unhook action to garantee single save |
||
107 | remove_action( 'save_post', array( $this, '_save' ) ); |
||
108 | |||
109 | $this->set_post_id( $post_id ); |
||
110 | |||
111 | foreach ( $this->fields as $field ) { |
||
112 | $field->set_value_from_input( Helper::input() ); |
||
113 | $field->save(); |
||
114 | } |
||
115 | |||
116 | do_action( 'carbon_fields_post_meta_container_saved', $post_id, $this ); |
||
117 | } |
||
118 | |||
119 | /** |
||
120 | * Get environment array for page request (in admin) |
||
121 | * |
||
122 | * @return array |
||
123 | */ |
||
124 | protected function get_environment_for_request() { |
||
125 | global $pagenow; |
||
126 | |||
127 | $input = stripslashes_deep( $_GET ); |
||
128 | $request_post_type = isset( $input['post_type'] ) ? $input['post_type'] : ''; |
||
129 | $post_type = ''; |
||
130 | |||
131 | if ( $this->post_id ) { |
||
132 | $post_type = get_post_type( $this->post_id ); |
||
133 | } elseif ( ! empty( $request_post_type ) ) { |
||
134 | $post_type = $request_post_type; |
||
135 | } elseif ( $pagenow === 'post-new.php' ) { |
||
136 | $post_type = 'post'; |
||
137 | } |
||
138 | |||
139 | $post = get_post( $this->post_id ); |
||
140 | $post = $post ? $post : null; |
||
141 | $environment = array( |
||
142 | 'post_id' => $post ? $post->ID : 0, |
||
143 | 'post_type' => $post ? $post->post_type : $post_type, |
||
144 | 'post' => $post, |
||
145 | ); |
||
146 | return $environment; |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Perform checks whether the container should be attached during the current request |
||
151 | * |
||
152 | * @return bool True if the container is allowed to be attached |
||
153 | */ |
||
154 | public function is_valid_attach_for_request() { |
||
168 | |||
169 | /** |
||
170 | * Get environment array for object id |
||
171 | * |
||
172 | * @return array |
||
173 | */ |
||
174 | protected function get_environment_for_object( $object_id ) { |
||
190 | |||
191 | /** |
||
192 | * Check container attachment rules against object id |
||
193 | * |
||
194 | * @param int $object_id |
||
195 | * @return bool |
||
196 | */ |
||
197 | View Code Duplication | public function is_valid_attach_for_object( $object_id = null ) { |
|
206 | |||
207 | /** |
||
208 | * Add meta box for each of the container post types |
||
209 | */ |
||
210 | public function attach() { |
||
227 | |||
228 | /** |
||
229 | * Classes to add to the post meta box |
||
230 | */ |
||
231 | public function add_postbox_classes( $classes ) { |
||
235 | |||
236 | /** |
||
237 | * Output the container markup |
||
238 | */ |
||
239 | public function render() { |
||
242 | |||
243 | /** |
||
244 | * Set the post ID the container will operate with. |
||
245 | * |
||
246 | * @param int $post_id |
||
247 | */ |
||
248 | View Code Duplication | public function set_post_id( $post_id ) { |
|
259 | |||
260 | /** |
||
261 | * Get array of post types this container can appear on conditionally |
||
262 | * |
||
263 | * @return array<string> |
||
264 | */ |
||
265 | View Code Duplication | public function get_post_type_visibility() { |
|
280 | |||
281 | /** |
||
282 | * COMMON USAGE METHODS |
||
283 | */ |
||
284 | |||
285 | /** |
||
286 | * Show the container only on particular page referenced by its path. |
||
287 | * |
||
288 | * @deprecated |
||
289 | * @param int|string $page page ID or page path |
||
290 | * @return object $this |
||
291 | */ |
||
292 | public function show_on_page( $page ) { |
||
306 | |||
307 | /** |
||
308 | * Show the container only on pages whose parent is referenced by $parent_page_path. |
||
309 | * |
||
310 | * @deprecated |
||
311 | * @param string $parent_page_path |
||
312 | * @return object $this |
||
313 | */ |
||
314 | public function show_on_page_children( $parent_page_path ) { |
||
320 | |||
321 | /** |
||
322 | * Show the container only on pages whose template has filename $template_path. |
||
323 | * |
||
324 | * @deprecated |
||
325 | * @param string|array $template_path |
||
326 | * @return object $this |
||
327 | */ |
||
328 | public function show_on_template( $template_path ) { |
||
338 | |||
339 | /** |
||
340 | * Hide the container from pages whose template has filename $template_path. |
||
341 | * |
||
342 | * @deprecated |
||
343 | * @param string|array $template_path |
||
344 | * @return object $this |
||
345 | */ |
||
346 | public function hide_on_template( $template_path ) { |
||
351 | |||
352 | /** |
||
353 | * Show the container only on hierarchical posts of level $level. |
||
354 | * Levels start from 1 (top level post) |
||
355 | * |
||
356 | * @deprecated |
||
357 | * @param int $level |
||
358 | * @return object $this |
||
359 | */ |
||
360 | public function show_on_level( $level ) { |
||
364 | |||
365 | /** |
||
366 | * Show the container only on posts from the specified format. |
||
367 | * Learn more about {@link http://codex.wordpress.org/Post_Formats Post Formats (Codex)} |
||
368 | * |
||
369 | * @deprecated |
||
370 | * @param string|array $post_format Name of the format as listed on Codex |
||
371 | * @return object $this |
||
372 | */ |
||
373 | public function show_on_post_format( $post_format ) { |
||
378 | |||
379 | /** |
||
380 | * Show the container only on posts from the specified type(s). |
||
381 | * |
||
382 | * @deprecated |
||
383 | * @param string|array $post_types |
||
384 | * @return object $this |
||
385 | */ |
||
386 | public function show_on_post_type( $post_types ) { |
||
391 | |||
392 | /** |
||
393 | * Show the container only on posts from the specified category. |
||
394 | * |
||
395 | * @see show_on_taxonomy_term() |
||
396 | * |
||
397 | * @deprecated |
||
398 | * @param string $category_slug |
||
399 | * @return object $this |
||
400 | */ |
||
401 | public function show_on_category( $category_slug ) { |
||
409 | |||
410 | /** |
||
411 | * Show the container only on posts which have term $term_slug from the $taxonomy_slug taxonomy. |
||
412 | * |
||
413 | * @deprecated |
||
414 | * @param string $taxonomy_slug |
||
415 | * @param string $term_slug |
||
416 | * @return object $this |
||
417 | */ |
||
418 | public function show_on_taxonomy_term( $term_slug, $taxonomy_slug ) { |
||
426 | |||
427 | /** |
||
428 | * Sets the meta box container context |
||
429 | * |
||
430 | * @see https://codex.wordpress.org/Function_Reference/add_meta_box |
||
431 | * @param string $context ('normal', 'advanced', 'side' or the custom `carbon_fields_after_title`) |
||
432 | */ |
||
433 | public function set_context( $context ) { |
||
437 | |||
438 | /** |
||
439 | * Sets the meta box container priority |
||
440 | * |
||
441 | * @see https://codex.wordpress.org/Function_Reference/add_meta_box |
||
442 | * @param string $priority ('high', 'core', 'default' or 'low') |
||
443 | */ |
||
444 | public function set_priority( $priority ) { |
||
448 | |||
449 | public function set_revisions_disabled( $revisions_disabled ) { |
||
453 | |||
454 | public function get_revisions_disabled() { |
||
457 | } |
||
458 |
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.