Completed
Push — gm-17/payment-widget ( cb2702...55e70e )
by
unknown
13:32 queued 03:19
created

Jetpack_CLI   D

Complexity

Total Complexity 185

Size/Duplication

Total Lines 1061
Duplicated Lines 10.08 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
dl 107
loc 1061
rs 4.4229
c 0
b 0
f 0
wmc 185
lcom 2
cbo 6

13 Methods

Rating   Name   Duplication   Size   Complexity  
C status() 3 62 11
B test_connection() 0 30 5
D disconnect() 0 52 13
B reset() 8 55 8
F module() 17 67 22
C sync_queue() 0 51 8
F protect() 7 104 20
F options() 11 97 25
F sync() 37 109 25
C partner_cancel() 3 43 8
A get_api_host() 0 4 2
A partner_provision_error() 0 8 1
F partner_provision() 21 162 37

How to fix   Duplicated Code    Complexity   

Duplicated Code

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 Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_CLI 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_CLI, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
WP_CLI::add_command( 'jetpack', 'Jetpack_CLI' );
4
5
/**
6
 * Control your local Jetpack installation.
7
 */
8
class Jetpack_CLI extends WP_CLI_Command {
9
10
	// Aesthetics
11
	public $green_open  = "\033[32m";
12
	public $red_open    = "\033[31m";
13
	public $yellow_open = "\033[33m";
14
	public $color_close = "\033[0m";
15
16
	/**
17
	 * Get Jetpack Details
18
	 *
19
	 * ## OPTIONS
20
	 *
21
	 * empty: Leave it empty for basic stats
22
	 *
23
	 * full: View full stats.  It's the data from the heartbeat
24
	 *
25
	 * ## EXAMPLES
26
	 *
27
	 * wp jetpack status
28
	 * wp jetpack status full
29
	 *
30
	 */
31
	public function status( $args, $assoc_args ) {
32
		if ( ! Jetpack::is_active() ) {
33
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
34
		}
35
36 View Code Duplication
		if ( isset( $args[0] ) && 'full' !== $args[0] ) {
37
			/* translators: %s is a command like "prompt" */
38
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $args[0] ) );
39
		}
40
41
		$master_user_email = Jetpack::get_master_user_email();
42
43
		/*
44
		 * Are they asking for all data?
45
		 *
46
		 * Loop through heartbeat data and organize by priority.
47
		 */
48
		$all_data = ( isset( $args[0] ) && 'full' == $args[0] ) ? 'full' : false;
