Completed
Push — add/cli-api-wrapper ( 35bdfa...680e4c )
by
unknown
09:53 queued 11s
created

Jetpack_CLI::get_stats()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
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
33
		WP_CLI::line( sprintf( __( 'Checking status for %s', 'jetpack' ), esc_url( get_site_url() ) ) );
34
35
		if ( ! Jetpack::is_active() ) {
36
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
37
		}
38
39 View Code Duplication
		if ( isset( $args[0] ) && 'full' !== $args[0] ) {
40
			/* translators: %s is a command like "prompt" */
41
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $args[0] ) );
42
		}
43
44
		$master_user_email = Jetpack::get_master_user_email();
45
46
		/*
47
		 * Are they asking for all data?
48
		 *
49
		 * Loop through heartbeat data and organize by priority.
50
		 */
51
		$all_data = ( isset( $args[0] ) && 'full' == $args[0] ) ? 'full' : false;
52
		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...
53
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
54
			WP_CLI::line( sprintf( __( "The Jetpack Version is %s", 'jetpack' ), JETPACK__VERSION ) );
55
			WP_CLI::line( sprintf( __( "The WordPress.com blog_id is %d", 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
56
			WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) );
57
58
			// Heartbeat data
59
			WP_CLI::line( "\n" . __( 'Additional data: ', 'jetpack' ) );
60
61
			// Get the filtered heartbeat data.
62
			// Filtered so we can color/list by severity
63
			$stats = Jetpack::jetpack_check_heartbeat_data();
64
65
			// Display red flags first
66
			foreach ( $stats['bad'] as $stat => $value ) {
67
				printf( "$this->red_open%-'.16s %s $this->color_close\n", $stat, $value );
68
			}
69
70
			// Display caution warnings next
71
			foreach ( $stats['caution'] as $stat => $value ) {
72
				printf( "$this->yellow_open%-'.16s %s $this->color_close\n", $stat, $value );
73
			}
74
75
			// The rest of the results are good!
76
			foreach ( $stats['good'] as $stat => $value ) {
77
78
				// Modules should get special spacing for aestetics
79
				if ( strpos( $stat, 'odule-' ) ) {
80
					printf( "%-'.30s %s\n", $stat, $value );
81
					usleep( 4000 ); // For dramatic effect lolz
82
					continue;
83
				}
84
				printf( "%-'.16s %s\n", $stat, $value );
85
				usleep( 4000 ); // For dramatic effect lolz
86
			}
87
		} else {
88
			// Just the basics
89
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
90
			WP_CLI::line( sprintf( __( 'The Jetpack Version is %s', 'jetpack' ), JETPACK__VERSION ) );
91
			WP_CLI::line( sprintf( __( 'The WordPress.com blog_id is %d', 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
92
			WP_CLI::line( sprintf( __( 'The WordPress.com account for the primary connection is %s', 'jetpack' ), $master_user_email ) );
93
			WP_CLI::line( "\n" . _x( "View full status with 'wp jetpack status full'", '"wp jetpack status full" is a command - do not translate', 'jetpack' ) );
94
		}
95
	}
96
97
	/**
98
	 * Tests the active connection
99
	 *
100
	 * 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.
101
	 *
102
	 * ## EXAMPLES
103
	 *
104
	 * wp jetpack test-connection
105
	 *
106
	 * @subcommand test-connection
107
	 */
108
	public function test_connection( $args, $assoc_args ) {
109
110
		WP_CLI::line( sprintf( __( 'Testing connection for %s', 'jetpack' ), esc_url( get_site_url() ) ) );
111
112
		if ( ! Jetpack::is_active() ) {
113
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
114
		}
115
116
		$response = Jetpack_Client::wpcom_json_api_request_as_blog(
117
			sprintf( '/jetpack-blogs/%d/test-connection', Jetpack_Options::get_option( 'id' ) ),
118
			Jetpack_Client::WPCOM_JSON_API_VERSION
119
		);
120
121 View Code Duplication
		if ( is_wp_error( $response ) ) {
122
			/* translators: %1$s is the error code, %2$s is the error message */
123
			WP_CLI::error( sprintf( __( 'Failed to test connection (#%1$s: %2$s)', 'jetpack' ), $response->get_error_code(), $response->get_error_message() ) );
124
		}
125
126
		$body = wp_remote_retrieve_body( $response );
127
		if ( ! $body ) {
128
			WP_CLI::error( __( 'Failed to test connection (empty response body)', 'jetpack' ) );
129
		}
130
131
		$result = json_decode( $body );
132
		$is_connected = (bool) $result->connected;
133
		$message = $result->message;
134
135
		if ( $is_connected ) {
136
			WP_CLI::success( $message );
137
		} else {
138
			WP_CLI::error( $message );
139
		}
140
	}
141
142
	/**
143
	 * Disconnect Jetpack Blogs or Users
144
	 *
145
	 * ## OPTIONS
146
	 *
147
	 * blog: Disconnect the entire blog.
148
	 *
149
	 * user <user_identifier>: Disconnect a specific user from WordPress.com.
150
	 *
151
	 * Please note, the primary account that the blog is connected
152
	 * to WordPress.com with cannot be disconnected without
153
	 * disconnecting the entire blog.
154
	 *
155
	 * ## EXAMPLES
156
	 *
157
	 * wp jetpack disconnect blog
158
	 * wp jetpack disconnect user 13
159
	 * wp jetpack disconnect user username
160
	 * wp jetpack disconnect user [email protected]
161
	 *
162
	 * @synopsis <blog|user> [<user_identifier>]
163
	 */
164
	public function disconnect( $args, $assoc_args ) {
165
		if ( ! Jetpack::is_active() ) {
166
			WP_CLI::error( __( 'You cannot disconnect, without having first connected.', 'jetpack' ) );
167
		}
168
169
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
170
		if ( ! in_array( $action, array( 'blog', 'user', 'prompt' ) ) ) {
171
			/* translators: %s is a command like "prompt" */
172
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
173
		}
174
175
		if ( in_array( $action, array( 'user' ) ) ) {
176
			if ( isset( $args[1] ) ) {
177
				$user_id = $args[1];
178
				if ( ctype_digit( $user_id ) ) {
179
					$field = 'id';
180
					$user_id = (int) $user_id;
181
				} elseif ( is_email( $user_id ) ) {
182
					$field = 'email';
183
					$user_id = sanitize_user( $user_id, true );
184
				} else {
185
					$field = 'login';
186
					$user_id = sanitize_user( $user_id, true );
187
				}
188
				if ( ! $user = get_user_by( $field, $user_id ) ) {
189
					WP_CLI::error( __( 'Please specify a valid user.', 'jetpack' ) );
190
				}
191
			} else {
192
				WP_CLI::error( __( 'Please specify a user by either ID, username, or email.', 'jetpack' ) );
193
			}
194
		}
195
196
		switch ( $action ) {
197
			case 'blog':
198
				Jetpack::log( 'disconnect' );
199
				Jetpack::disconnect();
200
				WP_CLI::success( sprintf(
201
					__( 'Jetpack has been successfully disconnected for %s.', 'jetpack' ),
202
					esc_url( get_site_url() )
203
				) );
204
				break;
205
			case 'user':
206
				if ( Jetpack::unlink_user( $user->ID ) ) {
207
					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...
208
					WP_CLI::success( __( 'User has been successfully disconnected.', 'jetpack' ) );
209
				} else {
210
					/* translators: %s is a username */
211
					WP_CLI::error( sprintf( __( "User %s could not be disconnected. Are you sure they're connected currently?", 'jetpack' ), "{$user->login} <{$user->email}>" ) );
212
				}
213
				break;
214
			case 'prompt':
215
				WP_CLI::error( __( 'Please specify if you would like to disconnect a blog or user.', 'jetpack' ) );
216
				break;
217
		}
218
	}
219
220
	/**
221
	 * Reset Jetpack options and settings to default
222
	 *
223
	 * ## OPTIONS
224
	 *
225
	 * modules: Resets modules to default state ( get_default_modules() )
226
	 *
227
	 * options: Resets all Jetpack options except:
228
	 *  - All private options (Blog token, user token, etc...)
229
	 *  - id (The Client ID/WP.com Blog ID of this site)
230
	 *  - master_user
231
	 *  - version
232
	 *  - activated
233
	 *
234
	 * ## EXAMPLES
235
	 *
236
	 * wp jetpack reset options
237
	 * wp jetpack reset modules
238
	 *
239
	 * @synopsis <modules|options>
240
	 */
241
	public function reset( $args, $assoc_args ) {
242
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
243 View Code Duplication
		if ( ! in_array( $action, array( 'options', 'modules' ) ) ) {
244
			/* translators: %s is a command like "prompt" */
245
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
246
		}
247
248
		// Are you sure?
249
		jetpack_cli_are_you_sure();
250
251
		switch ( $action ) {
252
			case 'options':
253
				$options_to_reset = Jetpack_Options::get_options_for_reset();
254
255
				// Reset the Jetpack options
256
				WP_CLI::line( sprintf(
257
					__( "Resetting Jetpack Options for %s...\n", "jetpack" ),
258
					esc_url( get_site_url() )
259
				) );
260
				sleep(1); // Take a breath
261
				foreach ( $options_to_reset['jp_options'] as $option_to_reset ) {
262
					Jetpack_Options::delete_option( $option_to_reset );
263
					usleep( 100000 );
264
					/* translators: This is the result of an action. The option named %s was reset */
265
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
266
				}
267
268
				// Reset the WP options
269
				WP_CLI::line( __( "Resetting the jetpack options stored in wp_options...\n", "jetpack" ) );
270
				usleep( 500000 ); // Take a breath
271
				foreach ( $options_to_reset['wp_options'] as $option_to_reset ) {
272
					delete_option( $option_to_reset );
273
					usleep( 100000 );
274
					/* translators: This is the result of an action. The option named %s was reset */
275
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
276
				}
277
278
				// Reset to default modules
279
				WP_CLI::line( __( "Resetting default modules...\n", "jetpack" ) );
280
				usleep( 500000 ); // Take a breath
281
				$default_modules = Jetpack::get_default_modules();
282
				Jetpack::update_active_modules( $default_modules );
283
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
284
285
				// Jumpstart option is special
286
				Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
287
				WP_CLI::success( __( 'jumpstart option reset', 'jetpack' ) );
288
				break;
289 View Code Duplication
			case 'modules':
290
				$default_modules = Jetpack::get_default_modules();
291
				Jetpack::update_active_modules( $default_modules );
292
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
293
				break;
294
			case 'prompt':
295
				WP_CLI::error( __( 'Please specify if you would like to reset your options, or modules', 'jetpack' ) );
296
				break;
297
		}
298
	}
299
300
	/**
301
	 * Manage Jetpack Modules
302
	 *
303
	 * ## OPTIONS
304
	 *
305
	 * list          : View all available modules, and their status.
306
	 * activate all  : Activate all modules
307
	 * deactivate all: Deactivate all modules
308
	 *
309
	 * activate   <module_slug> : Activate a module.
310
	 * deactivate <module_slug> : Deactivate a module.
311
	 * toggle     <module_slug> : Toggle a module on or off.
312
	 *
313
	 * ## EXAMPLES
314
	 *
315
	 * wp jetpack module list
316
	 * wp jetpack module activate stats
317
	 * wp jetpack module deactivate stats
318
	 * wp jetpack module toggle stats
319
	 *
320
	 * wp jetpack module activate all
321
	 * wp jetpack module deactivate all
322
	 *
323
	 * @synopsis <list|activate|deactivate|toggle> [<module_name>]
324
	 */
325
	public function module( $args, $assoc_args ) {
326
		$action = isset( $args[0] ) ? $args[0] : 'list';
327
		if ( ! in_array( $action, array( 'list', 'activate', 'deactivate', 'toggle' ) ) ) {
328
			/* translators: %s is a command like "prompt" */
329
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
330
		}
331
		if ( in_array( $action, array( 'activate', 'deactivate', 'toggle' ) ) ) {
332
			if ( isset( $args[1] ) ) {
333
				$module_slug = $args[1];
334
				if ( 'all' !== $module_slug && ! Jetpack::is_module( $module_slug ) ) {
335
					WP_CLI::error( sprintf( __( '%s is not a valid module.', 'jetpack' ), $module_slug ) );
336
				}
337
				if ( 'toggle' == $action ) {
338
					$action = Jetpack::is_module_active( $module_slug ) ? 'deactivate' : 'activate';
339
				}
340
				// Bulk actions
341
				if ( 'all' == $args[1] ) {
342
					$action = ( 'deactivate' == $action ) ? 'deactivate_all' : 'activate_all';
343
				}
344
			} else {
345
				WP_CLI::line( __( 'Please specify a valid module.', 'jetpack' ) );
346
				$action = 'list';
347
			}
348
		}
349
		switch ( $action ) {
350
			case 'list':
351
				WP_CLI::line( __( 'Available Modules:', 'jetpack' ) );
352
				$modules = Jetpack::get_available_modules();
353
				sort( $modules );
354
				foreach( $modules as $module_slug ) {
355
					if ( 'vaultpress' == $module_slug ) {
356
						continue;
357
					}
358
					$active = Jetpack::is_module_active( $module_slug ) ? __( 'Active', 'jetpack' ) : __( 'Inactive', 'jetpack' );
359
					WP_CLI::line( "\t" . str_pad( $module_slug, 24 ) . $active );
360
				}
361
				break;
362
			case 'activate':
363
				$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...
364
				Jetpack::log( 'activate', $module_slug );
365
				if ( Jetpack::activate_module( $module_slug, false, false ) ) {
366
					WP_CLI::success( sprintf( __( '%s has been activated.', 'jetpack' ), $module['name'] ) );
367
				} else {
368
					WP_CLI::error( sprintf( __( '%s could not be activated.', 'jetpack' ), $module['name'] ) );
369
				}
370
				break;
371 View Code Duplication
			case 'activate_all':
372
				$modules = Jetpack::get_available_modules();
373
				Jetpack::update_active_modules( $modules );
374
				WP_CLI::success( __( 'All modules activated!', 'jetpack' ) );
375
				break;
376
			case 'deactivate':
377
				$module = Jetpack::get_module( $module_slug );
378
				Jetpack::log( 'deactivate', $module_slug );
379
				Jetpack::deactivate_module( $module_slug );
380
				WP_CLI::success( sprintf( __( '%s has been deactivated.', 'jetpack' ), $module['name'] ) );
381
				break;
382
			case 'deactivate_all':
383
				Jetpack::delete_active_modules();
384
				WP_CLI::success( __( 'All modules deactivated!', 'jetpack' ) );
385
				break;
386
			case 'toggle':
387
				// Will never happen, should have been handled above and changed to activate or deactivate.
388
				break;
389
		}
390
	}
391
392
	/**
393
	 * Manage Protect Settings
394
	 *
395
	 * ## OPTIONS
396
	 *
397
	 * whitelist: Whitelist an IP address.  You can also read or clear the whitelist.
398
	 *
399
	 *
400
	 * ## EXAMPLES
401
	 *
402
	 * wp jetpack protect whitelist <ip address>
403
	 * wp jetpack protect whitelist list
404
	 * wp jetpack protect whitelist clear
405
	 *
406
	 * @synopsis <whitelist> [<ip|ip_low-ip_high|list|clear>]
407
	 */
408
	public function protect( $args, $assoc_args ) {
409
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
410
		if ( ! in_array( $action, array( 'whitelist' ) ) ) {
411
			/* translators: %s is a command like "prompt" */
412
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
413
		}
414
		// Check if module is active
415
		if ( ! Jetpack::is_module_active( __FUNCTION__ ) ) {
416
			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__ ) );
417
		}
418
		if ( in_array( $action, array( 'whitelist' ) ) ) {
419
			if ( isset( $args[1] ) ) {
420
				$action = 'whitelist';
421
			} else {
422
				$action = 'prompt';
423
			}
424
		}
425
		switch ( $action ) {
426
			case 'whitelist':
427
				$whitelist         = array();
428
				$new_ip            = $args[1];
429
				$current_whitelist = get_site_option( 'jetpack_protect_whitelist', array() );
430
431
				// Build array of IPs that are already whitelisted.
432
				// Re-build manually instead of using jetpack_protect_format_whitelist() so we can easily get
433
				// low & high range params for jetpack_protect_ip_address_is_in_range();
434
				foreach( $current_whitelist as $whitelisted ) {
435
436
					// IP ranges
437
					if ( $whitelisted->range ) {
438
439
						// Is it already whitelisted?
440
						if ( jetpack_protect_ip_address_is_in_range( $new_ip, $whitelisted->range_low, $whitelisted->range_high ) ) {
441
							/* translators: %s is an IP address */
442
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
443
							break;
444
						}
445
						$whitelist[] = $whitelisted->range_low . " - " . $whitelisted->range_high;
446
447
					} else { // Individual IPs
448
449
						// Check if the IP is already whitelisted (single IP only)
450
						if ( $new_ip == $whitelisted->ip_address ) {
451
							/* translators: %s is an IP address */
452
							WP_CLI::error( sprintf( __( '%s has already been whitelisted', 'jetpack' ), $new_ip ) );
453
							break;
454
						}
455
						$whitelist[] = $whitelisted->ip_address;
456
457
					}
458
				}
459
460
				/*
461
				 * List the whitelist
462
				 * Done here because it's easier to read the $whitelist array after it's been rebuilt
463
				 */
464
				if ( isset( $args[1] ) && 'list' == $args[1] ) {
465 View Code Duplication
					if ( ! empty( $whitelist ) ) {
466
						WP_CLI::success( __( 'Here are your whitelisted IPs:', 'jetpack' ) );
467
						foreach ( $whitelist as $ip ) {
468
							WP_CLI::line( "\t" . str_pad( $ip, 24 ) ) ;
469
						}
470
					} else {
471
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
472
					}
473
					break;
474
				}
475
476
				/*
477
				 * Clear the whitelist
478
				 */
479
				if ( isset( $args[1] ) && 'clear' == $args[1] ) {
480
					if ( ! empty( $whitelist ) ) {
481
						$whitelist = array();
482
						jetpack_protect_save_whitelist( $whitelist );
483
						WP_CLI::success( __( 'Cleared all whitelisted IPs', 'jetpack' ) );
484
					} else {
485
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
486
					}
487
					break;
488
				}
489
490
				// Append new IP to whitelist array
491
				array_push( $whitelist, $new_ip );
492
493
				// Save whitelist if there are no errors
494
				$result = jetpack_protect_save_whitelist( $whitelist );
495
				if ( is_wp_error( $result ) ) {
496
					WP_CLI::error( __( $result, 'jetpack' ) );
497
				}
498
499
				/* translators: %s is an IP address */
500
				WP_CLI::success( sprintf( __( '%s has been whitelisted.', 'jetpack' ), $new_ip ) );
501
				break;
502
			case 'prompt':
503
				WP_CLI::error(
504
					__( 'No command found.', 'jetpack' ) . "\n" .
505
					__( 'Please enter the IP address you want to whitelist.', 'jetpack' ) . "\n" .
506
					_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" .
507
					_x( "You can also 'list' or 'clear' the whitelist.", "'list' and 'clear' are commands and should not be translated", 'jetpack' ) . "\n"
508
				);
509
				break;
510
		}
511
	}
