Completed
Push — update/remove-disconnect-link ( 4b6a2c )
by
unknown
73:18 queued 63:47
created

Jetpack_CLI   D

Complexity

Total Complexity 128

Size/Duplication

Total Lines 739
Duplicated Lines 9.07 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 67
loc 739
rs 4
c 0
b 0
f 0
wmc 128
lcom 1
cbo 5

8 Methods

Rating   Name   Duplication   Size   Complexity  
C status() 3 57 11
D disconnect() 0 50 13
C reset() 8 52 8
F module() 17 66 22
F options() 6 96 25
F protect() 8 100 20
F sync() 25 103 21
C sync_queue() 0 51 8

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
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $args[0] ) );
38
		}
39
40
		/*
41
		 * Are they asking for all data?
42
		 *
43
		 * Loop through heartbeat data and organize by priority.
44
		 */
45
		$all_data = ( isset( $args[0] ) && 'full' == $args[0] ) ? 'full' : false;
46
		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...
47
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
48
			WP_CLI::line( sprintf( __( "The Jetpack Version is %s", 'jetpack' ), JETPACK__VERSION ) );
49
			WP_CLI::line( sprintf( __( "The WordPress.com blog_id is %d", 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
50
51
			// Heartbeat data
52
			WP_CLI::line( "\n" . __( 'Additional data: ', 'jetpack' ) );
53
54
			// Get the filtered heartbeat data.
55
			// Filtered so we can color/list by severity
56
			$stats = Jetpack::jetpack_check_heartbeat_data();
57
58
			// Display red flags first
59
			foreach ( $stats['bad'] as $stat => $value ) {
60
				printf( "$this->red_open%-'.16s %s $this->color_close\n", $stat, $value );
61
			}
62
63
			// Display caution warnings next
64
			foreach ( $stats['caution'] as $stat => $value ) {
65
				printf( "$this->yellow_open%-'.16s %s $this->color_close\n", $stat, $value );
66
			}
67
68
			// The rest of the results are good!
69
			foreach ( $stats['good'] as $stat => $value ) {
70
71
				// Modules should get special spacing for aestetics
72
				if ( strpos( $stat, 'odule-' ) ) {
73
					printf( "%-'.30s %s\n", $stat, $value );
74
					usleep( 4000 ); // For dramatic effect lolz
75
					continue;
76
				}
77
				printf( "%-'.16s %s\n", $stat, $value );
78
				usleep( 4000 ); // For dramatic effect lolz
79
			}
80
		} else {
81
			// Just the basics
82
			WP_CLI::success( __( 'Jetpack is currently connected to WordPress.com', 'jetpack' ) );
83
			WP_CLI::line( sprintf( __( 'The Jetpack Version is %s', 'jetpack' ), JETPACK__VERSION ) );
84
			WP_CLI::line( sprintf( __( 'The WordPress.com blog_id is %d', 'jetpack' ), Jetpack_Options::get_option( 'id' ) ) );
85
			WP_CLI::line( "\n" . _x( "View full status with 'wp jetpack status full'", '"wp jetpack status full" is a command - do not translate', 'jetpack' ) );
86
		}
87
	}
88
89
	/**
90
	 * Disconnect Jetpack Blogs or Users
91
	 *
92
	 * ## OPTIONS
93
	 *
94
	 * blog: Disconnect the entire blog.
95
	 *
96
	 * user <user_identifier>: Disconnect a specific user from WordPress.com.
97
	 *
98
	 * Please note, the primary account that the blog is connected
99
	 * to WordPress.com with cannot be disconnected without
100
	 * disconnecting the entire blog.
101
	 *
102
	 * ## EXAMPLES
103
	 *
104
	 * wp jetpack disconnect blog
105
	 * wp jetpack disconnect user 13
106
	 * wp jetpack disconnect user username
107
	 * wp jetpack disconnect user [email protected]
108
	 *
109
	 * @synopsis <blog|user> [<user_identifier>]
110
	 */
111
	public function disconnect( $args, $assoc_args ) {
112
		if ( ! Jetpack::is_active() ) {
113
			WP_CLI::error( __( 'You cannot disconnect, without having first connected.', 'jetpack' ) );
114
		}
115
116
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
117
		if ( ! in_array( $action, array( 'blog', 'user', 'prompt' ) ) ) {
118
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
119
		}
120
121
		if ( in_array( $action, array( 'user' ) ) ) {
122
			if ( isset( $args[1] ) ) {
123
				$user_id = $args[1];
124
				if ( ctype_digit( $user_id ) ) {
125
					$field = 'id';
126
					$user_id = (int) $user_id;
127
				} elseif ( is_email( $user_id ) ) {
128
					$field = 'email';
129
					$user_id = sanitize_user( $user_id, true );
130
				} else {
131
					$field = 'login';
132
					$user_id = sanitize_user( $user_id, true );
133
				}
134
				if ( ! $user = get_user_by( $field, $user_id ) ) {
135
					WP_CLI::error( __( 'Please specify a valid user.', 'jetpack' ) );
136
				}
137
			} else {
138
				WP_CLI::error( __( 'Please specify a user by either ID, username, or email.', 'jetpack' ) );
139
			}
140
		}
141
142
		switch ( $action ) {
143
			case 'blog':
144
				Jetpack::log( 'disconnect' );
145
				Jetpack::disconnect();
146
				WP_CLI::success( __( 'Jetpack has been successfully disconnected.', 'jetpack' ) );
147
				break;
148
			case 'user':
149
				if ( Jetpack::unlink_user( $user->ID ) ) {
150
					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...
151
					WP_CLI::success( sprintf( __( '%s has been successfully disconnected.', 'jetpack' ), $action ) );
152
				} else {
153
					WP_CLI::error( sprintf( __( '%s could not be disconnected.  Are you sure they\'re connected currently?', 'jetpack' ), "{$user->login} <{$user->email}>" ) );
154
				}
155
				break;
156
			case 'prompt':
157
				WP_CLI::error( __( 'Please specify if you would like to disconnect a blog or user.', 'jetpack' ) );
158
				break;
159
		}
160
	}
161
162
	/**
163
	 * Reset Jetpack options and settings to default
164
	 *
165
	 * ## OPTIONS
166
	 *
167
	 * modules: Resets modules to default state ( get_default_modules() )
168
	 *
169
	 * options: Resets all Jetpack options except:
170
	 *  - All private options (Blog token, user token, etc...)
171
	 *  - id (The Client ID/WP.com Blog ID of this site)
172
	 *  - master_user
173
	 *  - version
174
	 *  - activated
175
	 *
176
	 * ## EXAMPLES
177
	 *
178
	 * wp jetpack reset options
179
	 * wp jetpack reset modules
180
	 *
181
	 * @synopsis <modules|options>
182
	 */
183
	public function reset( $args, $assoc_args ) {
184
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
185 View Code Duplication
		if ( ! in_array( $action, array( 'options', 'modules' ) ) ) {
186
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
187
		}
188
189
		// Are you sure?
190
		jetpack_cli_are_you_sure();
191
192
		switch ( $action ) {
193
			case 'options':
194
				$options_to_reset = Jetpack::get_jetpack_options_for_reset();
195
196
				// Reset the Jetpack options
197
				_e( "Resetting Jetpack Options...\n", "jetpack" );
198
				sleep(1); // Take a breath
199
				foreach ( $options_to_reset['jp_options'] as $option_to_reset ) {
200
					Jetpack_Options::delete_option( $option_to_reset );
201
					usleep( 100000 );
202
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
203
				}
204
205
				// Reset the WP options
206
				_e( "Resetting the jetpack options stored in wp_options...\n", "jetpack" );
207
				usleep( 500000 ); // Take a breath
208
				foreach ( $options_to_reset['wp_options'] as $option_to_reset ) {
209
					delete_option( $option_to_reset );
210
					usleep( 100000 );
211
					WP_CLI::success( sprintf( __( '%s option reset', 'jetpack' ), $option_to_reset ) );
212
				}
213
214
				// Reset to default modules
215
				_e( "Resetting default modules...\n", "jetpack" );
216
				usleep( 500000 ); // Take a breath
217
				$default_modules = Jetpack::get_default_modules();
218
				Jetpack::update_active_modules( $default_modules );
219
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
220
221
				// Jumpstart option is special
222
				Jetpack_Options::update_option( 'jumpstart', 'new_connection' );
223
				WP_CLI::success( __( 'jumpstart option reset', 'jetpack' ) );
224
				break;
225 View Code Duplication
			case 'modules':
226
				$default_modules = Jetpack::get_default_modules();
227
				Jetpack::update_active_modules( $default_modules );
228
				WP_CLI::success( __( 'Modules reset to default.', 'jetpack' ) );
229
				break;
230
			case 'prompt':
231
				WP_CLI::error( __( 'Please specify if you would like to reset your options, or modules', 'jetpack' ) );
232
				break;
233
		}
234
	}
235
236
	/**
237
	 * Manage Jetpack Modules
238
	 *
239
	 * ## OPTIONS
240
	 *
241
	 * list          : View all available modules, and their status.
242
	 * activate all  : Activate all modules
243
	 * deactivate all: Deactivate all modules
244
	 *
245
	 * activate   <module_slug> : Activate a module.
246
	 * deactivate <module_slug> : Deactivate a module.
247
	 * toggle     <module_slug> : Toggle a module on or off.
248
	 *
249
	 * ## EXAMPLES
250
	 *
251
	 * wp jetpack module list
252
	 * wp jetpack module activate stats
253
	 * wp jetpack module deactivate stats
254
	 * wp jetpack module toggle stats
255
	 *
256
	 * wp jetpack module activate all
257
	 * wp jetpack module deactivate all
258
	 *
259
	 * @synopsis <list|activate|deactivate|toggle> [<module_name>]
260
	 */
261
	public function module( $args, $assoc_args ) {
262
		$action = isset( $args[0] ) ? $args[0] : 'list';
263
		if ( ! in_array( $action, array( 'list', 'activate', 'deactivate', 'toggle' ) ) ) {
264
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
265
		}
266
		if ( in_array( $action, array( 'activate', 'deactivate', 'toggle' ) ) ) {
267
			if ( isset( $args[1] ) ) {
268
				$module_slug = $args[1];
269
				if ( 'all' !== $module_slug && ! Jetpack::is_module( $module_slug ) ) {
270
					WP_CLI::error( sprintf( __( '%s is not a valid module.', 'jetpack' ), $module_slug ) );
271
				}
272
				if ( 'toggle' == $action ) {
273
					$action = Jetpack::is_module_active( $module_slug ) ? 'deactivate' : 'activate';
274
				}
275
				// Bulk actions
276
				if ( 'all' == $args[1] ) {
277
					$action = ( 'deactivate' == $action ) ? 'deactivate_all' : 'activate_all';
278
				}
279
				// VaultPress needs to be handled elsewhere.
280
				if ( in_array( $action, array( 'activate', 'deactivate', 'toggle' ) ) && 'vaultpress' == $args[1] ) {
281
					WP_CLI::error( sprintf( _x( 'Please visit %s to configure your VaultPress subscription.', '%s is a website', 'jetpack' ), esc_url( 'https://vaultpress.com/jetpack/' ) ) );
282
				}
283
			} else {
284
				WP_CLI::line( __( 'Please specify a valid module.', 'jetpack' ) );
285
				$action = 'list';
286
			}
287
		}
288
		switch ( $action ) {
289
			case 'list':
290
				WP_CLI::line( __( 'Available Modules:', 'jetpack' ) );
291
				$modules = Jetpack::get_available_modules();
292
				sort( $modules );
293
				foreach( $modules as $module_slug ) {
294
					if ( 'vaultpress' == $module_slug ) {
295
						continue;
296
					}
297
					$active = Jetpack::is_module_active( $module_slug ) ? __( 'Active', 'jetpack' ) : __( 'Inactive', 'jetpack' );
298
					WP_CLI::line( "\t" . str_pad( $module_slug, 24 ) . $active );
299
				}
300
				break;
301 View Code Duplication
			case 'activate':
302
				$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...
303
				Jetpack::log( 'activate', $module_slug );
304
				Jetpack::activate_module( $module_slug, false, false );
305
				WP_CLI::success( sprintf( __( '%s has been activated.', 'jetpack' ), $module['name'] ) );
306
				break;
307 View Code Duplication
			case 'activate_all':
308
				$modules = Jetpack::get_available_modules();
309
				Jetpack::update_active_modules( $modules );
310
				WP_CLI::success( __( 'All modules activated!', 'jetpack' ) );
311
				break;
312 View Code Duplication
			case 'deactivate':
313
				$module = Jetpack::get_module( $module_slug );
314
				Jetpack::log( 'deactivate', $module_slug );
315
				Jetpack::deactivate_module( $module_slug );
316
				WP_CLI::success( sprintf( __( '%s has been deactivated.', 'jetpack' ), $module['name'] ) );
317
				break;
318
			case 'deactivate_all':
319
				Jetpack::delete_active_modules();
320
				WP_CLI::success( __( 'All modules deactivated!', 'jetpack' ) );
321
				break;
322
			case 'toggle':
323
				// Will never happen, should have been handled above and changed to activate or deactivate.
324
				break;
325
		}
326
	}
327
328
	/**
329
	 * Manage Protect Settings
330
	 *
331
	 * ## OPTIONS
332
	 *
333
	 * whitelist: Whitelist an IP address.  You can also read or clear the whitelist.
334
	 *
335
	 *
336
	 * ## EXAMPLES
337
	 *
338
	 * wp jetpack protect whitelist <ip address>
339
	 * wp jetpack protect whitelist list
340
	 * wp jetpack protect whitelist clear
341
	 *
342
	 * @synopsis <whitelist> [<ip|ip_low-ip_high|list|clear>]
343
	 */
344
	public function protect( $args, $assoc_args ) {
345
		$action = isset( $args[0] ) ? $args[0] : 'prompt';
346
		if ( ! in_array( $action, array( 'whitelist' ) ) ) {
347
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
348
		}
349
		// Check if module is active
350
		if ( ! Jetpack::is_module_active( __FUNCTION__ ) ) {
351
			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__ ) );
352
		}
