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 | * Add a submenu item to the Jetpack admin menu. |
||
31 | * |
||
32 | * @return string |
||
33 | */ |
||
34 | public function get_page_hook() { |
||
45 | |||
46 | /** |
||
47 | * Add page action |
||
48 | * |
||
49 | * @param string $hook Hook of current page, unused. |
||
50 | */ |
||
51 | public function add_page_actions( $hook ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable |
||
56 | |||
57 | /** |
||
58 | * Enqueues scripts and styles for the admin page. |
||
59 | */ |
||
60 | public function page_admin_scripts() { |
||
69 | |||
70 | /** |
||
71 | * Load styles for static page. |
||
72 | */ |
||
73 | public function additional_styles() { |
||
76 | |||
77 | /** |
||
78 | * Render the page with a common top and bottom part, and page specific content |
||
79 | */ |
||
80 | public function render() { |
||
83 | |||
84 | /** |
||
85 | * Change order of menu item so the About page menu item is below Site Stats. |
||
86 | * |
||
87 | * @param array $menu_order List of menu slugs. It's unaffected. This filter is used to reorder the Jetpack submenu items. |
||
88 | * |
||
89 | * @return array |
||
90 | */ |
||
91 | public function submenu_order( $menu_order ) { |
||
114 | |||
115 | /** |
||
116 | * Render the page content |
||
117 | */ |
||
118 | public function page_render() { |
||
231 | |||
232 | /** |
||
233 | * Add information cards for a8c plugins. |
||
234 | */ |
||
235 | public function display_plugins() { |
||
236 | $plugins_allowedtags = array( |
||
237 | 'a' => array( |
||
238 | 'href' => array(), |
||
239 | 'title' => array(), |
||
240 | 'target' => array(), |
||
241 | ), |
||
242 | 'abbr' => array( 'title' => array() ), |
||
243 | 'acronym' => array( 'title' => array() ), |
||
244 | 'code' => array(), |
||
245 | 'pre' => array(), |
||
246 | 'em' => array(), |
||
247 | 'strong' => array(), |
||
248 | 'ul' => array(), |
||
249 | 'ol' => array(), |
||
250 | 'li' => array(), |
||
251 | 'p' => array(), |
||
252 | 'br' => array(), |
||
253 | ); |
||
254 | |||
255 | // slugs for plugins we want to display. |
||
256 | $a8c_plugins = array( |
||
257 | 'woocommerce', |
||
258 | 'wp-super-cache', |
||
259 | 'wp-job-manager', |
||
260 | 'co-authors-plus', |
||
261 | ); |
||
262 | |||
263 | // need this to access the plugins_api() function. |
||
264 | include_once ABSPATH . 'wp-admin/includes/plugin-install.php'; |
||
265 | |||
266 | $plugins = array(); |
||
267 | foreach ( $a8c_plugins as $slug ) { |
||
268 | $args = array( |
||
269 | 'slug' => $slug, |
||
270 | 'fields' => array( |
||
271 | 'added' => false, |
||
272 | 'author' => false, |
||
273 | 'author_profile' => false, |
||
274 | 'banners' => false, |
||
275 | 'contributors' => false, |
||
276 | 'donate_link' => false, |
||
277 | 'homepage' => false, |
||
278 | 'reviews' => false, |
||
279 | 'screenshots' => false, |
||
280 | 'support_threads' => false, |
||
281 | 'support_threads_resolved' => false, |
||
282 | 'sections' => false, |
||
283 | 'tags' => false, |
||
284 | 'versions' => false, |
||
285 | |||
286 | 'compatibility' => true, |
||
287 | 'downloaded' => true, |
||
288 | 'downloadlink' => true, |
||
289 | 'icons' => true, |
||
290 | 'last_updated' => true, |
||
291 | 'num_ratings' => true, |
||
292 | 'rating' => true, |
||
293 | 'requires' => true, |
||
294 | 'requires_php' => true, |
||
295 | 'short_description' => true, |
||
296 | 'tested' => true, |
||
297 | ), |
||
298 | ); |
||
299 | |||
300 | // should probably add some error checking here too. |
||
301 | $api = plugins_api( 'plugin_information', $args ); |
||
302 | $plugins[] = $api; |
||
303 | } |
||
304 | |||
305 | foreach ( $plugins as $plugin ) { |
||
306 | if ( is_object( $plugin ) ) { |
||
307 | $plugin = (array) $plugin; |
||
308 | } |
||
309 | |||
310 | $title = wp_kses( $plugin['name'], $plugins_allowedtags ); |
||
311 | $version = wp_kses( $plugin['version'], $plugins_allowedtags ); |
||
312 | |||
313 | $name = wp_strip_all_tags( $title . ' ' . $version ); |
||
314 | |||
315 | // Remove any HTML from the description. |
||
316 | $description = wp_strip_all_tags( $plugin['short_description'] ); |
||
317 | |||
318 | $wp_version = get_bloginfo( 'version' ); |
||
319 | |||
320 | $compatible_php = ( empty( $plugin['requires_php'] ) || version_compare( phpversion(), $plugin['requires_php'], '>=' ) ); |
||
321 | $compatible_wp = ( empty( $plugin['requires'] ) || version_compare( $wp_version, $plugin['requires'], '>=' ) ); |
||
322 | |||
323 | $action_links = array(); |
||
324 | |||
325 | // install button. |
||
326 | if ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) { |
||
327 | $status = install_plugin_install_status( $plugin ); |
||
328 | switch ( $status['status'] ) { |
||
329 | case 'install': |
||
330 | if ( $status['url'] ) { |
||
331 | if ( $compatible_php && $compatible_wp ) { |
||
332 | $action_links[] = sprintf( |
||
333 | '<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>', |
||
334 | esc_attr( $plugin['slug'] ), |
||
335 | esc_url( $status['url'] ), |
||
336 | /* translators: %s: plugin name and version */ |
||
337 | esc_attr( sprintf( __( 'Install %s now', 'jetpack' ), $name ) ), |
||
338 | esc_attr( $name ), |
||
339 | esc_html__( 'Install Now', 'jetpack' ) |
||
340 | ); |
||
341 | } else { |
||
342 | $action_links[] = sprintf( |
||
343 | '<button type="button" class="button button-disabled" disabled="disabled">%s</button>', |
||
344 | _x( 'Cannot Install', 'plugin', 'jetpack' ) |
||
345 | ); |
||
346 | } |
||
347 | } |
||
348 | break; |
||
349 | |||
350 | case 'update_available': |
||
351 | if ( $status['url'] ) { |
||
352 | $action_links[] = sprintf( |
||
353 | '<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>', |
||
354 | esc_attr( $status['file'] ), |
||
355 | esc_attr( $plugin['slug'] ), |
||
356 | esc_url( $status['url'] ), |
||
357 | /* translators: %s: plugin name and version */ |
||
358 | esc_attr( sprintf( __( 'Update %s now', 'jetpack' ), $name ) ), |
||
359 | esc_attr( $name ), |
||
360 | __( 'Update Now', 'jetpack' ) |
||
361 | ); |
||
362 | } |
||
363 | break; |
||
364 | |||
365 | case 'latest_installed': |
||
366 | case 'newer_installed': |
||
367 | if ( is_plugin_active( $status['file'] ) ) { |
||
368 | $action_links[] = sprintf( |
||
369 | '<button type="button" class="button button-disabled" disabled="disabled">%s</button>', |
||
370 | _x( 'Active', 'plugin', 'jetpack' ) |
||
371 | ); |
||
372 | } elseif ( current_user_can( 'activate_plugin', $status['file'] ) ) { |
||
373 | $button_text = __( 'Activate', 'jetpack' ); |
||
374 | /* translators: %s: plugin name */ |
||
375 | $button_label = _x( 'Activate %s', 'plugin', 'jetpack' ); |
||
376 | $activate_url = add_query_arg( |
||
377 | array( |
||
378 | '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $status['file'] ), |
||
379 | 'action' => 'activate', |
||
380 | 'plugin' => $status['file'], |
||
381 | ), |
||
382 | network_admin_url( 'plugins.php' ) |
||
383 | ); |
||
384 | |||
385 | if ( is_network_admin() ) { |
||
386 | $button_text = __( 'Network Activate', 'jetpack' ); |
||
387 | /* translators: %s: plugin name */ |
||
388 | $button_label = _x( 'Network Activate %s', 'plugin', 'jetpack' ); |
||
389 | $activate_url = add_query_arg( array( 'networkwide' => 1 ), $activate_url ); |
||
390 | } |
||
391 | |||
392 | $action_links[] = sprintf( |
||
393 | '<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>', |
||
394 | esc_url( $activate_url ), |
||
395 | esc_attr( sprintf( $button_label, $plugin['name'] ) ), |
||
396 | esc_attr( $plugin['name'] ), |
||
397 | $button_text |
||
398 | ); |
||
399 | } else { |
||
400 | $action_links[] = sprintf( |
||
401 | '<button type="button" class="button button-disabled" disabled="disabled">%s</button>', |
||
402 | _x( 'Installed', 'plugin', 'jetpack' ) |
||
403 | ); |
||
404 | } |
||
405 | break; |
||
406 | } |
||
407 | } |
||
408 | |||
409 | $plugin_install = "plugin-install.php?tab=plugin-information&plugin={$plugin['slug']}&TB_iframe=true&width=600&height=550"; |
||
410 | $details_link = is_multisite() |
||
411 | ? network_admin_url( $plugin_install ) |
||
412 | : admin_url( $plugin_install ); |
||
413 | |||
414 | if ( ! empty( $plugin['icons']['svg'] ) ) { |
||
415 | $plugin_icon_url = $plugin['icons']['svg']; |
||
416 | } elseif ( ! empty( $plugin['icons']['2x'] ) ) { |
||
417 | $plugin_icon_url = $plugin['icons']['2x']; |
||
418 | } elseif ( ! empty( $plugin['icons']['1x'] ) ) { |
||
419 | $plugin_icon_url = $plugin['icons']['1x']; |
||
420 | } else { |
||
421 | $plugin_icon_url = $plugin['icons']['default']; |
||
422 | } |
||
423 | ?> |
||
424 | |||
425 | <li class="jetpack-about__plugin plugin-card-<?php echo sanitize_html_class( $plugin['slug'] ); ?>"> |
||
426 | <?php |
||
427 | if ( ! $compatible_php || ! $compatible_wp ) { |
||
428 | echo '<div class="notice inline notice-error notice-alt"><p>'; |
||
429 | if ( ! $compatible_php && ! $compatible_wp ) { |
||
430 | esc_html_e( 'This plugin doesn’t work with your versions of WordPress and PHP.', 'jetpack' ); |
||
431 | if ( current_user_can( 'update_core' ) && current_user_can( 'update_php' ) ) { |
||
432 | printf( |
||
433 | /* translators: 1: "Update WordPress" screen URL, 2: "Update PHP" page URL */ |
||
434 | ' ' . 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 ) ) ), |
||
435 | esc_url( self_admin_url( 'update-core.php' ) ), |
||
436 | esc_url( $this->jp_get_update_php_url() ) |
||
437 | ); |
||
438 | $this->jp_update_php_annotation(); |
||
439 | } elseif ( current_user_can( 'update_core' ) ) { |
||
440 | printf( |
||
441 | /* translators: %s: "Update WordPress" screen URL */ |
||
442 | ' ' . wp_kses( __( '<a href="%s">Please update WordPress</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ), |
||
443 | esc_url( self_admin_url( 'update-core.php' ) ) |
||
444 | ); |
||
445 | View Code Duplication | } elseif ( current_user_can( 'update_php' ) ) { |
|
446 | printf( |
||
447 | /* translators: %s: "Update PHP" page URL */ |
||
448 | ' ' . wp_kses( __( '<a href="%s">Learn more about updating PHP</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ), |
||
449 | esc_url( $this->jp_get_update_php_url() ) |
||
450 | ); |
||
451 | $this->jp_update_php_annotation(); |
||
452 | } |
||
453 | } elseif ( ! $compatible_wp ) { |
||
454 | esc_html_e( 'This plugin doesn’t work with your version of WordPress.', 'jetpack' ); |
||
455 | if ( current_user_can( 'update_core' ) ) { |
||
456 | printf( |
||
457 | /* translators: %s: "Update WordPress" screen URL */ |
||
458 | ' ' . wp_kses( __( '<a href="%s">Please update WordPress</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ), |
||
459 | esc_url( self_admin_url( 'update-core.php' ) ) |
||
460 | ); |
||
461 | } |
||
462 | View Code Duplication | } elseif ( ! $compatible_php ) { |
|
463 | esc_html_e( 'This plugin doesn’t work with your version of PHP.', 'jetpack' ); |
||
464 | if ( current_user_can( 'update_php' ) ) { |
||
465 | printf( |
||
466 | /* translators: %s: "Update PHP" page URL */ |
||
467 | ' ' . wp_kses( __( '<a href="%s">Learn more about updating PHP</a>.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ), |
||
468 | esc_url( $this->jp_get_update_php_url() ) |
||
469 | ); |
||
470 | $this->jp_update_php_annotation(); |
||
471 | } |
||
472 | } |
||
473 | echo '</p></div>'; |
||
474 | } |
||
475 | ?> |
||
476 | |||
477 | <div class="plugin-card-top"> |
||
478 | <div class="name column-name"> |
||
479 | <h3> |
||
480 | <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'] ); ?>"> |
||
481 | <?php echo esc_html( $title ); ?> |
||
482 | <img src="<?php echo esc_attr( $plugin_icon_url ); ?>" class="plugin-icon" alt=""> |
||
483 | </a> |
||
484 | </h3> |
||
485 | </div> |
||
486 | <div class="desc column-description"> |
||
487 | <p><?php echo esc_html( $description ); ?></p> |
||
488 | </div> |
||
489 | |||
490 | <div class="details-link"> |
||
491 | <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> |
||
492 | </div> |
||
493 | </div> |
||
494 | |||
495 | <div class="plugin-card-bottom"> |
||
496 | <div class="meta"> |
||
497 | <?php |
||
498 | wp_star_rating( |
||
499 | array( |
||
500 | 'rating' => $plugin['rating'], |
||
501 | 'type' => 'percent', |
||
502 | 'number' => $plugin['num_ratings'], |
||
503 | ) |
||
504 | ); |
||
505 | ?> |
||
506 | <span class="num-ratings" aria-hidden="true">(<?php echo esc_html( number_format_i18n( $plugin['num_ratings'] ) ); ?> <?php esc_html_e( 'ratings', 'jetpack' ); ?>)</span> |
||
507 | <div class="downloaded"> |
||
508 | <?php |
||
509 | if ( $plugin['active_installs'] >= 1000000 ) { |
||
510 | $active_installs_millions = floor( $plugin['active_installs'] / 1000000 ); |
||
511 | $active_installs_text = sprintf( |
||
512 | /* translators: number of millions of installs. */ |
||
513 | _nx( '%s+ Million', '%s+ Million', $active_installs_millions, 'Active plugin installations', 'jetpack' ), |
||
514 | number_format_i18n( $active_installs_millions ) |
||
515 | ); |
||
516 | } elseif ( 0 === $plugin['active_installs'] ) { |
||
517 | $active_installs_text = _x( 'Less Than 10', 'Active plugin installations', 'jetpack' ); |
||
518 | } else { |
||
519 | $active_installs_text = number_format_i18n( $plugin['active_installs'] ) . '+'; |
||
520 | } |
||
521 | /* translators: number of active installs */ |
||
522 | printf( esc_html__( '%s Active Installations', 'jetpack' ), esc_html( $active_installs_text ) ); |
||
523 | ?> |
||
524 | </div> |
||
525 | </div> |
||
526 | |||
527 | <div class="action-links"> |
||
528 | <?php |
||
529 | if ( $action_links ) { |
||
|
|||
530 | // The var simply collects strings that have already been sanitized. |
||
531 | // phpcs:ignore WordPress.Security.EscapeOutput |
||
532 | echo '<ul class="action-buttons"><li>' . implode( '</li><li>', $action_links ) . '</li></ul>'; |
||
533 | } |
||
534 | ?> |
||
535 | </div> |
||
536 | </div> |
||
537 | </li> |
||
538 | <?php |
||
539 | |||
540 | } |
||
541 | |||
542 | } |
||
543 | |||
544 | /** |
||
545 | * Fetch Gravatar hashes for public A12s from wpcom and display them as a list. |
||
546 | * |
||
547 | * @since 7.3 |
||
548 | */ |
||
549 | public function display_gravatars() { |
||
582 | |||
583 | // The following methods jp_get_update_php_url, jp_get_default_update_php_url, and jp_update_php_annotation, |
||
584 | // are copies of functions introduced in WP 5.1 |
||
585 | // At the time of releasing this, we're still supporting WP 5.0, so we needed |
||
586 | // to have them here to avoid fatal errors in old installations. |
||
587 | |||
588 | /** |
||
589 | * Gets the URL to learn more about updating the PHP version the site is running on. |
||
590 | * |
||
591 | * This URL can be overridden by specifying an environment variable `WP_UPDATE_PHP_URL` or by using the |
||
592 | * {@see 'wp_update_php_url'} filter. Providing an empty string is not allowed and will result in the |
||
593 | * default URL being used. Furthermore the page the URL links to should preferably be localized in the |
||
594 | * site language. |
||
595 | * |
||
596 | * @todo: Remove when 5.1 is minimum WP version. |
||
597 | * @since 5.1.0 |
||
598 | * |
||
599 | * @return string URL to learn more about updating PHP. |
||
600 | */ |
||
601 | private function jp_get_update_php_url() { |
||
627 | |||
628 | /** |
||
629 | * Gets the default URL to learn more about updating the PHP version the site is running on. |
||
630 | * |
||
631 | * Do not use this function to retrieve this URL. Instead, use {@see wp_get_update_php_url()} when relying on the URL. |
||
632 | * This function does not allow modifying the returned URL, and is only used to compare the actually used URL with the |
||
633 | * default one. |
||
634 | * |
||
635 | * @todo: Remove when 5.1 is minimum WP version. |
||
636 | * @since 5.1.0 |
||
637 | * @access private |
||
638 | * |
||
639 | * @return string Default URL to learn more about updating PHP. |
||
640 | */ |
||
641 | private function jp_get_default_update_php_url() { |
||
644 | |||
645 | /** |
||
646 | * Prints the default annotation for the web host altering the "Update PHP" page URL. |
||
647 | * |
||
648 | * This function is to be used after {@see wp_get_update_php_url()} to display a consistent |
||
649 | * annotation if the web host has altered the default "Update PHP" page URL. |
||
650 | * |
||
651 | * @todo: Remove when 5.1 is minimum WP version. |
||
652 | * @since 5.1.0 |
||
653 | */ |
||
654 | private function jp_update_php_annotation() { |
||
678 | |||
679 | } |
||
680 |
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.