512
513
	/**
514
	 * Manage Jetpack Options
515
	 *
516
	 * ## OPTIONS
517
	 *
518
	 * list   : List all jetpack options and their values
519
	 * delete : Delete an option
520
	 *          - can only delete options that are white listed.
521
	 * update : update an option
522
	 *          - can only update option strings
523
	 * get    : get the value of an option
524
	 *
525
	 * ## EXAMPLES
526
	 *
527
	 * wp jetpack options list
528
	 * wp jetpack options get    <option_name>
529
	 * wp jetpack options delete <option_name>
530
	 * wp jetpack options update <option_name> [<option_value>]
531
	 *
532
	 * @synopsis <list|get|delete|update> [<option_name>] [<option_value>]
533
	 */
534
	public function options( $args, $assoc_args ) {
535
		$action = isset( $args[0] ) ? $args[0] : 'list';
536
		$safe_to_modify = Jetpack_Options::get_options_for_reset();
537
538
		// Jumpstart is special
539
		array_push( $safe_to_modify, 'jumpstart' );
540
541
		// Is the option flagged as unsafe?
542
		$flagged = ! in_array( $args[1], $safe_to_modify );
543
544 View Code Duplication
		if ( ! in_array( $action, array( 'list', 'get', 'delete', 'update' ) ) ) {
545
			/* translators: %s is a command like "prompt" */
546
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
547
		}
548
549
		if ( isset( $args[0] ) ) {
550
			if ( 'get' == $args[0] && isset( $args[1] ) ) {
551
				$action = 'get';
552
			} else if ( 'delete' == $args[0] && isset( $args[1] ) ) {
553
				$action = 'delete';
554 View Code Duplication
			} else if ( 'update' == $args[0] && isset( $args[1] ) ) {
555
				$action = 'update';
556
			} else {
557
				$action = 'list';
558
			}
559
		}
560
561
		// Bail if the option isn't found
562
		$option = isset( $args[1] ) ? Jetpack_Options::get_option( $args[1] ) : false;
563
		if ( isset( $args[1] ) && ! $option && 'update' !== $args[0] ) {
564
			WP_CLI::error( __( 'Option not found or is empty.  Use "list" to list option names', 'jetpack' ) );
565
		}
566
567
		// Let's print_r the option if it's an array
568
		// Used in the 'get' and 'list' actions
569
		$option = is_array( $option ) ? print_r( $option ) : $option;
570
571
		switch ( $action ) {
572
			case 'get':
573
				WP_CLI::success( "\t" . $option );
574
				break;
575
			case 'delete':
576
				jetpack_cli_are_you_sure( $flagged );
577
578
				Jetpack_Options::delete_option( $args[1] );
579
				WP_CLI::success( sprintf( __( 'Deleted option: %s', 'jetpack' ), $args[1] ) );
580
				break;
581
			case 'update':
582
				jetpack_cli_are_you_sure( $flagged );
583
584
				// Updating arrays would get pretty tricky...
585
				$value = Jetpack_Options::get_option( $args[1] );
586
				if ( $value && is_array( $value ) ) {
587
					WP_CLI::error( __( 'Sorry, no updating arrays at this time', 'jetpack' ) );
588
				}
589
590
				Jetpack_Options::update_option( $args[1], $args[2] );
591
				WP_CLI::success( sprintf( _x( 'Updated option: %s to "%s"', 'Updating an option from "this" to "that".', 'jetpack' ), $args[1], $args[2] ) );
592
				break;
593
			case 'list':
594
				$options_compact     = Jetpack_Options::get_option_names();
595
				$options_non_compact = Jetpack_Options::get_option_names( 'non_compact' );
596
				$options_private     = Jetpack_Options::get_option_names( 'private' );
597
				$options             = array_merge( $options_compact, $options_non_compact, $options_private );
598
599
				// Table headers
600
				WP_CLI::line( "\t" . str_pad( __( 'Option', 'jetpack' ), 30 ) . __( 'Value', 'jetpack' ) );
601
602
				// List out the options and their values
603
				// Tell them if the value is empty or not
604
				// Tell them if it's an array
605
				foreach ( $options as $option ) {
606
					$value = Jetpack_Options::get_option( $option );
607
					if ( ! $value ) {
608
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Empty' );
609
						continue;
610
					}
611
612
					if ( ! is_array( $value ) ) {
613
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . $value );
614
					} else if ( is_array( $value ) ) {
615
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Array - Use "get <option>" to read option array.' );
616
					}
617
				}
618
				$option_text = '{' . _x( 'option', 'a variable command that a user can write, provided in the printed instructions', 'jetpack' ) . '}';
619
				$value_text  = '{' . _x( 'value', 'the value that they want to update the option to', 'jetpack' ) . '}';
620
621
				WP_CLI::success(
622
					_x( "Above are your options. You may 'get', 'delete', and 'update' them.", "'get', 'delete', and 'update' are commands - do not translate.", 'jetpack' ) . "\n" .
623
					str_pad( 'wp jetpack options get', 26 )    . $option_text . "\n" .
624
					str_pad( 'wp jetpack options delete', 26 ) . $option_text . "\n" .
625
					str_pad( 'wp jetpack options update', 26 ) . "$option_text $value_text" . "\n" .
626
					_x( "Type 'wp jetpack options' for more info.", "'wp jetpack options' is a command - do not translate.", 'jetpack' ) . "\n"
627
				);
628
				break;
629
		}