353
		if ( in_array( $action, array( 'whitelist' ) ) ) {
354
			if ( isset( $args[1] ) ) {
355
				$action = 'whitelist';
356
			} else {
357
				$action = 'prompt';
358
			}
359
		}
360
		switch ( $action ) {
361
			case 'whitelist':
362
				$whitelist         = array();
363
				$new_ip            = $args[1];
364
				$current_whitelist = get_site_option( 'jetpack_protect_whitelist' );
365
366
				// Build array of IPs that are already whitelisted.
367
				// Re-build manually instead of using jetpack_protect_format_whitelist() so we can easily get
368
				// low & high range params for jetpack_protect_ip_address_is_in_range();
369
				foreach( $current_whitelist as $whitelisted ) {
370
371
					// IP ranges
372
					if ( $whitelisted->range ) {
373
374
						// Is it already whitelisted?
375
						if ( jetpack_protect_ip_address_is_in_range( $new_ip, $whitelisted->range_low, $whitelisted->range_high ) ) {
376
							WP_CLI::error( sprintf( __( "%s has already been whitelisted", 'jetpack' ), $new_ip ) );
377
							break;
378
						}
379
						$whitelist[] = $whitelisted->range_low . " - " . $whitelisted->range_high;
380
381
					} else { // Individual IPs
382
383
						// Check if the IP is already whitelisted (single IP only)
384
						if ( $new_ip == $whitelisted->ip_address ) {
385
							WP_CLI::error( sprintf( __( "%s has already been whitelisted", 'jetpack' ), $new_ip ) );
386
							break;
387
						}
388
						$whitelist[] = $whitelisted->ip_address;
389
390
					}
391
				}
392
393
				/*
394
				 * List the whitelist
395
				 * Done here because it's easier to read the $whitelist array after it's been rebuilt
396
				 */
397
				if ( isset( $args[1] ) && 'list' == $args[1] ) {
398 View Code Duplication
					if ( ! empty( $whitelist ) ) {
399
						WP_CLI::success( __( 'Here are your whitelisted IPs:', 'jetpack' ) );
400
						foreach ( $whitelist as $ip ) {
401
							WP_CLI::line( "\t" . str_pad( $ip, 24 ) ) ;
402
						}
403
					} else {
404
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
405
					}
406
					break;
407
				}
408
409
				/*
410
				 * Clear the whitelist
411
				 */
412
				if ( isset( $args[1] ) && 'clear' == $args[1] ) {
413
					if ( ! empty( $whitelist ) ) {
414
						$whitelist = array();
415
						jetpack_protect_save_whitelist( $whitelist );
416
						WP_CLI::success( __( 'Cleared all whitelisted IPs', 'jetpack' ) );
417
					} else {
418
						WP_CLI::line( __( 'Whitelist is empty.', "jetpack" ) ) ;
419
					}
420
					break;
421
				}
422
423
				// Append new IP to whitelist array
424
				array_push( $whitelist, $new_ip );
425
426
				// Save whitelist if there are no errors
427
				$result = jetpack_protect_save_whitelist( $whitelist );
428
				if ( is_wp_error( $result ) ) {
429
					WP_CLI::error( __( $result, 'jetpack' ) );
430
				}
431
432
				WP_CLI::success( sprintf( __( '%s has been whitelisted.', 'jetpack' ), $new_ip ) );
433
				break;
434
			case 'prompt':
435
				WP_CLI::error(
436
					__( 'No command found.', 'jetpack' ) . "\n" .
437
					__( 'Please enter the IP address you want to whitelist.', 'jetpack' ) . "\n" .
438
					_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" .
439
					_x( "You can also 'list' or 'clear' the whitelist.", "'list' and 'clear' are commands and should not be translated", 'jetpack' ) . "\n"
440
				);
441
				break;
442
		}