49
		if ( $all_data ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $all_data of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
50
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
51
			WP_CLI::line( sprintf( __( "The Jetpack Version is %s", 'jetpack' ), JETPACK__VERSION ) );
52
			WP_CLI::line( sprintf( __( "The WordPress.com blog_id is %d", 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
53
			WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) );
54
55
			// Heartbeat data
56
			WP_CLI::line( "\n" . __( 'Additional data: ', 'jetpack' ) );
57
58
			// Get the filtered heartbeat data.
59
			// Filtered so we can color/list by severity
60
			$stats = Jetpack::jetpack_check_heartbeat_data();
61
62
			// Display red flags first
63
			foreach ( $stats['bad'] as $stat => $value ) {
64
				printf( "$this->red_open%-'.16s %s $this->color_close\n", $stat, $value );
65
			}
66
67
			// Display caution warnings next
68
			foreach ( $stats['caution'] as $stat => $value ) {
69
				printf( "$this->yellow_open%-'.16s %s $this->color_close\n", $stat, $value );
70
			}
71
72
			// The rest of the results are good!
73
			foreach ( $stats['good'] as $stat => $value ) {
74
75
				// Modules should get special spacing for aestetics
76
				if ( strpos( $stat, 'odule-' ) ) {
77
					printf( "%-'.30s %s\n", $stat, $value );
78
					usleep( 4000 ); // For dramatic effect lolz
79
					continue;
80
				}
81
				printf( "%-'.16s %s\n", $stat, $value );
82
				usleep( 4000 ); // For dramatic effect lolz
83
			}
84
		} else {
85
			// Just the basics
86
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
87
			WP_CLI::line( sprintf( __( 'The Jetpack Version is %s', 'jetpack' ), JETPACK__VERSION ) );
88
			WP_CLI::line( sprintf( __( 'The WordPress.com blog_id is %d', 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
89
			WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) );
90
			WP_CLI::line( "\n" . _x( "View full status with 'wp jetpack status full'", '"wp jetpack status full" is a command - do not translate', 'jetpack' ) );
91
		}
92
	}
93
94
	/**
95
	 * Tests the active connection
96
	 *
97
	 * Does a two-way test to verify that the local site can communicate with remote Jetpack/WP.com servers and that Jetpack/WP.com servers can talk to the local site.
98
	 *
99
	 * ## EXAMPLES
100
	 *
101
	 * wp jetpack test-connection
102
	 *
103
	 * @subcommand test-connection
104
	 */
105
	public function test_connection( $args, $assoc_args ) {
106
		if ( ! Jetpack::is_active() ) {
107
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
108
		}
109
110
		$response = Jetpack_Client::wpcom_json_api_request_as_blog(
111
			sprintf( '/jetpack-blogs/%d/test-connection', Jetpack_Options::get_option( 'id' ) ),
112
			Jetpack_Client::WPCOM_JSON_API_VERSION
113
		);
114
115
		if ( is_wp_error( $response ) ) {
116
			/* translators: %1$s is the error code, %2$s is the error message */
117
			WP_CLI::error( sprintf( __( 'Failed to test connection (#%1$s: %2$s)', 'jetpack' ), $response->get_error_code(), $response->get_error_message() ) );
118
		}
119
120
		$body = wp_remote_retrieve_body( $response );
121
		if ( ! $body ) {
122
			WP_CLI::error( __( 'Failed to test connection (empty response body)', 'jetpack' ) );
123
		}
124
125
		$result = json_decode( $body );
126
		$is_connected = (bool) $result->connected;
127
		$message = $result->message;
128
129
		if ( $is_connected ) {
130
			WP_CLI::success( $message );
131
		} else {
132
			WP_CLI::error( $message );
133
		}
134
	}
135
136
	/**
137
	 * Disconnect Jetpack Blogs or Users
138
	 *
139
	 * ## OPTIONS
140
	 *
141
	 * blog: Disconnect the entire blog.
142
	 *
143
	 * user <user_identifier>: Disconnect a specific user from WordPress.com.
144
	 *
145
	 * Please note, the primary account that the blog is connected
146
	 * to WordPress.com with cannot be disconnected without
147
	 * disconnecting the entire blog.
148
	 *
149
	 * ## EXAMPLES
150
	 *
151
	 * wp jetpack disconnect blog
152
	 * wp jetpack disconnect user 13
153
	 * wp jetpack disconnect user username
154
	 * wp jetpack disconnect user [email protected]
155
	 *
156
	 * @synopsis <blog|user> [<user_identifier>]
157
	 */
158
	public function disconnect( $args, $assoc_args ) {
159
		if ( ! Jetpack::is_active() ) {
160
			WP_CLI::error( __( 'You cannot disconnect, without having first connected.', 'jetpack' ) );
161
		}
162
163
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
164
		if ( ! in_array( $action, array( 'blog', 'user', 'prompt' ) ) ) {
165
			/* translators: %s is a command like "prompt" */
166
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
167
		}
168
169
		if ( in_array( $action, array( 'user' ) ) ) {
170
			if ( isset( $args[1] ) ) {
171
				$user_id = $args[1];
172
				if ( ctype_digit( $user_id ) ) {
173
					$field = 'id';
174
					$user_id = (int) $user_id;
175
				} elseif ( is_email( $user_id ) ) {
176
					$field = 'email';
177
					$user_id = sanitize_user( $user_id, true );
178
				} else {
179
					$field = 'login';
180
					$user_id = sanitize_user( $user_id, true );
181
				}
182
				if ( ! $user = get_user_by( $field, $user_id ) ) {
183
					WP_CLI::error( __( 'Please specify a valid user.', 'jetpack' ) );
184
				}
185
			} else {
186
				WP_CLI::error( __( 'Please specify a user by either ID, username, or email.', 'jetpack' ) );
187
			}
188
		}
189
190
		switch ( $action ) {
191
			case 'blog':
192
				Jetpack::log( 'disconnect' );
193
				Jetpack::disconnect();
194
				WP_CLI::success( __( 'Jetpack has been successfully disconnected.', 'jetpack' ) );
195
				break;
196
			case 'user':
197
				if ( Jetpack::unlink_user( $user->ID ) ) {
198
					Jetpack::log( 'unlink', $user->ID );
0 ignored issues
show
Bug introduced by
The variable $user does not seem to be defined for all execution paths leading up to this point.

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:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

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

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
199
					WP_CLI::success( __( 'User has been successfully disconnected.', 'jetpack' ) );
200
				} else {
201
					/* translators: %s is a username */
202
					WP_CLI::error( sprintf( __( "User %s could not be disconnected. Are you sure they're connected currently?", 'jetpack' ), "{$user->login} <{$user->email}>" ) );
203
				}
204
				break;
205
			case 'prompt':
206
				WP_CLI::error( __( 'Please specify if you would like to disconnect a blog or user.', 'jetpack' ) );
207
				break;
208
		}
209
	}
210
211
	/**
212
	 * Reset Jetpack options and settings to default
213
	 *
214
	 * ## OPTIONS
215
	 *
216
	 * modules: Resets modules to default state ( get_default_modules() )
217
	 *
218
	 * options: Resets all Jetpack options except:
219
	 *  - All private options (Blog token, user token, etc...)
220
	 *  - id (The Client ID/WP.com Blog ID of this site)
221
	 *  - master_user
222
	 *  - version
223
	 *  - activated
224
	 *
225
	 * ## EXAMPLES
226
	 *
227
	 * wp jetpack reset options
228
	 * wp jetpack reset modules
229
	 *
230
	 * @synopsis <modules|options>
231
	 */
232
	public function reset( $args, $assoc_args ) {
233
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
234 View Code Duplication
		if ( ! in_array( $action, array( 'options', 'modules' ) ) ) {
235
			/* translators: %s is a command like "prompt" */
236
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
237
		}
238
239
		// Are you sure?
240
		jetpack_cli_are_you_sure();
241
242
		switch ( $action ) {
243
			case 'options':
244
				$options_to_reset = Jetpack_Options::get_options_for_reset();
245
246
				// Reset the Jetpack options
247
				_e( "Resetting Jetpack Options...\n", "jetpack" );
248
				sleep(1); // Take a breath
249
				foreach ( $options_to_reset['jp_options'] as $option_to_reset ) {
250
					Jetpack_Options::delete_option( $option_to_reset );
251
					usleep( 100000 );
252
					/* translators: This is the result of an action. The option named %s was reset */
253
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
254
				}
255
256
				// Reset the WP options
257
				_e( "Resetting the jetpack options stored in wp_options...\n", "jetpack" );
258
				usleep( 500000 ); // Take a breath
259
				foreach ( $options_to_reset['wp_options'] as $option_to_reset ) {
260
					delete_option( $option_to_reset );
261
					usleep( 100000 );
262
					/* translators: This is the result of an action. The option named %s was reset */
263
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
264
				}
265
266
				// Reset to default modules
267
				_e( "Resetting default modules...\n", "jetpack" );
268
				usleep( 500000 ); // Take a breath
269
				$default_modules = Jetpack::get_default_modules();
270
				Jetpack::update_active_modules( $default_modules );
271
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
272
273
				// Jumpstart option is special
274
				Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
275
				WP_CLI::success( __( 'jumpstart option reset', 'jetpack' ) );
276
				break;
277 View Code Duplication
			case 'modules':
278
				$default_modules = Jetpack::get_default_modules();
279
				Jetpack::update_active_modules( $default_modules );
280
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
281
				break;
282
			case 'prompt':
283
				WP_CLI::error( __( 'Please specify if you would like to reset your options, or modules', 'jetpack' ) );
284
				break;
285
		}
286
	}
287
288
	/**
289
	 * Manage Jetpack Modules
290
	 *
291
	 * ## OPTIONS
292
	 *
293
	 * list          : View all available modules, and their status.
294
	 * activate all  : Activate all modules
295
	 * deactivate all: Deactivate all modules
296
	 *
297
	 * activate   <module_slug> : Activate a module.
298
	 * deactivate <module_slug> : Deactivate a module.
299
	 * toggle     <module_slug> : Toggle a module on or off.
300
	 *
301
	 * ## EXAMPLES
302
	 *
303
	 * wp jetpack module list
304
	 * wp jetpack module activate stats
305
	 * wp jetpack module deactivate stats
306
	 * wp jetpack module toggle stats
307
	 *
308
	 * wp jetpack module activate all
309
	 * wp jetpack module deactivate all
310
	 *
311
	 * @synopsis <list|activate|deactivate|toggle> [<module_name>]
312
	 */
313
	public function module( $args, $assoc_args ) {
314
		$action = isset( $args[0] ) ? $args[0] : 'list';
315
		if ( ! in_array( $action, array( 'list', 'activate', 'deactivate', 'toggle' ) ) ) {
316
			/* translators: %s is a command like "prompt" */
317
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
318
		}
319
		if ( in_array( $action, array( 'activate', 'deactivate', 'toggle' ) ) ) {
320
			if ( isset( $args[1] ) ) {
321
				$module_slug = $args[1];
322
				if ( 'all' !== $module_slug && ! Jetpack::is_module( $module_slug ) ) {
323
					WP_CLI::error( sprintf( __( '%s is not a valid module.', 'jetpack' ), $module_slug ) );
324
				}
325
				if ( 'toggle' == $action ) {
326
					$action = Jetpack::is_module_active( $module_slug ) ? 'deactivate' : 'activate';
327
				}
328
				// Bulk actions
329
				if ( 'all' == $args[1] ) {
330
					$action = ( 'deactivate' == $action ) ? 'deactivate_all' : 'activate_all';
331
				}
332
				// VaultPress needs to be handled elsewhere.
333
				if ( in_array( $action, array( 'activate', 'deactivate', 'toggle' ) ) && 'vaultpress' == $args[1] ) {
334
					WP_CLI::error( sprintf( _x( 'Please visit %s to configure your VaultPress subscription.', '%s is a website', 'jetpack' ), esc_url( 'https://vaultpress.com/jetpack/' ) ) );
335
				}
336
			} else {
337
				WP_CLI::line( __( 'Please specify a valid module.', 'jetpack' ) );
338
				$action = 'list';
339
			}
340
		}
341
		switch ( $action ) {
342
			case 'list':
343
				WP_CLI::line( __( 'Available Modules:', 'jetpack' ) );
344
				$modules = Jetpack::get_available_modules();
345
				sort( $modules );
346
				foreach( $modules as $module_slug ) {
347
					if ( 'vaultpress' == $module_slug ) {
348
						continue;
349
					}
350
					$active = Jetpack::is_module_active( $module_slug ) ? __( 'Active', 'jetpack' ) : __( 'Inactive', 'jetpack' );
351
					WP_CLI::line( "\t" . str_pad( $module_slug, 24 ) . $active );
352
				}
353
				break;
354 View Code Duplication
			case 'activate':
355
				$module = Jetpack::get_module( $module_slug );
0 ignored issues
show
Bug introduced by
The variable $module_slug does not seem to be defined for all execution paths leading up to this point.

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:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

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

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
356
				Jetpack::log( 'activate', $module_slug );
357
				Jetpack::activate_module( $module_slug, false, false );
358
				WP_CLI::success( sprintf( __( '%s has been activated.', 'jetpack' ), $module['name'] ) );
359
				break;
360 View Code Duplication
			case 'activate_all':
361
				$modules = Jetpack::get_available_modules();
362
				Jetpack::update_active_modules( $modules );
363
				WP_CLI::success( __( 'All modules activated!', 'jetpack' ) );
364
				break;
365 View Code Duplication
			case 'deactivate':
366
				$module = Jetpack::get_module( $module_slug );
367
				Jetpack::log( 'deactivate', $module_slug );
368
				Jetpack::deactivate_module( $module_slug );
369
				WP_CLI::success( sprintf( __( '%s has been deactivated.', 'jetpack' ), $module['name'] ) );
370
				break;
371
			case 'deactivate_all':
372
				Jetpack::delete_active_modules();
373
				WP_CLI::success( __( 'All modules deactivated!', 'jetpack' ) );
374
				break;
375
			case 'toggle':
376
				// Will never happen, should have been handled above and changed to activate or deactivate.
377
				break;
378
		}
379
	}
380
381
	/**
382
	 * Manage Protect Settings
383
	 *
384
	 * ## OPTIONS
385
	 *
386
	 * whitelist: Whitelist an IP address.  You can also read or clear the whitelist.
387
	 *
388
	 *
389
	 * ## EXAMPLES
390
	 *
391
	 * wp jetpack protect whitelist <ip address>
392
	 * wp jetpack protect whitelist list
393
	 * wp jetpack protect whitelist clear
394
	 *
395
	 * @synopsis <whitelist> [<ip|ip_low-ip_high|list|clear>]
396
	 */
397
	public function protect( $args, $assoc_args ) {
398
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
399
		if ( ! in_array( $action, array( 'whitelist' ) ) ) {
400
			/* translators: %s is a command like "prompt" */
401
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
402
		}
403
		// Check if module is active
404
		if ( ! Jetpack::is_module_active( __FUNCTION__ ) ) {
405
			WP_CLI::error( sprintf( _x( '%s is not active. You can activate it with "wp jetpack module activate %s"', '"wp jetpack module activate" is a command - do not translate', 'jetpack' ), __FUNCTION__, __FUNCTION__ ) );
406
		}
407
		if ( in_array( $action, array( 'whitelist' ) ) ) {
408
			if ( isset( $args[1] ) ) {
409
				$action = 'whitelist';
410
			} else {
411
				$action = 'prompt';
412
			}
413
		}
414
		switch ( $action ) {
415
			case 'whitelist':
416
				$whitelist         = array();
417
				$new_ip            = $args[1];
418
				$current_whitelist = get_site_option( 'jetpack_protect_whitelist', array() );
419
420
				// Build array of IPs that are already whitelisted.
421
				// Re-build manually instead of using jetpack_protect_format_whitelist() so we can easily get
422
				// low & high range params for jetpack_protect_ip_address_is_in_range();
423
				foreach( $current_whitelist as $whitelisted ) {
424
425
					// IP ranges
426
					if ( $whitelisted->range ) {
427
428
						// Is it already whitelisted?
429
						if ( jetpack_protect_ip_address_is_in_range( $new_ip, $whitelisted->range_low, $whitelisted->range_high ) ) {
430
							/* translators: %s is an IP address */
431
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
432
							break;
433
						}
434
						$whitelist[] = $whitelisted->range_low . " - " . $whitelisted->range_high;
435
436
					} else { // Individual IPs
437
438
						// Check if the IP is already whitelisted (single IP only)
439
						if ( $new_ip == $whitelisted->ip_address ) {
440
							/* translators: %s is an IP address */
441
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
442
							break;
443
						}
444
						$whitelist[] = $whitelisted->ip_address;
445
446
					}
447
				}
448
449
				/*
450
				 * List the whitelist
451
				 * Done here because it's easier to read the $whitelist array after it's been rebuilt
452
				 */
453
				if ( isset( $args[1] ) && 'list' == $args[1] ) {
454
					if ( ! empty( $whitelist ) ) {
455
						WP_CLI::success( __( 'Here are your whitelisted IPs:', 'jetpack' ) );
456
						foreach ( $whitelist as $ip ) {
457
							WP_CLI::line( "\t" . str_pad( $ip, 24 ) ) ;
458
						}
459
					} else {
460
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
461
					}
462
					break;
463
				}
464
465
				/*
466
				 * Clear the whitelist
467
				 */
468
				if ( isset( $args[1] ) && 'clear' == $args[1] ) {
469 View Code Duplication
					if ( ! empty( $whitelist ) ) {
470
						$whitelist = array();
471
						jetpack_protect_save_whitelist( $whitelist );
472
						WP_CLI::success( __( 'Cleared all whitelisted IPs', 'jetpack' ) );
473
					} else {
474
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
475
					}
476
					break;
477
				}
478
479
				// Append new IP to whitelist array
480
				array_push( $whitelist, $new_ip );
481
482
				// Save whitelist if there are no errors
483
				$result = jetpack_protect_save_whitelist( $whitelist );
484
				if ( is_wp_error( $result ) ) {
485
					WP_CLI::error( __( $result, 'jetpack' ) );
486
				}
487
488
				/* translators: %s is an IP address */
489
				WP_CLI::success( sprintf( __( '%s has been whitelisted.', 'jetpack' ), $new_ip ) );
490
				break;
491
			case 'prompt':
492
				WP_CLI::error(
493
					__( 'No command found.', 'jetpack' ) . "\n" .
494
					__( 'Please enter the IP address you want to whitelist.', 'jetpack' ) . "\n" .
495
					_x( 'You can save a range of IPs {low_range}-{high_range}. No spaces allowed.  (example: 1.1.1.1-2.2.2.2)', 'Instructions on how to whitelist IP ranges - low_range/high_range should be translated.', 'jetpack' ) . "\n" .
496
					_x( "You can also 'list' or 'clear' the whitelist.", "'list' and 'clear' are commands and should not be translated", 'jetpack' ) . "\n"
497
				);
498
				break;
499
		}
500
	}
501
502
	/**
503
	 * Manage Jetpack Options
504
	 *
505
	 * ## OPTIONS
506
	 *
507
	 * list   : List all jetpack options and their values
508
	 * delete : Delete an option
509
	 *          - can only delete options that are white listed.
510
	 * update : update an option
511
	 *          - can only update option strings
512
	 * get    : get the value of an option
513
	 *
514
	 * ## EXAMPLES
515
	 *
516
	 * wp jetpack options list
517
	 * wp jetpack options get    <option_name>
518
	 * wp jetpack options delete <option_name>
519
	 * wp jetpack options update <option_name> [<option_value>]
520
	 *
521
	 * @synopsis <list|get|delete|update> [<option_name>] [<option_value>]
522
	 */
523
	public function options( $args, $assoc_args ) {
524
		$action = isset( $args[0] ) ? $args[0] : 'list';
525
		$safe_to_modify = Jetpack_Options::get_options_for_reset();
526
527
		// Jumpstart is special
528
		array_push( $safe_to_modify, 'jumpstart' );
529
530
		// Is the option flagged as unsafe?
531
		$flagged = ! in_array( $args[1], $safe_to_modify );
532
533 View Code Duplication
		if ( ! in_array( $action, array( 'list', 'get', 'delete', 'update' ) ) ) {
534
			/* translators: %s is a command like "prompt" */
535
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
536
		}
537
538
		if ( isset( $args[0] ) ) {
539
			if ( 'get' == $args[0] && isset( $args[1] ) ) {
540
				$action = 'get';
541
			} else if ( 'delete' == $args[0] && isset( $args[1] ) ) {
542
				$action = 'delete';
543 View Code Duplication
			} else if ( 'update' == $args[0] && isset( $args[1] ) ) {
544
				$action = 'update';
545
			} else {
546
				$action = 'list';
547
			}
548
		}
549
550
		// Bail if the option isn't found
551
		$option = isset( $args[1] ) ? Jetpack_Options::get_option( $args[1] ) : false;
552 View Code Duplication
		if ( isset( $args[1] ) && ! $option && 'update' !== $args[0] ) {
553
			WP_CLI::error( __( 'Option not found or is empty.  Use "list" to list option names', 'jetpack' ) );
554
		}
555
556
		// Let's print_r the option if it's an array
557
		// Used in the 'get' and 'list' actions
558
		$option = is_array( $option ) ? print_r( $option ) : $option;
559
560
		switch ( $action ) {
561
			case 'get':
562
				WP_CLI::success( "\t" . $option );
563
				break;
564
			case 'delete':
565
				jetpack_cli_are_you_sure( $flagged );
566
567
				Jetpack_Options::delete_option( $args[1] );
568
				WP_CLI::success( sprintf( __( 'Deleted option: %s', 'jetpack' ), $args[1] ) );
569
				break;
570
			case 'update':
571
				jetpack_cli_are_you_sure( $flagged );
572
573
				// Updating arrays would get pretty tricky...
574
				$value = Jetpack_Options::get_option( $args[1] );
575
				if ( $value && is_array( $value ) ) {
576
					WP_CLI::error( __( 'Sorry, no updating arrays at this time', 'jetpack' ) );
577
				}
578
579
				Jetpack_Options::update_option( $args[1], $args[2] );
580
				WP_CLI::success( sprintf( _x( 'Updated option: %s to "%s"', 'Updating an option from "this" to "that".', 'jetpack' ), $args[1], $args[2] ) );
581
				break;
582
			case 'list':
583
				$options_compact     = Jetpack_Options::get_option_names();
584
				$options_non_compact = Jetpack_Options::get_option_names( 'non_compact' );
585
				$options_private     = Jetpack_Options::get_option_names( 'private' );
586
				$options             = array_merge( $options_compact, $options_non_compact, $options_private );
587
588
				// Table headers
589
				WP_CLI::line( "\t" . str_pad( __( 'Option', 'jetpack' ), 30 ) . __( 'Value', 'jetpack' ) );
590
591
				// List out the options and their values
592
				// Tell them if the value is empty or not
593
				// Tell them if it's an array
594
				foreach ( $options as $option ) {
595
					$value = Jetpack_Options::get_option( $option );
596
					if ( ! $value ) {
597
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Empty' );
598
						continue;
599
					}
600
601
					if ( ! is_array( $value ) ) {
602
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . $value );
603
					} else if ( is_array( $value ) ) {
604
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Array - Use "get <option>" to read option array.' );
605
					}
606
				}
607
				$option_text = '{' . _x( 'option', 'a variable command that a user can write, provided in the printed instructions', 'jetpack' ) . '}';
608
				$value_text  = '{' . _x( 'value', 'the value that they want to update the option to', 'jetpack' ) . '}';
609
610
				WP_CLI::success(
611
					_x( "Above are your options. You may 'get', 'delete', and 'update' them.", "'get', 'delete', and 'update' are commands - do not translate.", 'jetpack' ) . "\n" .
612
					str_pad( 'wp jetpack options get', 26 )    . $option_text . "\n" .
613
					str_pad( 'wp jetpack options delete', 26 ) . $option_text . "\n" .
614
					str_pad( 'wp jetpack options update', 26 ) . "$option_text $value_text" . "\n" .
615
					_x( "Type 'wp jetpack options' for more info.", "'wp jetpack options' is a command - do not translate.", 'jetpack' ) . "\n"
616
				);
617
				break;
618
		}
619
	}
