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 Jetpack_Sync_WP_Replicastore 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 Jetpack_Sync_WP_Replicastore, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
10 | class Jetpack_Sync_WP_Replicastore implements iJetpack_Sync_Replicastore { |
||
11 | |||
12 | |||
13 | public function reset() { |
||
34 | |||
35 | function full_sync_start( $config ) { |
||
38 | |||
39 | function full_sync_end( $checksum ) { |
||
42 | |||
43 | View Code Duplication | public function post_count( $status = null, $min_id = null, $max_id = null ) { |
|
64 | |||
65 | // TODO: actually use max_id/min_id |
||
66 | public function get_posts( $status = null, $min_id = null, $max_id = null ) { |
||
80 | |||
81 | public function get_post( $id ) { |
||
84 | |||
85 | public function upsert_post( $post, $silent = false ) { |
||
144 | |||
145 | public function delete_post( $post_id ) { |
||
148 | |||
149 | public function posts_checksum( $min_id = null, $max_id = null ) { |
||
153 | |||
154 | public function post_meta_checksum( $min_id = null, $max_id = null ) { |
||
158 | |||
159 | View Code Duplication | public function comment_count( $status = null, $min_id = null, $max_id = null ) { |
|
180 | |||
181 | private function comment_status_to_approval_value( $status ) { |
||
199 | |||
200 | // TODO: actually use max_id/min_id |
||
201 | public function get_comments( $status = null, $min_id = null, $max_id = null ) { |
||
213 | |||
214 | public function get_comment( $id ) { |
||
217 | |||
218 | public function upsert_comment( $comment ) { |
||
263 | |||
264 | public function trash_comment( $comment_id ) { |
||
267 | |||
268 | public function delete_comment( $comment_id ) { |
||
271 | |||
272 | public function spam_comment( $comment_id ) { |
||
275 | |||
276 | public function trashed_post_comments( $post_id, $statuses ) { |
||
279 | |||
280 | public function untrashed_post_comments( $post_id ) { |
||
283 | |||
284 | public function comments_checksum( $min_id = null, $max_id = null ) { |
||
288 | |||
289 | public function comment_meta_checksum( $min_id = null, $max_id = null ) { |
||
293 | |||
294 | public function options_checksum() { |
||
302 | |||
303 | |||
304 | public function update_option( $option, $value ) { |
||
307 | |||
308 | public function get_option( $option, $default = false ) { |
||
311 | |||
312 | public function delete_option( $option ) { |
||
315 | |||
316 | public function set_theme_support( $theme_support ) { |
||
319 | |||
320 | public function current_theme_supports( $feature ) { |
||
323 | |||
324 | public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) { |
||
327 | |||
328 | /** |
||
329 | * |
||
330 | * Stores remote meta key/values alongside an ID mapping key |
||
331 | * |
||
332 | * @param $type |
||
333 | * @param $object_id |
||
334 | * @param $meta_key |
||
335 | * @param $meta_value |
||
336 | * @param $meta_id |
||
337 | * |
||
338 | * @return bool |
||
339 | */ |
||
340 | public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) { |
||
382 | |||
383 | public function delete_metadata( $type, $object_id, $meta_ids ) { |
||
400 | |||
401 | // todo: test this out to make sure it works as expected. |
||
402 | public function delete_batch_metadata( $type, $object_ids, $meta_key ) { |
||
417 | |||
418 | // constants |
||
419 | public function get_constant( $constant ) { |
||
428 | |||
429 | public function set_constant( $constant, $value ) { |
||
432 | |||
433 | public function get_updates( $type ) { |
||
442 | |||
443 | public function set_updates( $type, $updates ) { |
||
448 | |||
449 | // functions |
||
450 | public function get_callable( $name ) { |
||
459 | |||
460 | public function set_callable( $name, $value ) { |
||
463 | |||
464 | // network options |
||
465 | public function get_site_option( $option ) { |
||
468 | |||
469 | public function update_site_option( $option, $value ) { |
||
472 | |||
473 | public function delete_site_option( $option ) { |
||
476 | |||
477 | // terms |
||
478 | // terms |
||
479 | public function get_terms( $taxonomy ) { |
||
482 | |||
483 | public function get_term( $taxonomy, $term_id, $is_term_id = true ) { |
||
491 | |||
492 | private function ensure_taxonomy( $taxonomy ) { |
||
511 | |||
512 | public function get_the_terms( $object_id, $taxonomy ) { |
||
515 | |||
516 | public function update_term( $term_object ) { |
||
549 | |||
550 | public function delete_term( $term_id, $taxonomy ) { |
||
553 | |||
554 | public function update_object_terms( $object_id, $taxonomy, $terms, $append ) { |
||
557 | |||
558 | public function delete_object_terms( $object_id, $tt_ids ) { |
||
602 | |||
603 | // users |
||
604 | public function user_count() { |
||
607 | |||
608 | public function get_user( $user_id ) { |
||
611 | |||
612 | public function upsert_user( $user ) { |
||
615 | |||
616 | public function delete_user( $user_id ) { |
||
619 | |||
620 | public function upsert_user_locale( $user_id, $local ) { |
||
623 | |||
624 | public function delete_user_locale( $user_id ) { |
||
627 | |||
628 | public function get_user_locale( $user_id ) { |
||
631 | |||
632 | public function get_allowed_mime_types( $user_id ) { |
||
635 | |||
636 | public function checksum_all() { |
||
647 | |||
648 | function checksum_histogram( $object_type, $buckets, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true, $salt = '' ) { |
||
649 | global $wpdb; |
||
650 | |||
651 | $wpdb->queries = array(); |
||
652 | |||
653 | switch ( $object_type ) { |
||
654 | case 'posts': |
||
655 | $object_count = $this->post_count( null, $start_id, $end_id ); |
||
656 | $object_table = $wpdb->posts; |
||
657 | $id_field = 'ID'; |
||
658 | $where_sql = Jetpack_Sync_Settings::get_blacklisted_post_types_sql(); |
||
659 | if ( empty( $columns ) ) { |
||
660 | $columns = Jetpack_Sync_Defaults::$default_post_checksum_columns; |
||
661 | } |
||
662 | break; |
||
663 | View Code Duplication | case 'post_meta': |
|
664 | $object_table = $wpdb->postmeta; |
||
665 | $where_sql = Jetpack_Sync_Settings::get_whitelisted_post_meta_sql(); |
||
666 | $object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id ); |
||
667 | $id_field = 'meta_id'; |
||
668 | |||
669 | if ( empty( $columns ) ) { |
||
670 | $columns = Jetpack_Sync_Defaults::$default_post_meta_checksum_columns; |
||
671 | } |
||
672 | break; |
||
673 | case 'comments': |
||
674 | $object_count = $this->comment_count( null, $start_id, $end_id ); |
||
675 | $object_table = $wpdb->comments; |
||
676 | $id_field = 'comment_ID'; |
||
677 | $where_sql = Jetpack_Sync_Settings::get_comments_filter_sql(); |
||
678 | if ( empty( $columns ) ) { |
||
679 | $columns = Jetpack_Sync_Defaults::$default_comment_checksum_columns; |
||
680 | } |
||
681 | break; |
||
682 | View Code Duplication | case 'comment_meta': |
|
683 | $object_table = $wpdb->commentmeta; |
||
684 | $where_sql = Jetpack_Sync_Settings::get_whitelisted_comment_meta_sql(); |
||
685 | $object_count = $this->meta_count( $object_table, $where_sql, $start_id, $end_id ); |
||
686 | $id_field = 'meta_id'; |
||
687 | if ( empty( $columns ) ) { |
||
688 | $columns = Jetpack_Sync_Defaults::$default_post_meta_checksum_columns; |
||
689 | } |
||
690 | break; |
||
691 | default: |
||
692 | return false; |
||
693 | } |
||
694 | |||
695 | $bucket_size = intval( ceil( $object_count / $buckets ) ); |
||
696 | $previous_max_id = 0; |
||
697 | $histogram = array(); |
||
698 | |||
699 | $where = '1=1'; |
||
700 | |||
701 | if ( $start_id ) { |
||
702 | $where .= " AND $id_field >= " . intval( $start_id ); |
||
703 | } |
||
704 | |||
705 | if ( $end_id ) { |
||
706 | $where .= " AND $id_field <= " . intval( $end_id ); |
||
707 | } |
||
708 | |||
709 | do { |
||
710 | list( $first_id, $last_id ) = $wpdb->get_row( |
||
711 | "SELECT MIN($id_field) as min_id, MAX($id_field) as max_id FROM ( SELECT $id_field FROM $object_table WHERE $where AND $id_field > $previous_max_id ORDER BY $id_field ASC LIMIT $bucket_size ) as ids", |
||
712 | ARRAY_N |
||
713 | ); |
||
714 | |||
715 | if ( null === $first_id || null === $last_id ) { |
||
716 | // Nothing to checksum here... |
||
717 | break; |
||
718 | } |
||
719 | |||
720 | // get the checksum value |
||
721 | $value = $this->table_checksum( $object_table, $columns, $id_field, $where_sql, $first_id, $last_id, $strip_non_ascii, $salt ); |
||
722 | |||
723 | if ( is_wp_error( $value ) ) { |
||
724 | return $value; |
||
725 | } |
||
726 | |||
727 | if ( null === $first_id || null === $last_id ) { |
||
728 | break; |
||
729 | } elseif ( $first_id === $last_id ) { |
||
730 | $histogram[ $first_id ] = $value; |
||
731 | } else { |
||
732 | $histogram[ "{$first_id}-{$last_id}" ] = $value; |
||
733 | } |
||
734 | |||
735 | $previous_max_id = $last_id; |
||
736 | } while ( true ); |
||
737 | |||
738 | return $histogram; |
||
739 | } |
||
740 | |||
741 | private function table_checksum( $table, $columns, $id_column, $where_sql = '1=1', $min_id = null, $max_id = null, $strip_non_ascii = true, $salt = '' ) { |
||
742 | global $wpdb; |
||
743 | |||
744 | // sanitize to just valid MySQL column names |
||
745 | $sanitized_columns = preg_grep( '/^[0-9,a-z,A-Z$_]+$/i', $columns ); |
||
746 | |||
747 | if ( $strip_non_ascii ) { |
||
748 | $columns_sql = implode( ',', array_map( array( $this, 'strip_non_ascii_sql' ), $sanitized_columns ) ); |
||
749 | } else { |
||
750 | $columns_sql = implode( ',', $sanitized_columns ); |
||
751 | } |
||
752 | |||
753 | if ( null !== $min_id && null !== $max_id ) { |
||
754 | if ( $min_id === $max_id ) { |
||
755 | $min_id = intval( $min_id ); |
||
756 | $where_sql .= " AND $id_column = $min_id LIMIT 1"; |
||
757 | } else { |
||
758 | $min_id = intval( $min_id ); |
||
759 | $max_id = intval( $max_id ); |
||
760 | $size = $max_id - $min_id; |
||
761 | $where_sql .= " AND $id_column >= $min_id AND $id_column <= $max_id LIMIT $size"; |
||
762 | } |
||
763 | } else { |
||
764 | if ( null !== $min_id ) { |
||
765 | $min_id = intval( $min_id ); |
||
766 | $where_sql .= " AND $id_column >= $min_id"; |
||
767 | } |
||
768 | |||
769 | if ( null !== $max_id ) { |
||
770 | $max_id = intval( $max_id ); |
||
771 | $where_sql .= " AND $id_column <= $max_id"; |
||
772 | } |
||
773 | } |
||
774 | |||
775 | $query = <<<ENDSQL |
||
776 | SELECT CAST( SUM( CRC32( CONCAT_WS( '#', '%s', {$columns_sql} ) ) ) AS UNSIGNED INT ) |
||
777 | FROM $table |
||
778 | WHERE $where_sql; |
||
779 | ENDSQL; |
||
780 | $result = $wpdb->get_var( $wpdb->prepare( $query, $salt ) ); |
||
781 | if ( $wpdb->last_error ) { |
||
782 | return new WP_Error( 'database_error', $wpdb->last_error ); |
||
783 | } |
||
784 | |||
785 | return $result; |
||
786 | } |
||
787 | |||
788 | public function get_checksum_type() { |
||
789 | return 'sum'; |
||
790 | } |
||
791 | |||
792 | private function meta_count( $table, $where_sql, $min_id, $max_id ) { |
||
805 | |||
806 | /** |
||
807 | * Wraps a column name in SQL which strips non-ASCII chars. |
||
808 | * This helps normalize data to avoid checksum differences caused by |
||
809 | * badly encoded data in the DB |
||
810 | */ |
||
811 | function strip_non_ascii_sql( $column_name ) { |
||
814 | |||
815 | private function invalid_call() { |
||
820 | } |
||
821 |
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.
Both the
$myVar
assignment in line 1 and the$higher
assignment in line 2 are dead. The first because$myVar
is never used and the second because$higher
is always overwritten for every possible time line.