443
	}
444
445
	/**
446
	 * Manage Jetpack Options
447
	 *
448
	 * ## OPTIONS
449
	 *
450
	 * list   : List all jetpack options and their values
451
	 * delete : Delete an option
452
	 *          - can only delete options that are white listed.
453
	 * update : update an option
454
	 *          - can only update option strings
455
	 * get    : get the value of an option
456
	 *
457
	 * ## EXAMPLES
458
	 *
459
	 * wp jetpack options list
460
	 * wp jetpack options get    <option_name>
461
	 * wp jetpack options delete <option_name>
462
	 * wp jetpack options update <option_name> [<option_value>]
463
	 *
464
	 * @synopsis <list|get|delete|update> [<option_name>] [<option_value>]
465
	 */
466
	public function options( $args, $assoc_args ) {
467
		$action = isset( $args[0] ) ? $args[0] : 'list';
468
		$safe_to_modify = Jetpack::get_jetpack_options_for_reset();
469
470
		// Jumpstart is special
471
		array_push( $safe_to_modify, 'jumpstart' );
472
473
		// Is the option flagged as unsafe?
474
		$flagged = ! in_array( $args[1], $safe_to_modify );
475
476 View Code Duplication
		if ( ! in_array( $action, array( 'list', 'get', 'delete', 'update' ) ) ) {
477
			WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack' ), $action ) );
478
		}
479
480
		if ( isset( $args[0] ) ) {
481
			if ( 'get' == $args[0] && isset( $args[1] ) ) {
482
				$action = 'get';
483
			} else if ( 'delete' == $args[0] && isset( $args[1] ) ) {
484
				$action = 'delete';
485
			} else if ( 'update' == $args[0] && isset( $args[1] ) ) {
486
				$action = 'update';
487
			} else {
488
				$action = 'list';
489
			}
490
		}
491
492
		// Bail if the option isn't found
493
		$option = isset( $args[1] ) ? Jetpack_Options::get_option( $args[1] ) : false;
494 View Code Duplication
		if ( isset( $args[1] ) && ! $option && 'update' !== $args[0] ) {
495
			WP_CLI::error( __( 'Option not found or is empty.  Use "list" to list option names', 'jetpack' ) );
496
		}
497
498
		// Let's print_r the option if it's an array
499
		// Used in the 'get' and 'list' actions
500
		$option = is_array( $option ) ? print_r( $option ) : $option;
501
502
		switch ( $action ) {
503
			case 'get':
504
				WP_CLI::success( "\t" . $option );
505
				break;
506
			case 'delete':
507
				jetpack_cli_are_you_sure( $flagged );
508
509
				Jetpack_Options::delete_option( $args[1] );
510
				WP_CLI::success( sprintf( __( 'Deleted option: %s', 'jetpack' ), $args[1] ) );
511
				break;
512
			case 'update':
513
				jetpack_cli_are_you_sure( $flagged );
514
515
				// Updating arrays would get pretty tricky...
516
				$value = Jetpack_Options::get_option( $args[1] );
517
				if ( $value && is_array( $value ) ) {
518
					WP_CLI::error( __( 'Sorry, no updating arrays at this time', 'jetpack' ) );
519
				}
520
521
				Jetpack_Options::update_option( $args[1], $args[2] );
522
				WP_CLI::success( sprintf( _x( 'Updated option: %s to "%s"', 'Updating an option from "this" to "that".', 'jetpack' ), $args[1], $args[2] ) );
523
				break;
524
			case 'list':
525
				$options_compact     = Jetpack_Options::get_option_names();
526
				$options_non_compact = Jetpack_Options::get_option_names( 'non_compact' );
527
				$options_private     = Jetpack_Options::get_option_names( 'private' );
528
				$options             = array_merge( $options_compact, $options_non_compact, $options_private );
529
530
				// Table headers
531
				WP_CLI::line( "\t" . str_pad( __( 'Option', 'jetpack' ), 30 ) . __( 'Value', 'jetpack' ) );
532
533
				// List out the options and their values
534
				// Tell them if the value is empty or not
535
				// Tell them if it's an array
536
				foreach ( $options as $option ) {
537
					$value = Jetpack_Options::get_option( $option );
538
					if ( ! $value ) {
539
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Empty' );
540
						continue;
541
					}
542
543
					if ( ! is_array( $value ) ) {
544
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . $value );
545
					} else if ( is_array( $value ) ) {
546
						WP_CLI::line( "\t" . str_pad( $option, 30 ) . 'Array - Use "get <option>" to read option array.' );
547
					}
548
				}
549
				$option_text = '{' . _x( 'option', 'a variable command that a user can write, provided in the printed instructions', 'jetpack' ) . '}';
550
				$value_text  = '{' . _x( 'value', 'the value that they want to update the option to', 'jetpack' ) . '}';
551
552
				WP_CLI::success(
553
					_x( "Above are your options. You may 'get', 'delete', and 'update' them.", "'get', 'delete', and 'update' are commands - do not translate.", 'jetpack' ) . "\n" .
554
					str_pad( 'wp jetpack options get', 26 )    . $option_text . "\n" .
555
					str_pad( 'wp jetpack options delete', 26 ) . $option_text . "\n" .
556
					str_pad( 'wp jetpack options update', 26 ) . "$option_text $value_text" . "\n" .
557
					_x( "Type 'wp jetpack options' for more info.", "'wp jetpack options' is a command - do not translate.", 'jetpack' ) . "\n"
558
				);
559
				break;
560
		}