630
	}
631
632
	/**
633
	 * Get the status of or start a new Jetpack sync.
634
	 *
635
	 * ## OPTIONS
636
	 *
637
	 * status : Print the current sync status
638
	 * start  : Start a full sync from this site to WordPress.com
639
	 *
640
	 * ## EXAMPLES
641
	 *
642
	 * wp jetpack sync status
643
	 * wp jetpack sync start --modules=functions --sync_wait_time=5
644
	 *
645
	 * @synopsis <status|start> [--<field>=<value>]
646
	 */
647
	public function sync( $args, $assoc_args ) {
648
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
649
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
650
		}
651
652
		$action = isset( $args[0] ) ? $args[0] : 'status';
653
654
		switch ( $action ) {
655
			case 'status':
656
				$status = Jetpack_Sync_Actions::get_sync_status();
657
				$collection = array();
658
				foreach ( $status as $key => $item ) {
659
					$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...
660
						'option' => $key,
661
						'value' => is_scalar( $item ) ? $item : json_encode( $item )
662
					);
663
				}
664
665
				WP_CLI\Utils\format_items( 'table', $collection, array( 'option', 'value' ) );
666
				break;
667
			case 'start':
668
				// Get the original settings so that we can restore them later
669
				$original_settings = Jetpack_Sync_Settings::get_settings();