620
621
	/**
622
	 * Get the status of or start a new Jetpack sync.
623
	 *
624
	 * ## OPTIONS
625
	 *
626
	 * status : Print the current sync status
627
	 * start  : Start a full sync from this site to WordPress.com
628
	 *
629
	 * ## EXAMPLES
630
	 *
631
	 * wp jetpack sync status
632
	 * wp jetpack sync start --modules=functions --sync_wait_time=5
633
	 *
634
	 * @synopsis <status|start> [--<field>=<value>]
635
	 */
636
	public function sync( $args, $assoc_args ) {
637
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
638
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
639
		}
640
641
		$action = isset( $args[0] ) ? $args[0] : 'status';
642
643
		switch ( $action ) {
644
			case 'status':
645
				$status = Jetpack_Sync_Actions::get_sync_status();
646
				$collection = array();
647
				foreach ( $status as $key => $item ) {
648
					$collection[]  = array(
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
649
						'option' => $key,
650
						'value' => is_scalar( $item ) ? $item : json_encode( $item )
651
					);
652
				}
653
654
				WP_CLI\Utils\format_items( 'table', $collection, array( 'option', 'value' ) );
655
				break;
656
			case 'start':
657
				// Get the original settings so that we can restore them later
658
				$original_settings = Jetpack_Sync_Settings::get_settings();
659
660
				// Initialize sync settigns so we can sync as quickly as possible
661
				$sync_settings = wp_parse_args(
662
					array_intersect_key( $assoc_args, Jetpack_Sync_Settings::$valid_settings ),
0 ignored issues
show
Bug introduced by
The property valid_settings cannot be accessed from this context as it is declared private in class Jetpack_Sync_Settings.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
663
					array(
664
						'sync_wait_time' => 0,
665
						'enqueue_wait_time' => 0,
666
						'queue_max_writes_sec' => 10000,
667
						'max_queue_size_full_sync' => 100000
668
					)
669
				);
670
				Jetpack_Sync_Settings::update_settings( $sync_settings );
671
672
				// Convert comma-delimited string of modules to an array
673 View Code Duplication
				if ( ! empty( $assoc_args['modules'] ) ) {
674
					$modules = array_map( 'trim', explode( ',', $assoc_args['modules'] ) );
675
676
					// Convert the array so that the keys are the module name and the value is true to indicate
677
					// that we want to sync the module
678
					$modules = array_map( '__return_true', array_flip( $modules ) );
679
				}
680
681 View Code Duplication
				foreach ( array( 'posts', 'comments', 'users' ) as $module_name ) {
682
					if (
683
						'users' === $module_name &&
684
						isset( $assoc_args[ $module_name ] ) &&
685
						'initial' === $assoc_args[ $module_name ]
686
					) {
687
						$modules[ 'users' ] = 'initial';
0 ignored issues
show
Bug introduced by
The variable $modules does not seem to be defined for all execution paths leading up to this point.

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:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

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

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
688
					} elseif ( isset( $assoc_args[ $module_name ] ) ) {
689
						$ids = explode( ',', $assoc_args[ $module_name ] );
690
						if ( count( $ids ) > 0 ) {
691
							$modules[ $module_name ] = $ids;
692
						}
693
					}
694
				}
695
696
				if ( empty( $modules ) ) {
697
					$modules = null;
698
				}
699
700
				// Kick off a full sync
701
				if ( Jetpack_Sync_Actions::do_full_sync( $modules ) ) {
702 View Code Duplication
					if ( $modules ) {
703
						WP_CLI::log( sprintf( __( 'Initialized a new full sync with modules: %s', 'jetpack' ), join( ', ', array_keys( $modules ) ) ) );
704
					} else {
705
						WP_CLI::log( __( 'Initialized a new full sync', 'jetpack' ) );
706
					}
707 View Code Duplication
				} else {
708
709
					// Reset sync settings to original.
710
					Jetpack_Sync_Settings::update_settings( $original_settings );
711
712
					if ( $modules ) {
713
						WP_CLI::error( sprintf( __( 'Could not start a new full sync with modules: %s', 'jetpack' ), join( ', ', $modules ) ) );
714
					} else {
715
						WP_CLI::error( __( 'Could not start a new full sync', 'jetpack' ) );
716
					}
717
				}
718
719
				// Keep sending to WPCOM until there's nothing to send
720
				$i = 1;
721
				do {
722
					$result = Jetpack_Sync_Actions::$sender->do_full_sync();
0 ignored issues
show
Bug introduced by
The property sender cannot be accessed from this context as it is declared private in class Jetpack_Sync_Actions.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
723
					if ( is_wp_error( $result ) ) {
724
						$queue_empty_error = ( 'empty_queue_full_sync' == $result->get_error_code() );
725
						if ( ! $queue_empty_error || ( $queue_empty_error && ( 1 == $i ) ) ) {
726
							WP_CLI::error( sprintf( __( 'Sync errored with code: %s', 'jetpack' ), $result->get_error_code() ) );
727
						}
728
					} else {
729
						if ( 1 == $i ) {
730
							WP_CLI::log( __( 'Sent data to WordPress.com', 'jetpack' ) );
731
						} else {
732
							WP_CLI::log( __( 'Sent more data to WordPress.com', 'jetpack' ) );
733
						}
734
					}
735
					$i++;
736
				} while ( $result && ! is_wp_error( $result ) );
737
738
				// Reset sync settings to original.
739
				Jetpack_Sync_Settings::update_settings( $original_settings );
740
741
				WP_CLI::success( __( 'Finished syncing to WordPress.com', 'jetpack' ) );
742
				break;
743
		}