561
	}
562
563
	/**
564
	 * Get the status of or start a new Jetpack sync.
565
	 *
566
	 * ## OPTIONS
567
	 *
568
	 * status : Print the current sync status
569
	 * start  : Start a full sync from this site to WordPress.com
570
	 *
571
	 * ## EXAMPLES
572
	 *
573
	 * wp jetpack sync status
574
	 * wp jetpack sync start --modules=functions --sync_wait_time=5
575
	 *
576
	 * @synopsis <status|start> [--<field>=<value>]
577
	 */
578
	public function sync( $args, $assoc_args ) {
579
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
580
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
581
		}
582
583
		$action = isset( $args[0] ) ? $args[0] : 'status';
584
585
		switch ( $action ) {
586
			case 'status':
587
				$status = Jetpack_Sync_Actions::get_sync_status();
588
				$collection = array();
589
				foreach ( $status as $key => $item ) {
590
					$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...
591
						'option' => $key,
592
						'value' => is_scalar( $item ) ? $item : json_encode( $item )
593
					);
594
				}
595
596
				WP_CLI\Utils\format_items( 'table', $collection, array( 'option', 'value' ) );
597
				break;
598
			case 'start':
599
				// Get the original settings so that we can restore them later
600
				$original_settings = Jetpack_Sync_Settings::get_settings();