670
671
				// Initialize sync settigns so we can sync as quickly as possible
672
				$sync_settings = wp_parse_args(
673
					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...
674
					array(
675
						'sync_wait_time' => 0,
676
						'enqueue_wait_time' => 0,
677
						'queue_max_writes_sec' => 10000,
678
						'max_queue_size_full_sync' => 100000
679
					)
680
				);
681
				Jetpack_Sync_Settings::update_settings( $sync_settings );
682
683
				// Convert comma-delimited string of modules to an array
684 View Code Duplication
				if ( ! empty( $assoc_args['modules'] ) ) {
685
					$modules = array_map( 'trim', explode( ',', $assoc_args['modules'] ) );
686
687
					// Convert the array so that the keys are the module name and the value is true to indicate
688
					// that we want to sync the module
689
					$modules = array_map( '__return_true', array_flip( $modules ) );
690
				}
691
692 View Code Duplication
				foreach ( array( 'posts', 'comments', 'users' ) as $module_name ) {
693
					if (
694
						'users' === $module_name &&
695
						isset( $assoc_args[ $module_name ] ) &&
696
						'initial' === $assoc_args[ $module_name ]
697
					) {
698
						$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...
699
					} elseif ( isset( $assoc_args[ $module_name ] ) ) {
700
						$ids = explode( ',', $assoc_args[ $module_name ] );
701
						if ( count( $ids ) > 0 ) {
702
							$modules[ $module_name ] = $ids;
703
						}
704
					}
705
				}
