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_Full 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_Full, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 17 | class Jetpack_Sync_Full { |
||
| 18 | static $array_chunk_size = 5; |
||
|
|
|||
| 19 | static $status_transient_name = 'jetpack_full_sync_progress'; |
||
| 20 | static $transient_timeout = 3600; // an hour |
||
| 21 | static $modules = array( |
||
| 22 | 'wp_version', |
||
| 23 | 'constants', |
||
| 24 | 'functions', |
||
| 25 | 'options', |
||
| 26 | 'posts', |
||
| 27 | 'comments', |
||
| 28 | 'themes', |
||
| 29 | 'updates', |
||
| 30 | 'users', |
||
| 31 | 'terms', |
||
| 32 | 'network_options', |
||
| 33 | ); |
||
| 34 | |||
| 35 | // singleton functions |
||
| 36 | private static $instance; |
||
| 37 | private $client; |
||
| 38 | |||
| 39 | public static function getInstance() { |
||
| 40 | if ( null === self::$instance ) { |
||
| 41 | self::$instance = new self(); |
||
| 42 | } |
||
| 43 | |||
| 44 | return self::$instance; |
||
| 45 | } |
||
| 46 | |||
| 47 | protected function __construct() { |
||
| 48 | $this->init(); |
||
| 49 | } |
||
| 50 | |||
| 51 | function init() { |
||
| 52 | add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'expand_post_ids' ) ); |
||
| 53 | add_filter( 'jetpack_sync_before_send_jetpack_full_sync_comments', array( $this, 'expand_comment_ids' ) ); |
||
| 54 | add_filter( 'jetpack_sync_before_send_jetpack_full_sync_options', array( $this, 'expand_options' ) ); |
||
| 55 | add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) ); |
||
| 56 | add_filter( 'jetpack_sync_before_send_jetpack_full_sync_network_options', array( |
||
| 57 | $this, |
||
| 58 | 'expand_network_options' |
||
| 59 | ) ); |
||
| 60 | add_filter( 'jetpack_sync_before_send_jetpack_full_sync_terms', array( $this, 'expand_term_ids' ) ); |
||
| 61 | } |
||
| 62 | |||
| 63 | function start() { |
||
| 96 | |||
| 97 | private function get_client() { |
||
| 98 | if ( ! $this->client ) { |
||
| 99 | $this->client = Jetpack_Sync_Client::getInstance(); |
||
| 100 | } |
||
| 101 | |||
| 102 | return $this->client; |
||
| 103 | } |
||
| 104 | |||
| 105 | private function enqueue_wp_version() { |
||
| 106 | $this->set_status( 'wp_version', 0 ); |
||
| 107 | global $wp_version; |
||
| 108 | do_action( 'jetpack_sync_wp_version', $wp_version ); |
||
| 109 | $this->set_status( 'wp_version', 100 ); |
||
| 110 | } |
||
| 111 | |||
| 112 | private function enqueue_all_constants() { |
||
| 113 | $this->set_status( 'constants', 0 ); |
||
| 114 | $this->get_client()->force_sync_constants(); |
||
| 115 | $this->set_status( 'constants', 100 ); |
||
| 116 | } |
||
| 117 | |||
| 118 | private function enqueue_all_functions() { |
||
| 119 | $this->set_status( 'functions', 0 ); |
||
| 120 | $this->get_client()->force_sync_callables(); |
||
| 121 | $this->set_status( 'functions', 100 ); |
||
| 122 | } |
||
| 123 | |||
| 124 | private function enqueue_all_options() { |
||
| 125 | $this->set_status( 'options', 0 ); |
||
| 126 | $this->get_client()->force_sync_options(); |
||
| 127 | $this->set_status( 'options', 100 ); |
||
| 128 | } |
||
| 129 | |||
| 130 | private function enqueue_all_network_options() { |
||
| 131 | $this->set_status( 'network_options', 0 ); |
||
| 132 | $this->get_client()->force_sync_network_options(); |
||
| 133 | $this->set_status( 'network_options', 100 ); |
||
| 134 | } |
||
| 135 | |||
| 136 | private function enqueue_all_terms() { |
||
| 137 | $this->set_status( 'terms', 0 ); |
||
| 138 | global $wpdb; |
||
| 139 | |||
| 140 | $taxonomies = get_taxonomies(); |
||
| 141 | |||
| 142 | $taxonomy_counter = 0; |
||
| 143 | $total_count = count( $taxonomies ); |
||
| 144 | |||
| 145 | foreach ( $taxonomies as $taxonomy ) { |
||
| 146 | |||
| 147 | // I hope this is never bigger than RAM... |
||
| 148 | $term_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_id FROM $wpdb->term_taxonomy WHERE taxonomy = %s", $taxonomy ) ); // Should we set a limit here? |
||
| 149 | // Request posts in groups of N for efficiency |
||
| 150 | $chunked_term_ids = array_chunk( $term_ids, self::$array_chunk_size ); |
||
| 151 | |||
| 152 | $total_chunks = count( $chunked_term_ids ); |
||
| 153 | $chunk_counter = 0; |
||
| 154 | // Send each chunk as an array of objects |
||
| 155 | foreach ( $chunked_term_ids as $chunk ) { |
||
| 156 | $this->set_status( 'terms', ( ( $taxonomy_counter / $total_count ) + ( ( $chunk_counter / $total_chunks ) / $total_count ) ) * 100 ); |
||
| 157 | do_action( 'jetpack_full_sync_terms', $chunk, $taxonomy ); |
||
| 158 | $chunk_counter ++; |
||
| 159 | } |
||
| 160 | $taxonomy_counter ++; |
||
| 161 | } |
||
| 162 | $this->set_status( 'terms', 100 ); |
||
| 163 | } |
||
| 164 | |||
| 165 | View Code Duplication | private function enqueue_all_posts() { |
|
| 166 | $this->set_status( 'posts', 0 ); |
||
| 167 | global $wpdb; |
||
| 168 | |||
| 169 | // I hope this is never bigger than RAM... |
||
| 170 | $post_type_sql = Jetpack_Sync_Defaults::get_blacklisted_post_types_sql(); |
||
| 171 | $post_ids = $wpdb->get_col( "SELECT id FROM $wpdb->posts WHERE $post_type_sql" ); // Should we set a limit here? |
||
| 172 | |||
| 173 | // Request posts in groups of N for efficiency |
||
| 174 | $chunked_post_ids = array_chunk( $post_ids, self::$array_chunk_size ); |
||
| 175 | |||
| 176 | $counter = 0; |
||
| 177 | $total = count( $chunked_post_ids ); |
||
| 178 | |||
| 179 | // Send each chunk as an array of objects |
||
| 180 | foreach ( $chunked_post_ids as $chunk ) { |
||
| 181 | $this->set_status( 'posts', ( $counter / $total ) * 100 ); |
||
| 182 | do_action( 'jetpack_full_sync_posts', $chunk ); |
||
| 183 | $counter += 1; |
||
| 184 | } |
||
| 185 | |||
| 186 | $this->set_status( 'posts', 100 ); |
||
| 187 | } |
||
| 188 | |||
| 189 | public function expand_post_ids( $args ) { |
||
| 190 | $post_ids = $args[0]; |
||
| 191 | |||
| 192 | $posts = array_map( array( 'WP_Post', 'get_instance' ), $post_ids ); |
||
| 193 | $posts = array_map( array( $this->get_client(), 'filter_post_content_and_add_links' ), $posts ); |
||
| 194 | |||
| 195 | return array( |
||
| 196 | 'posts' => $posts, |
||
| 197 | 'post_metas' => $this->get_metadata( $post_ids, 'post' ), |
||
| 198 | 'terms' => $this->get_term_relationships( $post_ids ) |
||
| 199 | ); |
||
| 200 | } |
||
| 201 | |||
| 202 | View Code Duplication | private function enqueue_all_comments() { |
|
| 203 | $this->set_status( 'comments', 0 ); |
||
| 204 | |||
| 205 | global $wpdb; |
||
| 206 | |||
| 207 | $comment_ids = $wpdb->get_col( "SELECT comment_id FROM $wpdb->comments" ); // Should we set a limit here? |
||
| 208 | $chunked_comment_ids = array_chunk( $comment_ids, self::$array_chunk_size ); |
||
| 209 | |||
| 210 | $counter = 0; |
||
| 211 | $total = count( $chunked_comment_ids ); |
||
| 212 | |||
| 213 | foreach ( $chunked_comment_ids as $chunk ) { |
||
| 214 | $this->set_status( 'comments', ( $counter / $total ) * 100 ); |
||
| 215 | do_action( 'jetpack_full_sync_comments', $chunk ); |
||
| 216 | $counter += 1; |
||
| 217 | } |
||
| 218 | |||
| 219 | $this->set_status( 'comments', 100 ); |
||
| 220 | } |
||
| 221 | |||
| 222 | public function expand_comment_ids( $args ) { |
||
| 223 | $comment_ids = $args[0]; |
||
| 224 | $comments = get_comments( array( |
||
| 225 | 'include_unapproved' => true, |
||
| 226 | 'comment__in' => $comment_ids, |
||
| 227 | ) ); |
||
| 228 | |||
| 229 | return array( |
||
| 230 | 'comments' => $comments, |
||
| 231 | 'comment_metas' => $this->get_metadata( $comment_ids, 'comment' ), |
||
| 232 | ); |
||
| 233 | } |
||
| 234 | |||
| 235 | public function expand_term_ids( $args ) { |
||
| 236 | global $wp_version; |
||
| 237 | $term_ids = $args[0]; |
||
| 238 | $taxonomy = $args[1]; |
||
| 239 | // version 4.5 or higher |
||
| 240 | if ( version_compare( $wp_version, 4.5, '>=' ) ) { |
||
| 241 | $terms = get_terms( array( |
||
| 242 | 'taxonomy' => $taxonomy, |
||
| 243 | 'hide_empty' => false, |
||
| 244 | 'include' => $term_ids |
||
| 245 | ) ); |
||
| 246 | } else { |
||
| 247 | $terms = get_terms( $taxonomy, array( |
||
| 248 | 'hide_empty' => false, |
||
| 249 | 'include' => $term_ids |
||
| 250 | ) ); |
||
| 251 | } |
||
| 252 | |||
| 253 | return $terms; |
||
| 254 | } |
||
| 255 | |||
| 256 | public function expand_options( $args ) { |
||
| 257 | if ( $args[0] ) { |
||
| 258 | return $this->get_client()->get_all_options(); |
||
| 259 | } |
||
| 260 | |||
| 261 | return $args; |
||
| 262 | } |
||
| 263 | |||
| 264 | private function enqueue_all_users() { |
||
| 287 | |||
| 288 | public function expand_users( $args ) { |
||
| 289 | $user_ids = $args[0]; |
||
| 290 | |||
| 291 | return array_map( array( $this->get_client(), 'sanitize_user' ), get_users( array( 'include' => $user_ids ) ) ); |
||
| 292 | } |
||
| 293 | |||
| 294 | public function expand_network_options( $args ) { |
||
| 295 | if ( $args[0] ) { |
||
| 296 | return $this->get_client()->get_all_network_options(); |
||
| 297 | } |
||
| 298 | |||
| 299 | return $args; |
||
| 300 | } |
||
| 301 | |||
| 302 | private function get_metadata( $ids, $meta_type ) { |
||
| 303 | global $wpdb; |
||
| 304 | $table = _get_meta_table( $meta_type ); |
||
| 305 | $id = $meta_type . '_id'; |
||
| 306 | if ( ! $table ) { |
||
| 307 | return array(); |
||
| 308 | } |
||
| 309 | |||
| 310 | return $wpdb->get_results( "SELECT * FROM $table WHERE $id IN ( " . implode( ',', wp_parse_id_list( $ids ) ) . " )", OBJECT ); |
||
| 311 | } |
||
| 312 | |||
| 313 | private function get_term_relationships( $ids ) { |
||
| 314 | global $wpdb; |
||
| 315 | |||
| 316 | return $wpdb->get_results( "SELECT * FROM $wpdb->term_relationships WHERE object_id IN ( " . implode( ',', wp_parse_id_list( $ids ) ) . " )", OBJECT ); |
||
| 317 | } |
||
| 318 | |||
| 319 | // TODO: |
||
| 320 | private function enqueue_all_theme_info() { |
||
| 321 | $this->set_status( 'themes', 0 ); |
||
| 322 | $this->get_client()->send_theme_info(); |
||
| 323 | $this->set_status( 'themes', 100 ); |
||
| 324 | } |
||
| 325 | |||
| 326 | private function enqueue_all_updates() { |
||
| 327 | $this->set_status( 'updates', 0 ); |
||
| 328 | // check for updates |
||
| 329 | wp_update_plugins(); |
||
| 330 | wp_update_themes(); |
||
| 331 | _maybe_update_core(); |
||
| 332 | $this->set_status( 'updates', 100 ); |
||
| 333 | } |
||
| 334 | |||
| 335 | private function set_status( $name, $percent, $count = 1, $total = 1 ) { |
||
| 336 | set_transient( self::$status_transient_name . '_' . $name, |
||
| 337 | array( |
||
| 338 | 'progress' => $percent, |
||
| 339 | // 'count' => $count, |
||
| 340 | // 'total' => $total |
||
| 341 | ), |
||
| 342 | self::$transient_timeout |
||
| 343 | ); |
||
| 344 | } |
||
| 345 | |||
| 346 | private function set_status_queuing_started() { |
||
| 347 | set_transient( self::$status_transient_name, array( 'phase' => 'queuing started' ), self::$transient_timeout ); |
||
| 348 | } |
||
| 349 | |||
| 350 | private function set_status_queuing_finished() { |
||
| 351 | set_transient( self::$status_transient_name, array( 'phase' => 'queuing finished' ), self::$transient_timeout ); |
||
| 352 | } |
||
| 353 | |||
| 354 | // these are called by the Sync Client when it sees that the full sync start/end actions have actually been transmitted |
||
| 355 | public function set_status_sending_started() { |
||
| 365 | |||
| 366 | public function set_status_sending_finished() { |
||
| 376 | |||
| 377 | public function get_status() { |
||
| 378 | $status = get_transient( self::$status_transient_name ); |
||
| 379 | if ( ! is_array( $status ) ) { |
||
| 380 | return array( 'phase' => 'not started' ); |
||
| 381 | } |
||
| 382 | |||
| 383 | return $status; |
||
| 384 | } |
||
| 385 | |||
| 386 | public function get_module_status( $module ) { |
||
| 387 | return get_transient( self::$status_transient_name . '_' . $module ); |
||
| 388 | } |
||
| 389 | |||
| 390 | public function get_complete_status() { |
||
| 391 | return array_merge( |
||
| 392 | $this->get_status(), |
||
| 393 | array_combine( |
||
| 394 | Jetpack_Sync_Full::$modules, |
||
| 395 | array_map( array( $this, 'get_module_status' ), Jetpack_Sync_Full::$modules ) |
||
| 396 | ) |
||
| 397 | ); |
||
| 398 | } |
||
| 399 | } |
||
| 400 |
The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using
the property is implicitly global.
To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.