601
602
				// Initialize sync settigns so we can sync as quickly as possible
603
				$sync_settings = wp_parse_args(
604
					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...
605
					array(
606
						'sync_wait_time' => 0,
607
						'enqueue_wait_time' => 0,
608
						'queue_max_writes_sec' => 10000,
609
						'max_queue_size_full_sync' => 100000
610
					)
611
				);
612
				Jetpack_Sync_Settings::update_settings( $sync_settings );
613
614
				// Convert comma-delimited string of modules to an array
615
				if ( ! empty( $assoc_args['modules'] ) ) {
616
					$modules = array_map( 'trim', explode( ',', $assoc_args['modules'] ) );
617
618
					// Convert the array so that the keys are the module name and the value is true to indicate
619
					// that we want to sync the module
620
					$modules = array_map( '__return_true', array_flip( $modules ) );
621
				}
622
623 View Code Duplication
				foreach ( array( 'posts', 'comments', 'users' ) as $module_name ) {
624
					if (
625
						'users' === $module_name &&
626
						isset( $assoc_args[ $module_name ] ) &&
627
						'initial' === $assoc_args[ $module_name ]
628
					) {
629
						$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...
630
					} elseif ( isset( $assoc_args[ $module_name ] ) ) {
631
						$ids = explode( ',', $assoc_args[ $module_name ] );
632
						if ( count( $ids ) > 0 ) {
633
							$modules[ $module_name ] = $ids;
634
						}
635
					}
636
				}