706
707
				if ( empty( $modules ) ) {
708
					$modules = null;
709
				}
710
711
				// Kick off a full sync
712
				if ( Jetpack_Sync_Actions::do_full_sync( $modules ) ) {
713
					if ( $modules ) {
714
						WP_CLI::log( sprintf( __( 'Initialized a new full sync with modules: %s', 'jetpack' ), join( ', ', array_keys( $modules ) ) ) );
715
					} else {
716
						WP_CLI::log( __( 'Initialized a new full sync', 'jetpack' ) );
717
					}
718 View Code Duplication
				} else {
719
720
					// Reset sync settings to original.
721
					Jetpack_Sync_Settings::update_settings( $original_settings );
722
723
					if ( $modules ) {
724
						WP_CLI::error( sprintf( __( 'Could not start a new full sync with modules: %s', 'jetpack' ), join( ', ', $modules ) ) );
725
					} else {
726
						WP_CLI::error( __( 'Could not start a new full sync', 'jetpack' ) );
727
					}
728
				}
729
730
				// Keep sending to WPCOM until there's nothing to send
731
				$i = 1;
732
				do {
733
					$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...
734
					if ( is_wp_error( $result ) ) {
735
						$queue_empty_error = ( 'empty_queue_full_sync' == $result->get_error_code() );
736
						if ( ! $queue_empty_error || ( $queue_empty_error && ( 1 == $i ) ) ) {
737
							WP_CLI::error( sprintf( __( 'Sync errored with code: %s', 'jetpack' ), $result->get_error_code() ) );
738
						}
739
					} else {
740
						if ( 1 == $i ) {
741
							WP_CLI::log( __( 'Sent data to WordPress.com', 'jetpack' ) );
742
						} else {
743
							WP_CLI::log( __( 'Sent more data to WordPress.com', 'jetpack' ) );
744
						}
745
					}
746
					$i++;
747
				} while ( $result && ! is_wp_error( $result ) );