744
	}
745
746
	/**
747
	 * List the contents of a specific Jetpack sync queue.
748
	 *
749
	 * ## OPTIONS
750
	 *
751
	 * peek : List the 100 front-most items on the queue.
752
	 *
753
	 * ## EXAMPLES
754
	 *
755
	 * wp jetpack sync_queue full_sync peek
756
	 *
757
	 * @synopsis <incremental|full_sync> <peek>
758
	 */
759
	public function sync_queue( $args, $assoc_args ) {
760
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
761
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
762
		}
763
764
		$queue_name = isset( $args[0] ) ? $args[0] : 'sync';
765
		$action = isset( $args[1] ) ? $args[1] : 'peek';
766
767
		// We map the queue name that way we can support more friendly queue names in the commands, but still use
768
		// the queue name that the code expects.
769
		$queue_name_map = $allowed_queues = array(
0 ignored issues
show
Unused Code introduced by
$allowed_queues is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
770
			'incremental' => 'sync',
771
			'full'        => 'full_sync',
772
		);
773
		$mapped_queue_name = isset( $queue_name_map[ $queue_name ] ) ? $queue_name_map[ $queue_name ] : $queue_name;
774
775
		switch( $action ) {
776
			case 'peek':
777
				require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-queue.php';
778
				$queue = new Jetpack_Sync_Queue( $mapped_queue_name );
779
				$items = $queue->peek( 100 );
780
781
				if ( empty( $items ) ) {
782
					/* translators: %s is the name of the queue, either 'incremental' or 'full' */
783
					WP_CLI::log( sprintf( __( 'Nothing is in the queue: %s', 'jetpack' ), $queue_name  ) );
784
				} else {
785
					$collection = array();
786
					foreach ( $items as $item ) {
787
						$collection[] = array(
788
							'action'          => $item[0],
789
							'args'            => json_encode( $item[1] ),
790
							'current_user_id' => $item[2],
791
							'microtime'       => $item[3],
792
							'importing'       => (string) $item[4],
793
						);
794
					}
795
					WP_CLI\Utils\format_items(
796
						'table',
797
						$collection,
798
						array(
799
							'action',
800
							'args',
801
							'current_user_id',
802
							'microtime',
803
							'importing',
804
						)
805
					);
806
				}
807
				break;
808
		}