637
638
				if ( empty( $modules ) ) {
639
					$modules = null;
640
				}
641
642
				// Kick off a full sync
643
				if ( Jetpack_Sync_Actions::do_full_sync( $modules ) ) {
644
					if ( $modules ) {
645
						WP_CLI::log( sprintf( __( 'Initialized a new full sync with modules: ', 'jetpack' ), join( ', ', $modules ) ) );
646
					} else {
647
						WP_CLI::log( __( 'Initialized a new full sync', 'jetpack' ) );
648
					}
649 View Code Duplication
				} else {
650
651
					// Reset sync settings to original.
652
					Jetpack_Sync_Settings::update_settings( $original_settings );
653
654
					if ( $modules ) {
655
						WP_CLI::error( sprintf( __( 'Could not start a new full sync with modules: %s', 'jetpack' ), join( ', ', $modules ) ) );
656
					} else {
657
						WP_CLI::error( __( 'Could not start a new full sync', 'jetpack' ) );
658
					}
659
				}
660
661
				// Keep sending to WPCOM until there's nothing to send
662
				$i = 1;
663
				do {
664
					$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...
665
					if ( $result ) {
666
						if ( 1 == $i++ ) {
667
							WP_CLI::log( __( 'Sent data to WordPress.com', 'jetpack' ) );
668
						} else {
669
							WP_CLI::log( __( 'Sent more data to WordPress.com', 'jetpack' ) );
670
						}
671
					}
672
				} while ( $result );