748
749
				// Reset sync settings to original.
750
				Jetpack_Sync_Settings::update_settings( $original_settings );
751
752
				WP_CLI::success( __( 'Finished syncing to WordPress.com', 'jetpack' ) );
753
				break;
754
		}
755
	}
756
757
	/**
758
	 * List the contents of a specific Jetpack sync queue.
759
	 *
760
	 * ## OPTIONS
761
	 *
762
	 * peek : List the 100 front-most items on the queue.
763
	 *
764
	 * ## EXAMPLES
765
	 *
766
	 * wp jetpack sync_queue full_sync peek
767
	 *
768
	 * @synopsis <incremental|full_sync> <peek>
769
	 */
770
	public function sync_queue( $args, $assoc_args ) {
771
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
772
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
773
		}
774
775
		$queue_name = isset( $args[0] ) ? $args[0] : 'sync';
776
		$action = isset( $args[1] ) ? $args[1] : 'peek';
777
778
		// We map the queue name that way we can support more friendly queue names in the commands, but still use
779
		// the queue name that the code expects.
780
		$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...
781
			'incremental' => 'sync',
782
			'full'        => 'full_sync',
783
		);
784
		$mapped_queue_name = isset( $queue_name_map[ $queue_name ] ) ? $queue_name_map[ $queue_name ] : $queue_name;
785
786
		switch( $action ) {
787
			case 'peek':
788
				require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-queue.php';
789
				$queue = new Jetpack_Sync_Queue( $mapped_queue_name );
790
				$items = $queue->peek( 100 );
791
792
				if ( empty( $items ) ) {
793
					/* translators: %s is the name of the queue, either 'incremental' or 'full' */
794
					WP_CLI::log( sprintf( __( 'Nothing is in the queue: %s', 'jetpack' ), $queue_name  ) );
795
				} else {
796
					$collection = array();
797
					foreach ( $items as $item ) {
798
						$collection[] = array(
799
							'action'          => $item[0],
800
							'args'            => json_encode( $item[1] ),
801
							'current_user_id' => $item[2],
802
							'microtime'       => $item[3],
803
							'importing'       => (string) $item[4],
804
						);
805
					}
806
					WP_CLI\Utils\format_items(
807
						'table',
808
						$collection,
809
						array(
810
							'action',
811
							'args',
812
							'current_user_id',
813
							'microtime',
814
							'importing',
815
						)
816
					);
817
				}
818
				break;
819
		}
820
	}
821
822
	/**
823
	 * Cancel's the current Jetpack plan granted by this partner, if applicable
824
	 *
825
	 * Returns success or error JSON
826
	 *
827
	 * <token_json>
828
	 * : JSON blob of WPCOM API token
829
	 *  [--partner_tracking_id=<partner_tracking_id>]
830
	 * : This is an optional ID that a host can pass to help identify a site in logs on WordPress.com
831
	 *
832
	 *  * @synopsis <token_json> [--partner_tracking_id=<partner_tracking_id>]
833
	 */
