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 Publicize 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 Publicize, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 3 | class Publicize extends Publicize_Base { |
||
| 4 | |||
| 5 | function __construct() { |
||
| 43 | |||
| 44 | function force_user_connection() { |
||
| 62 | |||
| 63 | function admin_page_warning() { |
||
| 91 | |||
| 92 | /** |
||
| 93 | * Remove a Publicize connection |
||
| 94 | */ |
||
| 95 | function disconnect( $service_name, $connection_id, $_blog_id = false, $_user_id = false, $force_delete = false ) { |
||
| 106 | |||
| 107 | function receive_updated_publicize_connections( $publicize_connections ) { |
||
| 112 | |||
| 113 | function register_update_publicize_connections_xmlrpc_method( $methods ) { |
||
| 118 | |||
| 119 | function get_connections( $service_name, $_blog_id = false, $_user_id = false ) { |
||
| 136 | |||
| 137 | function get_connection_id( $connection ) { |
||
| 140 | |||
| 141 | function get_connection_meta( $connection ) { |
||
| 145 | |||
| 146 | function admin_page_load() { |
||
| 147 | if ( isset( $_GET['action'] ) ) { |
||
| 148 | if ( isset( $_GET['service'] ) ) { |
||
| 149 | $service_name = $_GET['service']; |
||
| 150 | } |
||
| 151 | |||
| 152 | switch ( $_GET['action'] ) { |
||
| 153 | case 'error': |
||
| 154 | add_action( 'pre_admin_screen_sharing', array( $this, 'display_connection_error' ), 9 ); |
||
| 155 | break; |
||
| 156 | |||
| 157 | case 'request': |
||
| 158 | check_admin_referer( 'keyring-request', 'kr_nonce' ); |
||
| 159 | check_admin_referer( "keyring-request-$service_name", 'nonce' ); |
||
|
|
|||
| 160 | |||
| 161 | $verification = Jetpack::create_nonce( 'publicize' ); |
||
| 162 | if ( is_wp_error( $verification ) ) { |
||
| 163 | $url = Jetpack::admin_url( 'jetpack#/settings' ); |
||
| 164 | wp_die( sprintf( __( "Jetpack is not connected. Please connect Jetpack by visiting <a href='%s'>Settings</a>.", 'jetpack' ), $url ) ); |
||
| 165 | |||
| 166 | } |
||
| 167 | $stats_options = get_option( 'stats_options' ); |
||
| 168 | $wpcom_blog_id = Jetpack_Options::get_option( 'id' ); |
||
| 169 | $wpcom_blog_id = ! empty( $wpcom_blog_id ) ? $wpcom_blog_id : $stats_options['blog_id']; |
||
| 170 | |||
| 171 | $user = wp_get_current_user(); |
||
| 172 | $redirect = $this->api_url( $service_name, urlencode_deep( array( |
||
| 173 | 'action' => 'request', |
||
| 174 | 'redirect_uri' => add_query_arg( array( 'action' => 'done' ), menu_page_url( 'sharing', false ) ), |
||
| 175 | 'for' => 'publicize', |
||
| 176 | // required flag that says this connection is intended for publicize |
||
| 177 | 'siteurl' => site_url(), |
||
| 178 | 'state' => $user->ID, |
||
| 179 | 'blog_id' => $wpcom_blog_id, |
||
| 180 | 'secret_1' => $verification['secret_1'], |
||
| 181 | 'secret_2' => $verification['secret_2'], |
||
| 182 | 'eol' => $verification['eol'], |
||
| 183 | ) ) ); |
||
| 184 | wp_redirect( $redirect ); |
||
| 185 | exit; |
||
| 186 | break; |
||
| 187 | |||
| 188 | case 'completed': |
||
| 189 | Jetpack::load_xml_rpc_client(); |
||
| 190 | $xml = new Jetpack_IXR_Client(); |
||
| 191 | $xml->query( 'jetpack.fetchPublicizeConnections' ); |
||
| 192 | |||
| 193 | if ( ! $xml->isError() ) { |
||
| 194 | $response = $xml->getResponse(); |
||
| 195 | Jetpack_Options::update_option( 'publicize_connections', $response ); |
||
| 196 | } |
||
| 197 | |||
| 198 | break; |
||
| 199 | |||
| 200 | case 'delete': |
||
| 201 | $id = $_GET['id']; |
||
| 202 | |||
| 203 | check_admin_referer( 'keyring-request', 'kr_nonce' ); |
||
| 204 | check_admin_referer( "keyring-request-$service_name", 'nonce' ); |
||
| 205 | |||
| 206 | $this->disconnect( $service_name, $id ); |
||
| 207 | |||
| 208 | add_action( 'admin_notices', array( $this, 'display_disconnected' ) ); |
||
| 209 | break; |
||
| 210 | } |
||
| 211 | } |
||
| 212 | |||
| 213 | // Do we really need `admin_styles`? With the new admin UI, it's breaking some bits. |
||
| 214 | // Errors encountered on WordPress.com's end are passed back as a code |
||
| 215 | /* |
||
| 216 | if ( isset( $_GET['action'] ) && 'error' == $_GET['action'] ) { |
||
| 217 | // Load Jetpack's styles to handle the box |
||
| 218 | Jetpack::init()->admin_styles(); |
||
| 219 | } |
||
| 220 | */ |
||
| 221 | } |
||
| 222 | |||
| 223 | function display_connection_error() { |
||
| 224 | $code = false; |
||
| 225 | if ( isset( $_GET['service'] ) ) { |
||
| 226 | $service_name = $_GET['service']; |
||
| 227 | $error = sprintf( __( 'There was a problem connecting to %s to create an authorized connection. Please try again in a moment.', 'jetpack' ), Publicize::get_service_label( $service_name ) ); |
||
| 228 | } else { |
||
| 229 | if ( isset( $_GET['publicize_error'] ) ) { |
||
| 230 | $code = strtolower( $_GET['publicize_error'] ); |
||
| 231 | switch ( $code ) { |
||
| 232 | case '400': |
||
| 233 | $error = __( 'An invalid request was made. This normally means that something intercepted or corrupted the request from your server to the Jetpack Server. Try again and see if it works this time.', 'jetpack' ); |
||
| 234 | break; |
||
| 235 | case 'secret_mismatch': |
||
| 236 | $error = __( 'We could not verify that your server is making an authorized request. Please try again, and make sure there is nothing interfering with requests from your server to the Jetpack Server.', 'jetpack' ); |
||
| 237 | break; |
||
| 238 | case 'empty_blog_id': |
||
| 239 | $error = __( 'No blog_id was included in your request. Please try disconnecting Jetpack from WordPress.com and then reconnecting it. Once you have done that, try connecting Publicize again.', 'jetpack' ); |
||
| 240 | break; |
||
| 241 | case 'empty_state': |
||
| 242 | $error = sprintf( __( 'No user information was included in your request. Please make sure that your user account has connected to Jetpack. Connect your user account by going to the <a href="%s">Jetpack page</a> within wp-admin.', 'jetpack' ), Jetpack::admin_url() ); |
||
| 243 | break; |
||
| 244 | default: |
||
| 245 | $error = __( 'Something which should never happen, happened. Sorry about that. If you try again, maybe it will work.', 'jetpack' ); |
||
| 246 | break; |
||
| 247 | } |
||
| 248 | } else { |
||
| 249 | $error = __( 'There was a problem connecting with Publicize. Please try again in a moment.', 'jetpack' ); |
||
| 250 | } |
||
| 251 | } |
||
| 252 | // Using the same formatting/style as Jetpack::admin_notices() error |
||
| 253 | ?> |
||
| 254 | <div id="message" class="jetpack-message jetpack-err"> |
||
| 255 | <div class="squeezer"> |
||
| 256 | <h2><?php echo wp_kses( $error, array( 'a' => array( 'href' => true ), |
||
| 257 | 'code' => true, |
||
| 258 | 'strong' => true, |
||
| 259 | 'br' => true, |
||
| 260 | 'b' => true |
||
| 261 | ) ); ?></h2> |
||
| 262 | <?php if ( $code ) : ?> |
||
| 263 | <p><?php printf( __( 'Error code: %s', 'jetpack' ), esc_html( stripslashes( $code ) ) ); ?></p> |
||
| 264 | <?php endif; ?> |
||
| 265 | </div> |
||
| 266 | </div> |
||
| 267 | <?php |
||
| 268 | } |
||
| 269 | |||
| 270 | function display_disconnected() { |
||
| 271 | echo "<div class='updated'>\n"; |
||
| 272 | echo '<p>' . esc_html( __( 'That connection has been removed.', 'jetpack' ) ) . "</p>\n"; |
||
| 273 | echo "</div>\n\n"; |
||
| 274 | } |
||
| 275 | |||
| 276 | function globalization() { |
||
| 277 | if ( 'on' == $_REQUEST['global'] ) { |
||
| 278 | $id = $_REQUEST['connection']; |
||
| 279 | |||
| 280 | if ( ! current_user_can( $this->GLOBAL_CAP ) ) { |
||
| 281 | return; |
||
| 282 | } |
||
| 283 | |||
| 284 | Jetpack::load_xml_rpc_client(); |
||
| 285 | $xml = new Jetpack_IXR_Client(); |
||
| 286 | $xml->query( 'jetpack.globalizePublicizeConnection', $id, 'globalize' ); |
||
| 287 | |||
| 288 | if ( ! $xml->isError() ) { |
||
| 289 | $response = $xml->getResponse(); |
||
| 290 | Jetpack_Options::update_option( 'publicize_connections', $response ); |
||
| 291 | } |
||
| 292 | } |
||
| 293 | } |
||
| 294 | |||
| 295 | /** |
||
| 296 | * Gets a URL to the public-api actions. Works like WP's admin_url |
||
| 297 | * |
||
| 298 | * @param string $service Shortname of a specific service. |
||
| 299 | * |
||
| 300 | * @return URL to specific public-api process |
||
| 301 | */ |
||
| 302 | // on WordPress.com this is/calls Keyring::admin_url |
||
| 303 | function api_url( $service = false, $params = array() ) { |
||
| 304 | /** |
||
| 305 | * Filters the API URL used to interact with WordPress.com. |
||
| 306 | * |
||
| 307 | * @module publicize |
||
| 308 | * |
||
| 309 | * @since 2.0.0 |
||
| 310 | * |
||
| 311 | * @param string https://public-api.wordpress.com/connect/?jetpack=publicize Default Publicize API URL. |
||
| 312 | */ |
||
| 313 | $url = apply_filters( 'publicize_api_url', 'https://public-api.wordpress.com/connect/?jetpack=publicize' ); |
||
| 314 | |||
| 315 | if ( $service ) { |
||
| 316 | $url = add_query_arg( array( 'service' => $service ), $url ); |
||
| 317 | } |
||
| 318 | |||
| 319 | if ( count( $params ) ) { |
||
| 320 | $url = add_query_arg( $params, $url ); |
||
| 321 | } |
||
| 322 | |||
| 323 | return $url; |
||
| 324 | } |
||
| 325 | |||
| 326 | View Code Duplication | function connect_url( $service_name ) { |
|
| 327 | return add_query_arg( array( |
||
| 328 | 'action' => 'request', |
||
| 329 | 'service' => $service_name, |
||
| 330 | 'kr_nonce' => wp_create_nonce( 'keyring-request' ), |
||
| 331 | 'nonce' => wp_create_nonce( "keyring-request-$service_name" ), |
||
| 332 | ), menu_page_url( 'sharing', false ) ); |
||
| 333 | } |
||
| 334 | |||
| 335 | function refresh_url( $service_name ) { |
||
| 336 | return add_query_arg( array( |
||
| 337 | 'action' => 'request', |
||
| 338 | 'service' => $service_name, |
||
| 339 | 'kr_nonce' => wp_create_nonce( 'keyring-request' ), |
||
| 340 | 'refresh' => 1, |
||
| 341 | 'for' => 'publicize', |
||
| 342 | 'nonce' => wp_create_nonce( "keyring-request-$service_name" ), |
||
| 343 | ), admin_url( 'options-general.php?page=sharing' ) ); |
||
| 344 | } |
||
| 345 | |||
| 346 | View Code Duplication | function disconnect_url( $service_name, $id ) { |
|
| 347 | return add_query_arg( array( |
||
| 348 | 'action' => 'delete', |
||
| 349 | 'service' => $service_name, |
||
| 350 | 'id' => $id, |
||
| 351 | 'kr_nonce' => wp_create_nonce( 'keyring-request' ), |
||
| 352 | 'nonce' => wp_create_nonce( "keyring-request-$service_name" ), |
||
| 353 | ), menu_page_url( 'sharing', false ) ); |
||
| 354 | } |
||
| 355 | |||
| 356 | function get_services( $filter ) { |
||
| 357 | if ( ! in_array( $filter, array( 'all', 'connected' ) ) ) { |
||
| 358 | $filter = 'all'; |
||
| 359 | } |
||
| 360 | |||
| 361 | $services = array( |
||
| 362 | 'facebook' => array(), |
||
| 363 | 'twitter' => array(), |
||
| 364 | 'linkedin' => array(), |
||
| 365 | 'tumblr' => array(), |
||
| 366 | 'path' => array(), |
||
| 367 | 'google_plus' => array(), |
||
| 368 | ); |
||
| 369 | |||
| 370 | if ( 'all' == $filter ) { |
||
| 371 | return $services; |
||
| 372 | } else { |
||
| 373 | $connected_services = array(); |
||
| 374 | foreach ( $services as $service => $empty ) { |
||
| 375 | $connections = $this->get_connections( $service ); |
||
| 376 | if ( $connections ) { |
||
| 377 | $connected_services[ $service ] = $connections; |
||
| 378 | } |
||
| 379 | } |
||
| 380 | |||
| 381 | return $connected_services; |
||
| 382 | } |
||
| 383 | } |
||
| 384 | |||
| 385 | function get_connection( $service, $id, $_blog_id = false, $_user_id = false ) { |
||
| 386 | // Stub |
||
| 387 | } |
||
| 388 | |||
| 389 | function flag_post_for_publicize( $new_status, $old_status, $post ) { |
||
| 390 | if ( 'publish' == $new_status && 'publish' != $old_status ) { |
||
| 391 | /** |
||
| 392 | * Determines whether a post being published gets publicized. |
||
| 393 | * |
||
| 394 | * Side-note: Possibly our most alliterative filter name. |
||
| 395 | * |
||
| 396 | * @module publicize |
||
| 397 | * |
||
| 398 | * @since 4.1.0 |
||
| 399 | * |
||
| 400 | * @param bool $should_publicize Should the post be publicized? Default to true. |
||
| 401 | * @param WP_POST $post Current Post object. |
||
| 402 | */ |
||
| 403 | $should_publicize = apply_filters( 'publicize_should_publicize_published_post', true, $post ); |
||
| 404 | |||
| 405 | if ( $should_publicize ) { |
||
| 406 | update_post_meta( $post->ID, $this->PENDING, true ); |
||
| 407 | } |
||
| 408 | } |
||
| 409 | } |
||
| 410 | |||
| 411 | function test_connection( $service_name, $connection ) { |
||
| 412 | $connection_test_passed = true; |
||
| 413 | $connection_test_message = ''; |
||
| 414 | $user_can_refresh = false; |
||
| 415 | |||
| 416 | $id = $this->get_connection_id( $connection ); |
||
| 417 | |||
| 418 | Jetpack::load_xml_rpc_client(); |
||
| 419 | $xml = new Jetpack_IXR_Client(); |
||
| 420 | $xml->query( 'jetpack.testPublicizeConnection', $id ); |
||
| 421 | |||
| 422 | if ( $xml->isError() ) { |
||
| 423 | $xml_response = $xml->getResponse(); |
||
| 424 | $connection_test_message = $xml_response['faultString']; |
||
| 425 | $connection_test_passed = false; |
||
| 426 | } |
||
| 427 | |||
| 428 | // Bail if all is well |
||
| 429 | if ( $connection_test_passed ) { |
||
| 430 | return true; |
||
| 431 | } |
||
| 432 | |||
| 433 | // Set up refresh if the user can |
||
| 434 | $user_can_refresh = current_user_can( $this->GLOBAL_CAP ); |
||
| 435 | if ( $user_can_refresh ) { |
||
| 436 | $nonce = wp_create_nonce( "keyring-request-" . $service_name ); |
||
| 437 | $refresh_text = sprintf( _x( 'Refresh connection with %s', 'Refresh connection with {social media service}', 'jetpack' ), $this->get_service_label( $service_name ) ); |
||
| 438 | $refresh_url = $this->refresh_url( $service_name ); |
||
| 439 | } |
||
| 440 | |||
| 441 | $error_data = array( |
||
| 442 | 'user_can_refresh' => $user_can_refresh, |
||
| 443 | 'refresh_text' => $refresh_text, |
||
| 444 | 'refresh_url' => $refresh_url |
||
| 445 | ); |
||
| 446 | |||
| 447 | return new WP_Error( 'pub_conn_test_failed', $connection_test_message, $error_data ); |
||
| 448 | } |
||
| 449 | |||
| 450 | /** |
||
| 451 | * Save a flag locally to indicate that this post has already been Publicized via the selected |
||
| 452 | * connections. |
||
| 453 | */ |
||
| 454 | function save_publicized( $post_ID, $post, $update ) { |
||
| 455 | // Only do this when a post transitions to being published |
||
| 456 | if ( get_post_meta( $post->ID, $this->PENDING ) && $this->post_type_is_publicizeable( $post->post_type ) ) { |
||
| 457 | $connected_services = Jetpack_Options::get_option( 'publicize_connections' ); |
||
| 458 | if ( ! empty( $connected_services ) ) { |
||
| 459 | /** |
||
| 460 | * Fires when a post is saved that has is marked as pending publicizing |
||
| 461 | * |
||
| 462 | * @since 4.1.0 |
||
| 463 | * |
||
| 464 | * @param int The post ID |
||
| 465 | */ |
||
| 466 | do_action( 'jetpack_publicize_post', $post->ID ); |
||
| 467 | } |
||
| 468 | delete_post_meta( $post->ID, $this->PENDING ); |
||
| 469 | update_post_meta( $post->ID, $this->POST_DONE . 'all', true ); |
||
| 470 | } |
||
| 471 | } |
||
| 472 | |||
| 473 | function set_post_flags( $flags, $post ) { |
||
| 493 | |||
| 494 | /** |
||
| 495 | * Options Code |
||
| 496 | */ |
||
| 497 | |||
| 498 | function options_page_facebook() { |
||
| 613 | |||
| 614 | function options_save_facebook() { |
||
| 656 | |||
| 657 | function options_page_tumblr() { |
||
| 733 | |||
| 734 | function get_basehostname( $url ) { |
||
| 737 | |||
| 738 | function options_save_tumblr() { |
||
| 757 | |||
| 758 | function options_page_twitter() { |
||
| 761 | |||
| 762 | function options_page_linkedin() { |
||
| 765 | |||
| 766 | function options_page_path() { |
||
| 769 | |||
| 770 | function options_page_google_plus() { |
||
| 773 | |||
| 774 | function options_save_twitter() { |
||
| 777 | |||
| 778 | function options_save_linkedin() { |
||
| 781 | |||
| 782 | function options_save_path() { |
||
| 785 | |||
| 786 | function options_save_google_plus() { |
||
| 789 | |||
| 790 | function options_save_other( $service_name ) { |
||
| 795 | |||
| 796 | /** |
||
| 797 | * Already-published posts should not be Publicized by default. This filter sets checked to |
||
| 798 | * false if a post has already been published. |
||
| 799 | */ |
||
| 800 | function publicize_checkbox_default( $checked, $post_id, $name, $connection ) { |
||
| 807 | |||
| 808 | /** |
||
| 809 | * If there's only one shared connection to Twitter set it as twitter:site tag. |
||
| 810 | */ |
||
| 811 | function enhaced_twitter_cards_site_tag( $tag ) { |
||
| 830 | |||
| 831 | function save_publicized_twitter_account( $submit_post, $post_id, $service_name, $connection ) { |
||
| 840 | |||
| 841 | function get_publicized_twitter_account( $account, $post_id ) { |
||
| 852 | |||
| 853 | /** |
||
| 854 | * Save the Publicized Facebook account when publishing a post |
||
| 855 | * Use only Personal accounts, not Facebook Pages |
||
| 856 | */ |
||
| 857 | function save_publicized_facebook_account( $submit_post, $post_id, $service_name, $connection ) { |
||
| 870 | } |
||
| 871 |
If you define a variable conditionally, it can happen that it is not defined for all execution paths.
Let’s take a look at an example:
In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.
Available Fixes
Check for existence of the variable explicitly:
Define a default value for the variable:
Add a value for the missing path: