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_About_Page 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_About_Page, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
20 | class Jetpack_About_Page extends Jetpack_Admin_Page { |
||
21 | |||
22 | /** |
||
23 | * Show the settings page only when Jetpack is connected or in dev mode. |
||
24 | * |
||
25 | * @var bool If the page should be shown. |
||
26 | */ |
||
27 | protected $dont_show_if_not_active = true; |
||
28 | |||
29 | /** |
||
30 | * Anonymous info about a12s. The method fetch_a8c_data() stores the response from wpcom here. |
||
31 | * |
||
32 | * @var array |
||
33 | */ |
||
34 | private $a8c_data = null; |
||
35 | |||
36 | /** |
||
37 | * Add a submenu item to the Jetpack admin menu. |
||
38 | * |
||
39 | * @return string |
||
40 | */ |
||
41 | public function get_page_hook() { |
||
52 | |||
53 | /** |
||
54 | * Add page action |
||
55 | * |
||
56 | * @param string $hook Hook of current page, unused. |
||
57 | */ |
||
58 | public function add_page_actions( $hook ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable |
||
61 | |||
62 | /** |
||
63 | * Enqueues scripts and styles for the admin page. |
||
64 | */ |
||
65 | public function page_admin_scripts() { |
||
74 | |||
75 | /** |
||
76 | * Load styles for static page. |
||
77 | */ |
||
78 | public function additional_styles() { |
||
81 | |||
82 | /** |
||
83 | * Render the page with a common top and bottom part, and page specific content |
||
84 | */ |
||
85 | public function render() { |
||
88 | |||
89 | /** |
||
90 | * Render the page content |
||
91 | */ |
||
92 | public function page_render() { |
||
215 | |||
216 | /** |
||
217 | * Add information cards for a8c plugins. |
||
218 | */ |
||
219 | public function display_plugins() { |
||
220 | $plugins_allowedtags = array( |
||
221 | 'a' => array( |
||
222 | 'href' => array(), |
||
223 | 'title' => array(), |
||
224 | 'target' => array(), |
||
225 | ), |
||
226 | 'abbr' => array( 'title' => array() ), |
||
227 | 'acronym' => array( 'title' => array() ), |
||
228 | 'code' => array(), |
||
229 | 'pre' => array(), |
||
230 | 'em' => array(), |
||
231 | 'strong' => array(), |
||
232 | 'ul' => array(), |
||
233 | 'ol' => array(), |
||
234 | 'li' => array(), |
||
235 | 'p' => array(), |
||
236 | 'br' => array(), |
||
237 | ); |
||
238 | |||
239 | // slugs for plugins we want to display. |
||
240 | $a8c_plugins = $this->a8c_data['featured_plugins']; |
||
241 | |||
242 | // need this to access the plugins_api() function. |
||
243 | include_once ABSPATH . 'wp-admin/includes/plugin-install.php'; |
||
244 | |||
245 | $plugins = array(); |
||
246 | foreach ( $a8c_plugins as $slug ) { |
||
247 | $args = array( |
||
248 | 'slug' => $slug, |
||
249 | 'fields' => array( |
||
250 | 'added' => false, |
||
251 | 'author' => false, |
||
252 | 'author_profile' => false, |
||
253 | 'banners' => false, |
||
254 | 'contributors' => false, |
||
255 | 'donate_link' => false, |
||
256 | 'homepage' => false, |
||
257 | 'reviews' => false, |
||
258 | 'screenshots' => false, |
||
259 | 'support_threads' => false, |
||
260 | 'support_threads_resolved' => false, |
||
261 | 'sections' => false, |
||
262 | 'tags' => false, |
||
263 | 'versions' => false, |
||
264 | |||
265 | 'compatibility' => true, |
||
266 | 'downloaded' => true, |
||
267 | 'downloadlink' => true, |
||
268 | 'icons' => true, |
||
269 | 'last_updated' => true, |
||
270 | 'num_ratings' => true, |
||
271 | 'rating' => true, |
||
272 | 'requires' => true, |
||
273 | 'requires_php' => true, |
||
274 | 'short_description' => true, |
||
275 | 'tested' => true, |
||
276 | ), |
||
277 | ); |
||
278 | |||
279 | // should probably add some error checking here too. |
||
280 | $api = plugins_api( 'plugin_information', $args ); |
||
281 | $plugins[] = $api; |
||
282 | } |
||
283 | |||
284 | foreach ( $plugins as $plugin ) { |
||
285 | if ( is_object( $plugin ) ) { |
||
286 | $plugin = (array) $plugin; |
||
287 | } |
||
288 | |||
289 | $title = wp_kses( $plugin['name'], $plugins_allowedtags ); |
||
290 | $version = wp_kses( $plugin['version'], $plugins_allowedtags ); |
||
291 | |||
292 | $name = wp_strip_all_tags( $title . ' ' . $version ); |
||
293 | |||
294 | // Remove any HTML from the description. |
||
295 | $description = wp_strip_all_tags( $plugin['short_description'] ); |
||
296 | |||
297 | $wp_version = get_bloginfo( 'version' ); |
||
298 | |||
299 | $compatible_php = ( empty( $plugin['requires_php'] ) || version_compare( phpversion(), $plugin['requires_php'], '>=' ) ); |
||
300 | $compatible_wp = ( empty( $plugin['requires'] ) || version_compare( $wp_version, $plugin['requires'], '>=' ) ); |
||
301 | |||
302 | $action_links = array(); |
||
303 | |||
304 | // install button. |
||
305 | if ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) { |
||
306 | $status = install_plugin_install_status( $plugin ); |
||
307 | switch ( $status['status'] ) { |
||
308 | case 'install': |
||
309 | if ( $status['url'] ) { |
||
310 | if ( $compatible_php && $compatible_wp ) { |
||
311 | $action_links[] = sprintf( |
||
312 | '<a class="install-now button jptracks" data-slug="%1$s" href="%2$s" aria-label="%3$s" data-name="%4$s" data-jptracks-name="jetpack_about_install_button" data-jptracks-prop="%4$s">%5$s</a>', |
||
313 | esc_attr( $plugin['slug'] ), |
||
314 | esc_url( $status['url'] ), |
||
315 | /* translators: %s: plugin name and version */ |
||
316 | esc_attr( sprintf( __( 'Install %s now', 'jetpack' ), $name ) ), |
||
317 | esc_attr( $name ), |
||
318 | esc_html__( 'Install Now', 'jetpack' ) |
||
319 | ); |
||
320 | } else { |
||
321 | $action_links[] = sprintf( |
||
322 | '<button type="button" class="button button-disabled" disabled="disabled">%s</button>', |
||
323 | _x( 'Cannot Install', 'plugin', 'jetpack' ) |
||
324 | ); |
||
325 | } |
||
326 | } |
||
327 | break; |
||
328 | |||
329 | case 'update_available': |
||
330 | if ( $status['url'] ) { |
||
331 | $action_links[] = sprintf( |
||
332 | '<a class="update-now button aria-button-if-js jptracks" data-plugin="%1$s" data-slug="%2$s" href="%3$s" aria-label="%4$s" data-name="%5$s" data-jptracks-name="jetpack_about_update_button" data-jptracks-prop="%5$s">%6$s</a>', |
||
333 | esc_attr( $status['file'] ), |
||
334 | esc_attr( $plugin['slug'] ), |
||
335 | esc_url( $status['url'] ), |
||
336 | /* translators: %s: plugin name and version */ |
||
337 | esc_attr( sprintf( __( 'Update %s now', 'jetpack' ), $name ) ), |
||
338 | esc_attr( $name ), |
||
339 | __( 'Update Now', 'jetpack' ) |
||
340 | ); |
||
341 | } |
||
342 | break; |
||
343 | |||
344 | case 'latest_installed': |
||
345 | case 'newer_installed': |
||
346 | if ( is_plugin_active( $status['file'] ) ) { |
||
347 | $action_links[] = sprintf( |
||
348 | '<button type="button" class="button button-disabled" disabled="disabled">%s</button>', |
||
349 | _x( 'Active', 'plugin', 'jetpack' ) |
||
350 | ); |
||
351 | } elseif ( current_user_can( 'activate_plugin', $status['file'] ) ) { |
||
352 | $button_text = __( 'Activate', 'jetpack' ); |
||
353 | /* translators: %s: plugin name */ |
||
354 | $button_label = _x( 'Activate %s', 'plugin', 'jetpack' ); |
||
355 | $activate_url = add_query_arg( |
||
356 | array( |
||
357 | '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $status['file'] ), |
||
358 | 'action' => 'activate', |
||
359 | 'plugin' => $status['file'], |
||
360 | ), |
||
361 | network_admin_url( 'plugins.php' ) |
||
362 | ); |
||
363 | |||
364 | if ( is_network_admin() ) { |
||
365 | $button_text = __( 'Network Activate', 'jetpack' ); |
||
366 | /* translators: %s: plugin name */ |
||
367 | $button_label = _x( 'Network Activate %s', 'plugin', 'jetpack' ); |
||
368 | $activate_url = add_query_arg( array( 'networkwide' => 1 ), $activate_url ); |
||
369 | } |
||
370 | |||
371 | $action_links[] = sprintf( |
||
372 | '<a href="%1$s" class="button activate-now" aria-label="%2$s" data-jptracks-name="jetpack_about_activate_button" data-jptracks-prop="%3$s">%4$s</a>', |
||
373 | esc_url( $activate_url ), |
||
374 | esc_attr( sprintf( $button_label, $plugin['name'] ) ), |
||
375 | esc_attr( $plugin['name'] ), |
||
376 | $button_text |
||
377 | ); |
||
378 | } else { |
||
379 | $action_links[] = sprintf( |
||
380 | '<button type="button" class="button button-disabled" disabled="disabled">%s</button>', |
||
381 | _x( 'Installed', 'plugin', 'jetpack' ) |
||
382 | ); |
||
383 | } |
||
384 | break; |
||
385 | } |
||
386 | } |
||
387 | |||
388 | $plugin_install = "plugin-install.php?tab=plugin-information&plugin={$plugin['slug']}&TB_iframe=true&width=600&height=550"; |
||
389 | $details_link = is_multisite() |
||
390 | ? network_admin_url( $plugin_install ) |
||
391 | : admin_url( $plugin_install ); |
||
392 | |||
393 | if ( ! empty( $plugin['icons']['svg'] ) ) { |
||
394 | $plugin_icon_url = $plugin['icons']['svg']; |
||
395 | } elseif ( ! empty( $plugin['icons']['2x'] ) ) { |
||
396 | $plugin_icon_url = $plugin['icons']['2x']; |
||
397 | } elseif ( ! empty( $plugin['icons']['1x'] ) ) { |
||
398 | $plugin_icon_url = $plugin['icons']['1x']; |
||
399 | } else { |
||
400 | $plugin_icon_url = $plugin['icons']['default']; |
||
401 | } |
||
402 | ?> |
||
403 | |||
404 | <li class="jetpack-about__plugin plugin-card-<?php echo sanitize_html_class( $plugin['slug'] ); ?>"> |
||
405 | <?php |
||
406 | if ( ! $compatible_php || ! $compatible_wp ) { |
||
407 | echo '<div class="notice inline notice-error notice-alt"><p>'; |
||
408 | if ( ! $compatible_php && ! $compatible_wp ) { |
||
409 | esc_html_e( 'This plugin doesn’t work with your versions of WordPress and PHP.', 'jetpack' ); |
||
410 | if ( current_user_can( 'update_core' ) && current_user_can( 'update_php' ) ) { |
||
411 | printf( |
||
412 | /* translators: 1: "Update WordPress" screen URL, 2: "Update PHP" page URL */ |
||
413 | ' ' . wp_kses( __( '<a href="%1$s">Please update WordPress</a>, and then <a href="%2$s">learn more about updating PHP</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ), |
||
414 | esc_url( self_admin_url( 'update-core.php' ) ), |
||
415 | esc_url( wp_get_update_php_url() ) |
||
416 | ); |
||
417 | wp_update_php_annotation(); |
||
418 | } elseif ( current_user_can( 'update_core' ) ) { |
||
419 | printf( |
||
420 | /* translators: %s: "Update WordPress" screen URL */ |
||
421 | ' ' . wp_kses( __( '<a href="%s">Please update WordPress</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ), |
||
422 | esc_url( self_admin_url( 'update-core.php' ) ) |
||
423 | ); |
||
424 | View Code Duplication | } elseif ( current_user_can( 'update_php' ) ) { |
|
425 | printf( |
||
426 | /* translators: %s: "Update PHP" page URL */ |
||
427 | ' ' . wp_kses( __( '<a href="%s">Learn more about updating PHP</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ), |
||
428 | esc_url( wp_get_update_php_url() ) |
||
429 | ); |
||
430 | wp_update_php_annotation(); |
||
431 | } |
||
432 | View Code Duplication | } elseif ( ! $compatible_wp ) { |
|
433 | esc_html_e( 'This plugin doesn’t work with your version of WordPress.', 'jetpack' ); |
||
434 | if ( current_user_can( 'update_core' ) ) { |
||
435 | printf( |
||
436 | /* translators: %s: "Update WordPress" screen URL */ |
||
437 | ' ' . wp_kses( __( '<a href="%s">Please update WordPress</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ), |
||
438 | esc_url( self_admin_url( 'update-core.php' ) ) |
||
439 | ); |
||
440 | } |
||
441 | } elseif ( ! $compatible_php ) { |
||
442 | esc_html_e( 'This plugin doesn’t work with your version of PHP.', 'jetpack' ); |
||
443 | if ( current_user_can( 'update_php' ) ) { |
||
444 | printf( |
||
445 | /* translators: %s: "Update PHP" page URL */ |
||
446 | ' ' . wp_kses( __( '<a href="%s">Learn more about updating PHP</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ), |
||
447 | esc_url( wp_get_update_php_url() ) |
||
448 | ); |
||
449 | wp_update_php_annotation(); |
||
450 | } |
||
451 | } |
||
452 | echo '</p></div>'; |
||
453 | } |
||
454 | ?> |
||
455 | |||
456 | <div class="plugin-card-top"> |
||
457 | <div class="name column-name"> |
||
458 | <h3> |
||
459 | <a href="<?php echo esc_url( $details_link ); ?>" class="jptracks thickbox open-plugin-details-modal" data-jptracks-name="jetpack_about_plugin_modal" data-jptracks-prop="<?php echo esc_attr( $plugin['slug'] ); ?>"> |
||
460 | <?php echo esc_html( $title ); ?> |
||
461 | <img src="<?php echo esc_url( $plugin_icon_url ); ?>" class="plugin-icon" alt="<?php esc_attr_e( 'Plugin icon', 'jetpack' ); ?>" aria-hidden="true"> |
||
462 | </a> |
||
463 | </h3> |
||
464 | </div> |
||
465 | <div class="desc column-description"> |
||
466 | <p><?php echo esc_html( $description ); ?></p> |
||
467 | </div> |
||
468 | |||
469 | <div class="details-link"> |
||
470 | <a class="jptracks thickbox open-plugin-details-modal" href="<?php echo esc_url( $details_link ); ?>" data-jptracks-name="jetpack_about_plugin_details_modal" data-jptracks-prop="<?php echo esc_attr( $plugin['slug'] ); ?>"><?php esc_html_e( 'More Details', 'jetpack' ); ?></a> |
||
471 | </div> |
||
472 | </div> |
||
473 | |||
474 | <div class="plugin-card-bottom"> |
||
475 | <div class="meta"> |
||
476 | <?php |
||
477 | wp_star_rating( |
||
478 | array( |
||
479 | 'rating' => $plugin['rating'], |
||
480 | 'type' => 'percent', |
||
481 | 'number' => $plugin['num_ratings'], |
||
482 | ) |
||
483 | ); |
||
484 | ?> |
||
485 | <span class="num-ratings" aria-hidden="true">(<?php echo esc_html( number_format_i18n( $plugin['num_ratings'] ) ); ?> <?php esc_html_e( 'ratings', 'jetpack' ); ?>)</span> |
||
486 | <div class="downloaded"> |
||
487 | <?php |
||
488 | if ( $plugin['active_installs'] >= 1000000 ) { |
||
489 | $active_installs_millions = floor( $plugin['active_installs'] / 1000000 ); |
||
490 | $active_installs_text = sprintf( |
||
491 | /* translators: number of millions of installs. */ |
||
492 | _nx( '%s+ Million', '%s+ Million', $active_installs_millions, 'Active plugin installations', 'jetpack' ), |
||
493 | number_format_i18n( $active_installs_millions ) |
||
494 | ); |
||
495 | } elseif ( 0 === $plugin['active_installs'] ) { |
||
496 | $active_installs_text = _x( 'Less Than 10', 'Active plugin installations', 'jetpack' ); |
||
497 | } else { |
||
498 | $active_installs_text = number_format_i18n( $plugin['active_installs'] ) . '+'; |
||
499 | } |
||
500 | /* translators: number of active installs */ |
||
501 | printf( esc_html__( '%s Active Installations', 'jetpack' ), esc_html( $active_installs_text ) ); |
||
502 | ?> |
||
503 | </div> |
||
504 | </div> |
||
505 | |||
506 | <div class="action-links"> |
||
507 | <?php |
||
508 | if ( $action_links ) { |
||
|
|||
509 | // The var simply collects strings that have already been sanitized. |
||
510 | // phpcs:ignore WordPress.Security.EscapeOutput |
||
511 | echo '<ul class="action-buttons"><li>' . implode( '</li><li>', $action_links ) . '</li></ul>'; |
||
512 | } |
||
513 | ?> |
||
514 | </div> |
||
515 | </div> |
||
516 | </li> |
||
517 | <?php |
||
518 | |||
519 | } |
||
520 | |||
521 | } |
||
522 | |||
523 | /** |
||
524 | * Fetch anonymous data about A12s from wpcom: total count, number of countries, languages spoken. |
||
525 | * |
||
526 | * @since 7.4 |
||
527 | * |
||
528 | * @return array $data |
||
529 | */ |
||
530 | private function fetch_a8c_data() { |
||
558 | |||
559 | /** |
||
560 | * Compile and display a list of avatars for A12s that gave their permission. |
||
561 | * |
||
562 | * @since 7.3 |
||
563 | */ |
||
564 | public function display_gravatars() { |
||
597 | } |
||
598 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.