834
	public function partner_cancel( $args, $named_args ) {
835
		list( $token_json ) = $args;
836
837 View Code Duplication
		if ( ! $token_json || ! ( $token = json_decode( $token_json ) ) ) {
838
			$this->partner_provision_error( new WP_Error( 'missing_access_token',  sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
839
		}
840
841
		if ( isset( $token->error ) ) {
842
			$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...
843
		}
844
845
		if ( ! isset( $token->access_token ) ) {
846
			$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
847
		}
848
849
		if ( Jetpack::validate_sync_error_idc_option() ) {
850
			$this->partner_provision_error( new WP_Error(
851
				'site_in_safe_mode',
852
				esc_html__( 'Can not cancel a plan while in safe mode. See: https://jetpack.com/support/safe-mode/', 'jetpack' )
853
			) );
854
		}
855
856
		$site_identifier = Jetpack_Options::get_option( 'id' );
857
858
		if ( ! $site_identifier ) {
859
			$site_identifier = Jetpack::build_raw_urls( get_home_url() );
860
		}
861
862
		$request = array(
863
			'headers' => array(
864
				'Authorization' => "Bearer " . $token->access_token,
865
				'Host'          => defined( 'JETPACK__WPCOM_JSON_API_HOST_HEADER' ) ? JETPACK__WPCOM_JSON_API_HOST_HEADER : 'public-api.wordpress.com',
866
			),
867
			'timeout' => 60,
868
			'method'  => 'POST',
869
		);
870
871
		$url = sprintf( 'https://%s/rest/v1.3/jpphp/%s/partner-cancel', $this->get_api_host(), $site_identifier );
872 View Code Duplication
		if ( ! empty( $named_args ) && ! empty( $named_args['partner_tracking_id'] ) ) {
873
			$url = esc_url_raw( add_query_arg( 'partner_tracking_id', $named_args['partner_tracking_id'], $url ) );
874
		}
875
876
		$result = Jetpack_Client::_wp_remote_request( $url, $request );
877
878
		Jetpack_Options::delete_option( 'onboarding' );
879
880
		if ( is_wp_error( $result ) ) {
881
			$this->partner_provision_error( $result );
882
		}
883
884
		WP_CLI::log( wp_remote_retrieve_body( $result ) );
885
	}
886
887
	/**
888
	 * Provision a site using a Jetpack Partner license
889
	 *
890
	 * Returns JSON blob
891
	 *
892
	 * ## OPTIONS
893
	 *
894
	 * <token_json>
895
	 * : JSON blob of WPCOM API token
896
	 * [--plan=<plan_name>]
897
	 * : Slug of the requested plan, e.g. premium
898
	 * [--wpcom_user_id=<user_id>]
899
	 * : WordPress.com ID of user to connect as (must be whitelisted against partner key)
900
	 * [--wpcom_user_email=<wpcom_user_email>]
901
	 * : Override the email we send to WordPress.com for registration
902
	 * [--onboarding=<onboarding>]
903
	 * : Guide the user through an onboarding wizard
904
	 * [--force_register=<register>]
905
	 * : Whether to force a site to register
906
	 * [--force_connect=<force_connect>]
907
	 * : Force JPS to not reuse existing credentials
908
	 * [--home_url=<home_url>]
909
	 * : Overrides the home option via the home_url filter, or the WP_HOME constant
910
	 * [--site_url=<site_url>]
911
	 * : Overrides the siteurl option via the site_url filter, or the WP_SITEURL constant
912
	 * [--partner_tracking_id=<partner_tracking_id>]
913
	 * : This is an optional ID that a host can pass to help identify a site in logs on WordPress.com
914
	 *
915
	 * ## EXAMPLES
916
	 *
917
	 *     $ wp jetpack partner_provision '{ some: "json" }' premium 1
918
	 *     { success: true }
919
	 *
920
	 * @synopsis <token_json> [--wpcom_user_id=<user_id>] [--plan=<plan_name>] [--onboarding=<onboarding>] [--force_register=<register>] [--force_connect=<force_connect>] [--home_url=<home_url>] [--site_url=<site_url>] [--wpcom_user_email=<wpcom_user_email>] [--partner_tracking_id=<partner_tracking_id>]
921
	 */
922
	public function partner_provision( $args, $named_args ) {
923
		list( $token_json ) = $args;
924
925 View Code Duplication
		if ( ! $token_json || ! ( $token = json_decode( $token_json ) ) ) {
926
			$this->partner_provision_error( new WP_Error( 'missing_access_token',  sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
927
		}
928
929
		if ( isset( $token->error ) ) {
930
			$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...
931
				? $token->message
932
				: '';
933
			$this->partner_provision_error( new WP_Error( $token->error, $message ) );
934
		}
935
936
		if ( ! isset( $token->access_token ) ) {
937
			$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
938
		}
939
940
		require_once JETPACK__PLUGIN_DIR . '_inc/class.jetpack-provision.php';
941
942
		$body_json = Jetpack_Provision::partner_provision( $token->access_token, $named_args );
943
944
		if ( is_wp_error( $body_json ) ) {
945
			error_log( json_encode( array(
946
				'success'       => false,
947
				'error_code'    => $body_json->get_error_code(),
948
				'error_message' => $body_json->get_error_message()
949
			) ) );
950
			exit( 1 );
0 ignored issues
show
Coding Style Compatibility introduced by
The method partner_provision() 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...
951
		}
952
953
		WP_CLI::log( json_encode( $body_json ) );
954
	}
955
956
	/**
957
	 * Manages your Jetpack sitemap
958
	 *
959
	 * ## OPTIONS
960
	 *
961
	 * rebuild : Rebuild all sitemaps
962
	 * --purge : if set, will remove all existing sitemap data before rebuilding
963
	 *
964
	 * ## EXAMPLES
965
	 *
966
	 * wp jetpack sitemap rebuild
967
	 *
968
	 * @subcommand sitemap
969
	 * @synopsis <rebuild> [--purge]
970
	 */
971
	public function sitemap( $args, $assoc_args ) {
972
		if ( ! Jetpack::is_active() ) {
973
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
974
		}
975
		if ( ! Jetpack::is_module_active( 'sitemaps' ) ) {
976
			WP_CLI::error( __( 'Jetpack Sitemaps module is not currently active. Activate it first if you want to work with sitemaps.', 'jetpack' ) );
977
		}
978
		if ( ! class_exists( 'Jetpack_Sitemap_Builder' ) ) {
979
			WP_CLI::error( __( 'Jetpack Sitemaps module is active, but unavailable. This can happen if your site is set to discourage search engine indexing. Please enable search engine indexing to allow sitemap generation.', 'jetpack' ) );
980
		}
981
982
		if ( isset( $assoc_args['purge'] ) && $assoc_args['purge'] ) {
983
			$librarian = new Jetpack_Sitemap_Librarian();
984
			$librarian->delete_all_stored_sitemap_data();
985
		}
986
987
		$sitemap_builder = new Jetpack_Sitemap_Builder();
988
		$sitemap_builder->update_sitemap();
989
	}
990
991
	/**
992
	 * Allows authorizing a user via the command line and will activate
993
	 *
994
	 * ## EXAMPLES
995
	 *
996
	 * wp jetpack authorize_user --token=123456789abcdef
997
	 *
998
	 * @synopsis --token=<value>
999
	 */
1000
	public function authorize_user( $args, $named_args ) {
1001
		if ( ! is_user_logged_in() ) {
1002
			WP_CLI::error( __( 'Please select a user to authorize via the --user global argument.', 'jetpack' ) );
1003
		}
1004
1005
		if ( empty( $named_args['token'] ) ) {
1006
			WP_CLI::error( __( 'A non-empty token argument must be passed.', 'jetpack' ) );
1007
		}
1008
1009
		$token = sanitize_text_field( $named_args['token'] );
1010
1011
		$is_master_user  = ! Jetpack::is_active();
1012
		$current_user_id = get_current_user_id();
1013
1014
		Jetpack::update_user_token( $current_user_id, sprintf( '%s.%d', $token, $current_user_id ), $is_master_user );
1015
1016
		WP_CLI::log( wp_json_encode( $named_args ) );
1017
1018
		if ( $is_master_user ) {
1019
			/**
1020
			 * Auto-enable SSO module for new Jetpack Start connections
1021
			*
1022
			* @since 5.0.0
1023
			*
1024
			* @param bool $enable_sso Whether to enable the SSO module. Default to true.
1025
			*/
1026
			$enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
1027
			Jetpack::handle_post_authorization_actions( $enable_sso, false );
1028
1029
			/* translators: %d is a user ID */
1030
			WP_CLI::success( sprintf( __( 'Authorized %d and activated default modules.', 'jetpack' ), $current_user_id ) );
1031
		} else {
1032
			/* translators: %d is a user ID */
1033
			WP_CLI::success( sprintf( __( 'Authorized %d.', 'jetpack' ), $current_user_id ) );
1034
		}
1035
	}
1036
1037
	/**
1038
	 * Allows calling a WordPress.com API endpoint using the current blog's token.
1039
	 *
1040
	 * ## OPTIONS
1041
	 * --endpoint=<endpoint>
1042
	 * : The endpoint to call with the current blog's token, where `%d` represents the current blog's ID.
1043
	 *
1044
	 * [--api_version=<api_version>]
1045
	 * : The API version to query against.
1046
	 *
1047
	 * [--base_api_path=<base_api_path>]
1048
	 * : The base API path to query.
1049
	 * ---
1050
	 * default: rest
1051
	 * ---
1052
	 *
1053
	 * [--body=<body>]
1054
	 * : A JSON encoded string representing arguments to send in the body.
1055
	 *
1056
	 * [--field=<value>]
1057
	 * : Any number of arguments that should be passed to the endpoint.
1058
	 *
1059
	 * ## EXAMPLES
1060
	 *
1061
	 * wp jetpack call_api --endpoint='/sites/%d'
1062
	 */
1063
	public function call_api( $args, $named_args ) {
1064
		if ( ! Jetpack::is_active() ) {
1065
			WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
1066
		}
1067
1068
		$consumed_args = array(
1069
			'endpoint',
1070
			'api_version',
1071
			'base_api_path',
1072
			'body',
1073
		);
1074
1075
		// Get args that should be passed to endpoint.
1076
		$other_args = array_diff_key( $named_args, array_flip( $consumed_args ) );
1077
1078
		$decoded_body = ! empty( $named_args['body'] )
1079
			? json_decode( $named_args['body'] )
1080
			: false;
1081
1082
		$endpoint_url = ( false === strpos( $named_args['endpoint'], '%d' ) )
1083
			? $named_args['endpoint']
1084
			: sprintf( $named_args['endpoint'], Jetpack_Options::get_option( 'id' ) );
1085
1086
		$response = Jetpack_Client::wpcom_json_api_request_as_blog(
1087
			$endpoint_url,
1088
			empty( $named_args['api_version'] ) ? Jetpack_Client::WPCOM_JSON_API_VERSION : $named_args['api_version'],
1089
			$other_args,
1090
			empty( $decoded_body ) ? null : $decoded_body,
1091
			$named_args['base_api_path']
1092
		);
1093
1094 View Code Duplication
		if ( is_wp_error( $response ) ) {
1095
			WP_CLI::error( sprintf(
1096
				/* translators: %1ds is an endpoint route (ex. /sites/123456), %2$d is an error code, %3$s is an error message. */
1097
				__( 'Request to %1$s returned an error: (%2$d) %3$s.', 'jetpack' ),
1098
				$endpoint_url,
1099
				$response->get_error_code(),
1100
				$response->get_error_message()
1101
			) );
1102
		}
1103
1104
		if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
1105
			WP_CLI::error( sprintf(
1106
				/* translators: %1ds is an endpoint route (ex. /sites/123456), %2$d is an HTTP status code. */
1107
				__( 'Request to %1$s returned a non-200 response code: %2$d.', 'jetpack' ),
1108
				$endpoint_url,
1109
				wp_remote_retrieve_response_code( $response )
1110
			) );
1111
		}
1112
1113
		WP_CLI::success( wp_remote_retrieve_body( $response ) );
1114
	}
1115
1116
	public function get_stats( $args, $named_args ) {
1117
1118
	}
1119
1120
	private function get_api_host() {
1121
		$env_api_host = getenv( 'JETPACK_START_API_HOST', true );
1122
		return $env_api_host ? $env_api_host : JETPACK__WPCOM_JSON_API_HOST;
1123
	}
1124
1125
	private function partner_provision_error( $error ) {
1126
		WP_CLI::log( json_encode( array(
1127
			'success'       => false,
1128
			'error_code'    => $error->get_error_code(),
1129
			'error_message' => $error->get_error_message()
1130
		) ) );
1131
		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...
1132
	}
1133
}
1134
1135
/*
1136
 * Standard "ask for permission to continue" function.
1137
 * If action cancelled, ask if they need help.
1138
 *
1139
 * Written outside of the class so it's not listed as an executable command w/ 'wp jetpack'
1140
 *
1141
 * @param $flagged   bool   false = normal option | true = flagged by get_jetpack_options_for_reset()
1142
 * @param $error_msg string (optional)
1143
 */
1144
function jetpack_cli_are_you_sure( $flagged = false, $error_msg = false ) {
1145
	$cli = new Jetpack_CLI();
1146
1147
	// Default cancellation message
1148
	if ( ! $error_msg ) {
1149
		$error_msg =
1150
			__( 'Action cancelled. Have a question?', 'jetpack' )
1151
			. ' '
1152
			. $cli->green_open
1153
			. 'jetpack.com/support'
1154
			.  $cli->color_close;
1155
	}
1156
1157
	if ( ! $flagged ) {
1158
		$prompt_message = __( 'Are you sure? This cannot be undone. Type "yes" to continue:', '"yes" is a command.  Do not translate that.', 'jetpack' );
1159
	} else {
1160
		/* translators: Don't translate the word yes here. */
1161
		$prompt_message = __( 'Are you sure? Modifying this option may disrupt your Jetpack connection.  Type "yes" to continue.', 'jetpack' );
1162
	}
1163
1164
	WP_CLI::line( $prompt_message );
1165
	$handle = fopen( "php://stdin", "r" );
1166
	$line = fgets( $handle );
1167
	if ( 'yes' != trim( $line ) ){
1168
		WP_CLI::error( $error_msg );
1169
	}
1170
}
1171