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_Options 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_Options, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 13 | class Jetpack_Options { |
||
| 14 | |||
| 15 | /** |
||
| 16 | * An array that maps a grouped option type to an option name. |
||
| 17 | * |
||
| 18 | * @var array |
||
| 19 | */ |
||
| 20 | private static $grouped_options = array( |
||
| 21 | 'compact' => 'jetpack_options', |
||
| 22 | 'private' => 'jetpack_private_options', |
||
| 23 | ); |
||
| 24 | |||
| 25 | /** |
||
| 26 | * Returns an array of option names for a given type. |
||
| 27 | * |
||
| 28 | * @param string $type The type of option to return. Defaults to 'compact'. |
||
| 29 | * |
||
| 30 | * @return array |
||
| 31 | */ |
||
| 32 | public static function get_option_names( $type = 'compact' ) { |
||
| 117 | |||
| 118 | /** |
||
| 119 | * Is the option name valid? |
||
| 120 | * |
||
| 121 | * @param string $name The name of the option. |
||
| 122 | * @param string|null $group The name of the group that the option is in. Default to null, which will search non_compact. |
||
| 123 | * |
||
| 124 | * @return bool Is the option name valid? |
||
| 125 | */ |
||
| 126 | public static function is_valid( $name, $group = null ) { |
||
| 154 | |||
| 155 | /** |
||
| 156 | * Checks if an option must be saved for the whole network in WP Multisite |
||
| 157 | * |
||
| 158 | * @param string $option_name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name. |
||
| 159 | * |
||
| 160 | * @return bool |
||
| 161 | */ |
||
| 162 | public static function is_network_option( $option_name ) { |
||
| 168 | |||
| 169 | /** |
||
| 170 | * Filters the requested option. |
||
| 171 | * This is a wrapper around `get_option_from_database` so that we can filter the option. |
||
| 172 | * |
||
| 173 | * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name. |
||
| 174 | * @param mixed $default (optional). |
||
| 175 | * |
||
| 176 | * @return mixed |
||
| 177 | */ |
||
| 178 | public static function get_option( $name, $default = false ) { |
||
| 191 | |||
| 192 | /** |
||
| 193 | * Returns the requested option. Looks in jetpack_options or jetpack_$name as appropriate. |
||
| 194 | * |
||
| 195 | * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name. |
||
| 196 | * @param mixed $default (optional). |
||
| 197 | * |
||
| 198 | * @return mixed |
||
| 199 | */ |
||
| 200 | private static function get_option_from_database( $name, $default = false ) { |
||
| 219 | |||
| 220 | /** |
||
| 221 | * Returns the requested option, and ensures it's autoloaded in the future. |
||
| 222 | * This does _not_ adjust the prefix in any way (does not prefix jetpack_%) |
||
| 223 | * |
||
| 224 | * @param string $name Option name. |
||
| 225 | * @param mixed $default (optional). |
||
| 226 | * |
||
| 227 | * @return mixed |
||
| 228 | */ |
||
| 229 | public static function get_option_and_ensure_autoload( $name, $default ) { |
||
| 248 | |||
| 249 | /** |
||
| 250 | * Update grouped option |
||
| 251 | * |
||
| 252 | * @param string $group Options group. |
||
| 253 | * @param string $name Options name. |
||
| 254 | * @param mixed $value Options value. |
||
| 255 | * |
||
| 256 | * @return bool Success or failure. |
||
| 257 | */ |
||
| 258 | private static function update_grouped_option( $group, $name, $value ) { |
||
| 267 | |||
| 268 | /** |
||
| 269 | * Updates the single given option. Updates jetpack_options or jetpack_$name as appropriate. |
||
| 270 | * |
||
| 271 | * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name. |
||
| 272 | * @param mixed $value Option value. |
||
| 273 | * @param string $autoload If not compact option, allows specifying whether to autoload or not. |
||
| 274 | * |
||
| 275 | * @return bool Was the option successfully updated? |
||
| 276 | */ |
||
| 277 | public static function update_option( $name, $value, $autoload = null ) { |
||
| 306 | |||
| 307 | /** |
||
| 308 | * Updates the multiple given options. Updates jetpack_options and/or jetpack_$name as appropriate. |
||
| 309 | * |
||
| 310 | * @param array $array array( option name => option value, ... ). |
||
| 311 | */ |
||
| 312 | public static function update_options( $array ) { |
||
| 324 | |||
| 325 | /** |
||
| 326 | * Deletes the given option. May be passed multiple option names as an array. |
||
| 327 | * Updates jetpack_options and/or deletes jetpack_$name as appropriate. |
||
| 328 | * |
||
| 329 | * @param string|array $names Option names. They must come _without_ `jetpack_%` prefix. The method will prefix the option names. |
||
| 330 | * |
||
| 331 | * @return bool Was the option successfully deleted? |
||
| 332 | */ |
||
| 333 | public static function delete_option( $names ) { |
||
| 360 | |||
| 361 | /** |
||
| 362 | * Get group option. |
||
| 363 | * |
||
| 364 | * @param string $group Option group name. |
||
| 365 | * @param string $name Option name. |
||
| 366 | * @param mixed $default Default option value. |
||
| 367 | * |
||
| 368 | * @return mixed Option. |
||
| 369 | */ |
||
| 370 | private static function get_grouped_option( $group, $name, $default ) { |
||
| 378 | |||
| 379 | /** |
||
| 380 | * Delete grouped option. |
||
| 381 | * |
||
| 382 | * @param string $group Option group name. |
||
| 383 | * @param array $names Option names. |
||
| 384 | * |
||
| 385 | * @return bool Success or failure. |
||
| 386 | */ |
||
| 387 | private static function delete_grouped_option( $group, $names ) { |
||
| 401 | |||
| 402 | /* |
||
| 403 | * Raw option methods allow Jetpack to get / update / delete options via direct DB queries, including options |
||
| 404 | * that are not created by the Jetpack plugin. This is helpful only in rare cases when we need to bypass |
||
| 405 | * cache and filters. |
||
| 406 | */ |
||
| 407 | |||
| 408 | /** |
||
| 409 | * Deletes an option via $wpdb query. |
||
| 410 | * |
||
| 411 | * @param string $name Option name. |
||
| 412 | * |
||
| 413 | * @return bool Is the option deleted? |
||
| 414 | */ |
||
| 415 | public static function delete_raw_option( $name ) { |
||
| 423 | |||
| 424 | /** |
||
| 425 | * Updates an option via $wpdb query. |
||
| 426 | * |
||
| 427 | * @param string $name Option name. |
||
| 428 | * @param mixed $value Option value. |
||
| 429 | * @param bool $autoload Specifying whether to autoload or not. |
||
| 430 | * |
||
| 431 | * @return bool Is the option updated? |
||
| 432 | */ |
||
| 433 | public static function update_raw_option( $name, $value, $autoload = false ) { |
||
| 473 | |||
| 474 | /** |
||
| 475 | * Gets an option via $wpdb query. |
||
| 476 | * |
||
| 477 | * @since 5.4.0 |
||
| 478 | * |
||
| 479 | * @param string $name Option name. |
||
| 480 | * @param mixed $default Default option value if option is not found. |
||
| 481 | * |
||
| 482 | * @return mixed Option value, or null if option is not found and default is not specified. |
||
| 483 | */ |
||
| 484 | public static function get_raw_option( $name, $default = null ) { |
||
| 504 | |||
| 505 | /** |
||
| 506 | * This function checks for a constant that, if present, will disable direct DB queries Jetpack uses to manage certain options and force Jetpack to always use Options API instead. |
||
| 507 | * Options can be selectively managed via a blocklist by filtering option names via the jetpack_disabled_raw_option filter. |
||
| 508 | * |
||
| 509 | * @param string $name Option name. |
||
| 510 | * |
||
| 511 | * @return bool |
||
| 512 | */ |
||
| 513 | public static function bypass_raw_option( $name ) { |
||
| 528 | |||
| 529 | /** |
||
| 530 | * Gets all known options that are used by Jetpack and managed by Jetpack_Options. |
||
| 531 | * |
||
| 532 | * @since 5.4.0 |
||
| 533 | * |
||
| 534 | * @param boolean $strip_unsafe_options If true, and by default, will strip out options necessary for the connection to WordPress.com. |
||
| 535 | * @return array An array of all options managed via the Jetpack_Options class. |
||
| 536 | */ |
||
| 537 | public static function get_all_jetpack_options( $strip_unsafe_options = true ) { |
||
| 572 | |||
| 573 | /** |
||
| 574 | * Get all options that are not managed by the Jetpack_Options class that are used by Jetpack. |
||
| 575 | * |
||
| 576 | * @since 5.4.0 |
||
| 577 | * |
||
| 578 | * @return array |
||
| 579 | */ |
||
| 580 | public static function get_all_wp_options() { |
||
| 581 | // A manual build of the wp options. |
||
| 582 | return array( |
||
| 583 | 'sharing-options', |
||
| 584 | 'disabled_likes', |
||
| 585 | 'disabled_reblogs', |
||
| 586 | 'jetpack_comments_likes_enabled', |
||
| 587 | 'stats_options', |
||
| 588 | 'stats_dashboard_widget', |
||
| 589 | 'safecss_preview_rev', |
||
| 590 | 'safecss_rev', |
||
| 591 | 'safecss_revision_migrated', |
||
| 592 | 'nova_menu_order', |
||
| 593 | 'jetpack_portfolio', |
||
| 594 | 'jetpack_portfolio_posts_per_page', |
||
| 595 | 'jetpack_testimonial', |
||
| 596 | 'jetpack_testimonial_posts_per_page', |
||
| 597 | 'sharedaddy_disable_resources', |
||
| 598 | 'sharing-options', |
||
| 599 | 'sharing-services', |
||
| 600 | 'site_icon_temp_data', |
||
| 601 | 'featured-content', |
||
| 602 | 'site_logo', |
||
| 603 | 'jetpack_dismissed_notices', |
||
| 604 | 'jetpack-twitter-cards-site-tag', |
||
| 605 | 'jetpack-sitemap-state', |
||
| 606 | 'jetpack_sitemap_post_types', |
||
| 607 | 'jetpack_sitemap_location', |
||
| 608 | 'jetpack_protect_key', |
||
| 609 | 'jetpack_protect_blocked_attempts', |
||
| 610 | 'jetpack_protect_activating', |
||
| 611 | 'jetpack_connection_banner_ab', |
||
| 612 | 'jetpack_active_plan', |
||
| 613 | 'jetpack_activation_source', |
||
| 614 | 'jetpack_site_products', |
||
| 615 | 'jetpack_sso_match_by_email', |
||
| 616 | 'jetpack_sso_require_two_step', |
||
| 617 | 'jetpack_sso_remove_login_form', |
||
| 618 | 'jetpack_last_connect_url_check', |
||
| 619 | 'jpo_business_address', |
||
| 620 | 'jpo_site_type', |
||
| 621 | 'jpo_homepage_format', |
||
| 622 | 'jpo_contact_page', |
||
| 623 | 'jetpack_excluded_extensions', |
||
| 624 | ); |
||
| 625 | } |
||
| 626 | |||
| 627 | /** |
||
| 628 | * Gets all options that can be safely reset by CLI. |
||
| 629 | * |
||
| 630 | * @since 5.4.0 |
||
| 631 | * |
||
| 632 | * @return array array Associative array containing jp_options which are managed by the Jetpack_Options class and wp_options which are not. |
||
| 633 | */ |
||
| 634 | public static function get_options_for_reset() { |
||
| 646 | |||
| 647 | /** |
||
| 648 | * Delete all known options |
||
| 649 | * |
||
| 650 | * @since 5.4.0 |
||
| 651 | * |
||
| 652 | * @return void |
||
| 653 | */ |
||
| 654 | public static function delete_all_known_options() { |
||
| 670 | } |
||
| 671 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.