809
	}
810
811
	/**
812
	 * Cancel's the current Jetpack plan granted by this partner, if applicable
813
	 *
814
	 * Returns success or error JSON
815
	 *
816
	 * <token_json>
817
	 * : JSON blob of WPCOM API token
818
	 */
819
	public function partner_cancel( $args, $named_args ) {
820
		list( $token_json ) = $args;
821
822 View Code Duplication
		if ( ! $token_json || ! ( $token = json_decode( $token_json ) ) ) {
823
			$this->partner_provision_error( new WP_Error( 'missing_access_token',  sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
824
		}
825
826
		if ( isset( $token->error ) ) {
827
			$this->partner_provision_error( new WP_Error( $token->error, $token->message ) );
0 ignored issues
show
Bug introduced by
The variable $token does not seem to be defined for all execution paths leading up to this point.

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:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

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

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
828
		}
829
830
		if ( ! isset( $token->access_token ) ) {
831
			$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
832
		}
833
834
		$blog_id    = Jetpack_Options::get_option( 'id' );
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 4 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
835
836
		if ( ! $blog_id ) {
837
			$this->partner_provision_error( new WP_Error( 'site_not_registered',  __( 'This site is not connected to Jetpack', 'jetpack' ) ) );
838
		}
839
840
		$request = array(
841
			'headers' => array(
842
				'Authorization' => "Bearer " . $token->access_token,
843
				'Host'          => defined( 'JETPACK__WPCOM_JSON_API_HOST_HEADER' ) ? JETPACK__WPCOM_JSON_API_HOST_HEADER : 'public-api.wordpress.com',
844
			),
845
			'timeout' => 60,
846
			'method'  => 'POST',
847
			'body'    => json_encode( array( 'site_id' => $blog_id ) )
848
		);
849
850
		$url = sprintf( 'https://%s/rest/v1.3/jpphp/%d/partner-cancel', $this->get_api_host(), $blog_id );
851
852
		$result = Jetpack_Client::_wp_remote_request( $url, $request );
853
854
		Jetpack_Options::delete_option( 'onboarding' );
855
856
		if ( is_wp_error( $result ) ) {
857
			$this->partner_provision_error( $result );
858
		}
859
860
		WP_CLI::log( json_encode( $result ) );
861
	}
862
863
	/**
864
	 * Provision a site using a Jetpack Partner license
865
	 *
866
	 * Returns JSON blob
867
	 *
868
	 * ## OPTIONS
869
	 *
870
	 * <token_json>
871
	 * : JSON blob of WPCOM API token
872
	 * --user_id=<user_id>
873
	 * : Local ID of user to connect as (if omitted, user will be required to redirect via wp-admin)
874
	 * [--plan=<plan_name>]
875
	 * : Slug of the requested plan, e.g. premium
876
	 * [--wpcom_user_id=<user_id>]
877
	 * : WordPress.com ID of user to connect as (must be whitelisted against partner key)
878
	 * [--onboarding=<onboarding>]
879
	 * : Guide the user through an onboarding wizard
880
	 * [--force_register=<register>]
881
	 * : Whether to force a site to register
882
	 * [--force_connect=<force_connect>]
883
	 * : Force JPS to not reuse existing credentials
884
	 *
885
	 * ## EXAMPLES
886
	 *
887
	 *     $ wp jetpack partner_provision '{ some: "json" }' premium 1
888
	 *     { success: true }
889
	 *
890
	 * @synopsis <token_json> [--wpcom_user_id=<user_id>] [--plan=<plan_name>] [--onboarding=<onboarding>] [--force_register=<register>] [--force_connect=<force_connect>]
891
	 */
892
	public function partner_provision( $args, $named_args ) {
893
		list( $token_json ) = $args;
894
895 View Code Duplication
		if ( ! $token_json || ! ( $token = json_decode( $token_json ) ) ) {
896
			$this->partner_provision_error( new WP_Error( 'missing_access_token',  sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
897
		}
898
899
		if ( isset( $token->error ) ) {
900
			$message = isset( $token->message )
0 ignored issues
show
Bug introduced by
The variable $token does not seem to be defined for all execution paths leading up to this point.

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:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

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

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
901
				? $token->message
902
				: '';
903
			$this->partner_provision_error( new WP_Error( $token->error, $message ) );
904
		}
905
906
		if ( ! isset( $token->access_token ) ) {
907
			$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
908
		}
909
910
		$blog_id    = Jetpack_Options::get_option( 'id' );
911
		$blog_token = Jetpack_Options::get_option( 'blog_token' );
912
913
		if ( ! $blog_id || ! $blog_token || ( isset( $named_args['force_register'] ) && intval( $named_args['force_register'] ) ) ) {
914
			// this code mostly copied from Jetpack::admin_page_load
915
			Jetpack::maybe_set_version_option();
916
			$registered = Jetpack::try_registration();
917
			if ( is_wp_error( $registered ) ) {
918
				$this->partner_provision_error( $registered );
919
			} elseif ( ! $registered ) {
920
				$this->partner_provision_error( new WP_Error( 'registration_error', __( 'There was an unspecified error registering the site', 'jetpack' ) ) );
921
			}
922
923
			$blog_id    = Jetpack_Options::get_option( 'id' );
924
			$blog_token = Jetpack_Options::get_option( 'blog_token' );
0 ignored issues
show
Unused Code introduced by
$blog_token is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
925
		}
926
927
		// if the user isn't specified, but we have a current master user, then set that to current user
928
		if ( ! get_current_user_id() && $master_user_id = Jetpack_Options::get_option( 'master_user' ) ) {
929
			wp_set_current_user( $master_user_id );
930
		}
931
932
		$site_icon = ( function_exists( 'has_site_icon') && has_site_icon() )
933
			? get_site_icon_url()
934
			: false;
935
936
		/** This filter is documented in class.jetpack-cli.php */
937
		if ( apply_filters( 'jetpack_start_enable_sso', true ) ) {
938
			$redirect_uri = add_query_arg(
939
				array( 'action' => 'jetpack-sso', 'redirect_to' => urlencode( admin_url() ) ),
940
				wp_login_url() // TODO: come back to Jetpack dashboard?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
941
			);
942
		} else {
943
			$redirect_uri = admin_url();
944
		}
945
946
		$request_body = array(
947
			'jp_version'    => JETPACK__VERSION,
948
			'redirect_uri'  => $redirect_uri
949
		);
950
951
		if ( $site_icon ) {
952
			$request_body['site_icon'] = $site_icon;
953
		}
954
955
		if ( get_current_user_id() ) {
956
			$user = wp_get_current_user();
957
958
			// role
959
			$role = Jetpack::translate_current_user_to_role();
960
			$signed_role = Jetpack::sign_role( $role );
961
962
			$secrets = Jetpack::init()->generate_secrets( 'authorize' );
963
964
			// Jetpack auth stuff
965
			$request_body['scope']  = $signed_role;
966
			$request_body['secret'] = $secrets['secret_1'];
967
968
			// User stuff
969
			$request_body['user_id']    = $user->ID;
970
			$request_body['user_email'] = $user->user_email;
971
			$request_body['user_login'] = $user->user_login;
972
		}
973
974
		// optional additional params
975 View Code Duplication
		if ( isset( $named_args['wpcom_user_id'] ) && ! empty( $named_args['wpcom_user_id'] ) ) {
976
			$request_body['wpcom_user_id'] = $named_args['wpcom_user_id'];
977
		}
978
979 View Code Duplication
		if ( isset( $named_args['plan'] ) && ! empty( $named_args['plan'] ) ) {
980
			$request_body['plan'] = $named_args['plan'];
981
		}
982
983 View Code Duplication
		if ( isset( $named_args['onboarding'] ) && ! empty( $named_args['onboarding'] ) ) {
984
			$request_body['onboarding'] = intval( $named_args['onboarding'] );
985
		}
986
987 View Code Duplication
		if ( isset( $named_args['force_connect'] ) && ! empty( $named_args['force_connect'] ) ) {
988
			$request_body['force_connect'] = intval( $named_args['force_connect'] );
989
		}
990
991
		if ( isset( $request_body['onboarding'] ) && (bool) $request_body['onboarding'] ) {
992
			Jetpack::create_onboarding_token();
993
		}
994
995
		$request = array(
996
			'headers' => array(
997
				'Authorization' => "Bearer " . $token->access_token,
998
				'Host'          => defined( 'JETPACK__WPCOM_JSON_API_HOST_HEADER' ) ? JETPACK__WPCOM_JSON_API_HOST_HEADER : 'public-api.wordpress.com',
999
			),
1000
			'timeout' => 60,
1001
			'method'  => 'POST',
1002
			'body'    => json_encode( $request_body )
1003
		);
1004
1005
		$url = sprintf( 'https://%s/rest/v1.3/jpphp/%d/partner-provision', $this->get_api_host(), $blog_id );
1006
1007
		// add calypso env if set
1008
		if ( getenv( 'CALYPSO_ENV' ) ) {
1009
			$url = add_query_arg( array( 'calypso_env' => getenv( 'CALYPSO_ENV' ) ), $url );
1010
		}
1011
1012
		$result = Jetpack_Client::_wp_remote_request( $url, $request );
1013
1014
		if ( is_wp_error( $result ) ) {
1015
			$this->partner_provision_error( $result );
1016
		}
1017
1018
		$response_code = wp_remote_retrieve_response_code( $result );
1019
		$body_json     = json_decode( wp_remote_retrieve_body( $result ) );
1020
1021
		if( 200 !== $response_code ) {
1022
			if ( isset( $body_json->error ) ) {
1023
				$this->partner_provision_error( new WP_Error( $body_json->error, $body_json->message ) );
1024
			} else {
1025
				$this->partner_provision_error( new WP_Error( 'server_error', sprintf( __( "Request failed with code %s" ), $response_code ) ) );
1026
			}
1027
		}
1028
1029
		if ( isset( $body_json->access_token ) ) {
1030
			// authorize user and enable SSO
1031
			Jetpack::update_user_token( $user->ID, sprintf( '%s.%d', $body_json->access_token, $user->ID ), true );
0 ignored issues
show
Bug introduced by
The variable $user does not seem to be defined for all execution paths leading up to this point.

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:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

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

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1032
1033 View Code Duplication
			if ( $active_modules = Jetpack_Options::get_option( 'active_modules' ) ) {
1034
				Jetpack::delete_active_modules();
1035
				Jetpack::activate_default_modules( 999, 1, $active_modules, false );
0 ignored issues
show
Documentation introduced by
999 is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1036
			} else {
1037
				Jetpack::activate_default_modules( false, false, array(), false );
1038
			}
1039
1040
			/**
1041
			 * Auto-enable SSO module for new Jetpack Start connections
1042
			 *
1043
			 * @since 5.0.0
1044
			 *
1045
			 * @param bool $enable_sso Whether to enable the SSO module. Default to true.
1046
			 */
1047
			if ( apply_filters( 'jetpack_start_enable_sso', true ) ) {
1048
				Jetpack::activate_module( 'sso', false, false );
1049
			}
1050
		}
1051
1052
		WP_CLI::log( json_encode( $body_json ) );
1053
	}
1054
1055
	private function get_api_host() {
1056
		$env_api_host = getenv( 'JETPACK_START_API_HOST', true );
1057
		return $env_api_host ? $env_api_host : JETPACK__WPCOM_JSON_API_HOST;
1058
	}
1059
1060
	private function partner_provision_error( $error ) {
1061
		WP_CLI::log( json_encode( array(
1062
			'success'       => false,
1063
			'error_code'    => $error->get_error_code(),
1064
			'error_message' => $error->get_error_message()
1065
		) ) );
1066
		exit( 1 );
0 ignored issues
show
Coding Style Compatibility introduced by
The method partner_provision_error() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1067
	}
1068
}
1069
1070
/*
1071
 * Standard "ask for permission to continue" function.
1072
 * If action cancelled, ask if they need help.
1073
 *
1074
 * Written outside of the class so it's not listed as an executable command w/ 'wp jetpack'
1075
 *
1076
 * @param $flagged   bool   false = normal option | true = flagged by get_jetpack_options_for_reset()
1077
 * @param $error_msg string (optional)
1078
 */
1079
function jetpack_cli_are_you_sure( $flagged = false, $error_msg = false ) {
1080
	$cli = new Jetpack_CLI();
1081
1082
	// Default cancellation message
1083
	if ( ! $error_msg ) {
1084
		$error_msg =
1085
			__( 'Action cancelled. Have a question?', 'jetpack' )
1086
			. ' '
1087
			. $cli->green_open
1088
			. 'jetpack.com/support'
1089
			.  $cli->color_close;
1090
	}
1091
1092
	if ( ! $flagged ) {
1093
		$prompt_message = __( 'Are you sure? This cannot be undone. Type "yes" to continue:', '"yes" is a command.  Do not translate that.', 'jetpack' );
1094
	} else {
1095
		/* translators: Don't translate the word yes here. */
1096
		$prompt_message = __( 'Are you sure? Modifying this option may disrupt your Jetpack connection.  Type "yes" to continue.', 'jetpack' );
1097
	}
1098
1099
	WP_CLI::line( $prompt_message );
1100
	$handle = fopen( "php://stdin", "r" );
1101
	$line = fgets( $handle );
1102
	if ( 'yes' != trim( $line ) ){
1103
		WP_CLI::error( $error_msg );
1104
	}
1105
}
1106