673
674
				// Reset sync settings to original.
675
				Jetpack_Sync_Settings::update_settings( $original_settings );
676
677
				WP_CLI::success( __( 'Finished syncing to WordPress.com', 'jetpack' ) );
678
				break;
679
		}
680
	}
681
682
	/**
683
	 * List the contents of a specific Jetpack sync queue.
684
	 *
685
	 * ## OPTIONS
686
	 *
687
	 * peek : List the 100 front-most items on the queue.
688
	 *
689
	 * ## EXAMPLES
690
	 *
691
	 * wp jetpack sync_queue full_sync peek
692
	 *
693
	 * @synopsis <incremental|full_sync> <peek>
694
	 */
695
	public function sync_queue( $args, $assoc_args ) {
696
		if ( ! Jetpack_Sync_Actions::sync_allowed() ) {
697
			WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
698
		}
699
700
		$queue_name = isset( $args[0] ) ? $args[0] : 'sync';
701
		$action = isset( $args[1] ) ? $args[1] : 'peek';
702
703
		// We map the queue name that way we can support more friendly queue names in the commands, but still use
704
		// the queue name that the code expects.
705
		$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...
706
			'incremental' => 'sync',
707
			'full'        => 'full_sync',
708
		);
709
		$mapped_queue_name = isset( $queue_name_map[ $queue_name ] ) ? $queue_name_map[ $queue_name ] : $queue_name;
710
711
		switch( $action ) {
712
			case 'peek':
713
				require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-queue.php';
714
				$queue = new Jetpack_Sync_Queue( $mapped_queue_name );
715
				$items = $queue->peek( 100 );
716
717
				if ( empty( $items ) ) {
718
					/* translators: %s is the name of the queue, either 'incremental' or 'full' */
719
					WP_CLI::log( sprintf( __( 'Nothing is in the queue: %s', 'jetpack' ), $queue_name  ) );
720
				} else {
721
					$collection = array();
722
					foreach ( $items as $item ) {
723
						$collection[] = array(
724
							'action'          => $item[0],
725
							'args'            => json_encode( $item[1] ),
726
							'current_user_id' => $item[2],
727
							'microtime'       => $item[3],
728
							'importing'       => (string) $item[4],
729
						);
730
					}
731
					WP_CLI\Utils\format_items(
732
						'table',
733
						$collection,
734
						array(
735
							'action',
736
							'args',
737
							'current_user_id',
738
							'microtime',
739
							'importing',
740
						)
741
					);
742
				}
743
				break;
744
		}
745
	}
746
}
747
748
/*
749
 * Standard "ask for permission to continue" function.
750
 * If action cancelled, ask if they need help.
751
 *
752
 * Written outside of the class so it's not listed as an executable command w/ 'wp jetpack'
753
 *
754
 * @param $flagged   bool   false = normal option | true = flagged by get_jetpack_options_for_reset()
755
 * @param $error_msg string (optional)
756
 */
757
function jetpack_cli_are_you_sure( $flagged = false, $error_msg = false ) {
758
	$cli = new Jetpack_CLI();
759
760
	// Default cancellation message
761
	if ( ! $error_msg ) {
762
		$error_msg =
763
			__( 'Action cancelled. Have a question?', 'jetpack' )
764
			. ' '
765
			. $cli->green_open
766
			. 'jetpack.com/support'
767
			.  $cli->color_close;
768
	}
769
770
	if ( ! $flagged ) {
771
		$prompt_message = __( 'Are you sure? This cannot be undone. Type "yes" to continue:', '"yes" is a command.  Do not translate that.', 'jetpack' );
772
	} else {
773
		/* translators: Don't translate the word yes here. */
774
		$prompt_message = __( 'Are you sure? Modifying this option may disrupt your Jetpack connection.  Type "yes" to continue.', 'jetpack' );
775
	}
776
777
	WP_CLI::line( $prompt_message );
778
	$handle = fopen( "php://stdin", "r" );
779
	$line = fgets( $handle );
780
	if ( 'yes' != trim( $line ) ){
781
		WP_CLI::error( $error_msg );
782
	}
783
}
784