Completed
Push — try/e2e-wp-env ( 20ea7b...e6c96f )
by Yaroslav
97:04 queued 88:26
created

Jetpack_Options::get_option_from_database()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 19

Duplication

Lines 7
Ratio 36.84 %

Importance

Changes 0
Metric Value
cc 5
nc 5
nop 2
dl 7
loc 19
rs 9.3222
c 0
b 0
f 0
1
<?php
2
/**
3
 * Legacy Jetpack_Options class.
4
 *
5
 * @package automattic/jetpack-options
6
 */
7
8
use Automattic\Jetpack\Constants;
9
10
/**
11
 * Class Jetpack_Options
12
 */
13
class Jetpack_Options {
14
15
	/**
16
	 * An array that maps a grouped option type to an option name.
17
	 *
18
	 * @var array
19
	 */
20
	private static $grouped_options = array(
21
		'compact' => 'jetpack_options',
22
		'private' => 'jetpack_private_options',
23
	);
24
25
	/**
26
	 * Returns an array of option names for a given type.
27
	 *
28
	 * @param string $type The type of option to return. Defaults to 'compact'.
29
	 *
30
	 * @return array
31
	 */
32
	public static function get_option_names( $type = 'compact' ) {
33
		switch ( $type ) {
34
			case 'non-compact':
35
			case 'non_compact':
36
				return array(
37
					'activated',
38
					'active_modules',
39
					'allowed_xsite_search_ids', // (array) Array of WP.com blog ids that are allowed to search the content of this site
40
					'available_modules',
41
					'do_activate',
42
					'edit_links_calypso_redirect', // (bool) Whether post/page edit links on front end should point to Calypso.
43
					'log',
44
					'slideshow_background_color',
45
					'widget_twitter',
46
					'wpcc_options',
47
					'relatedposts',
48
					'file_data',
49
					'autoupdate_plugins',          // (array)  An array of plugin ids ( eg. jetpack/jetpack ) that should be autoupdated
50
					'autoupdate_plugins_translations', // (array)  An array of plugin ids ( eg. jetpack/jetpack ) that should be autoupdated translation files.
51
					'autoupdate_themes',           // (array)  An array of theme ids ( eg. twentyfourteen ) that should be autoupdated
52
					'autoupdate_themes_translations', // (array)  An array of theme ids ( eg. twentyfourteen ) that should autoupdated translation files.
53
					'autoupdate_core',             // (bool)   Whether or not to autoupdate core
54
					'autoupdate_translations',     // (bool)   Whether or not to autoupdate all translations
55
					'json_api_full_management',    // (bool)   Allow full management (eg. Activate, Upgrade plugins) of the site via the JSON API.
56
					'sync_non_public_post_stati',  // (bool)   Allow synchronisation of posts and pages with non-public status.
57
					'site_icon_url',               // (string) url to the full site icon
58
					'site_icon_id',                // (int)    Attachment id of the site icon file
59
					'dismissed_manage_banner',     // (bool) Dismiss Jetpack manage banner allows the user to dismiss the banner permanently
60
					'unique_connection',           // (array)  A flag to determine a unique connection to wordpress.com two values "connected" and "disconnected" with values for how many times each has occured
61
					'protect_whitelist',           // (array) IP Address for the Protect module to ignore
62
					'sync_error_idc',              // (bool|array) false or array containing the site's home and siteurl at time of IDC error
63
					'sync_health_status',          // (bool|array) An array of data relating to Jetpack's sync health.
64
					'safe_mode_confirmed',         // (bool) True if someone confirms that this site was correctly put into safe mode automatically after an identity crisis is discovered.
65
					'migrate_for_idc',             // (bool) True if someone confirms that this site should migrate stats and subscribers from its previous URL
66
					'dismissed_connection_banner', // (bool) True if the connection banner has been dismissed
67
					'ab_connect_banner_green_bar', // (int) Version displayed of the A/B test for the green bar at the top of the connect banner.
68
					'onboarding',                  // (string) Auth token to be used in the onboarding connection flow
69
					'tos_agreed',                  // (bool)   Whether or not the TOS for connection has been agreed upon.
70
					'static_asset_cdn_files',      // (array) An nested array of files that we can swap out for cdn versions.
71
					'mapbox_api_key',              // (string) Mapbox API Key, for use with Map block.
72
					'mailchimp',                   // (string) Mailchimp keyring data, for mailchimp block.
73
					'xmlrpc_errors',               // (array) Keys are XML-RPC signature error codes. Values are truthy.
74
					'dismissed_wizard_banner',     // (int) True if the Wizard banner has been dismissed.
75
				);
76
77
			case 'private':
78
				return array(
79
					'blog_token',  // (string) The Client Secret/Blog Token of this site.
80
					'user_token',  // (string) The User Token of this site. (deprecated)
81
					'user_tokens',  // (array)  User Tokens for each user of this site who has connected to jetpack.wordpress.com.
82
				);
83
84
			case 'network':
85
				return array(
86
					'onboarding',                   // (string) Auth token to be used in the onboarding connection flow
87
					'file_data',                     // (array) List of absolute paths to all Jetpack modules
88
				);
89
		}
90
91
		return array(
92
			'id',                           // (int)    The Client ID/WP.com Blog ID of this site.
93
			'publicize_connections',        // (array)  An array of Publicize connections from WordPress.com.
94
			'master_user',                  // (int)    The local User ID of the user who connected this site to jetpack.wordpress.com.
95
			'version',                      // (string) Used during upgrade procedure to auto-activate new modules. version:time.
96
			'old_version',                  // (string) Used to determine which modules are the most recently added. previous_version:time.
97
			'fallback_no_verify_ssl_certs', // (int)    Flag for determining if this host must skip SSL Certificate verification due to misconfigured SSL.
98
			'time_diff',                    // (int)    Offset between Jetpack server's clocks and this server's clocks. Jetpack Server Time = time() + (int) Jetpack_Options::get_option( 'time_diff' )
99
			'public',                       // (int|bool) If we think this site is public or not (1, 0), false if we haven't yet tried to figure it out.
100
			'videopress',                   // (array)  VideoPress options array.
101
			'is_network_site',              // (int|bool) If we think this site is a network or a single blog (1, 0), false if we haven't yet tried to figue it out.
102
			'social_links',                 // (array)  The specified links for each social networking site.
103
			'identity_crisis_whitelist',    // (array)  An array of options, each having an array of the values whitelisted for it.
104
			'gplus_authors',                // (array)  The Google+ authorship information for connected users.
105
			'last_heartbeat',               // (int)    The timestamp of the last heartbeat that fired.
106
			'hide_jitm',                    // (array)  A list of just in time messages that we should not show because they have been dismissed by the user.
107
			'custom_css_4.7_migration',     // (bool)   Whether Custom CSS has scanned for and migrated any legacy CSS CPT entries to the new Core format.
108
			'image_widget_migration',       // (bool)   Whether any legacy Image Widgets have been converted to the new Core widget.
109
			'gallery_widget_migration',     // (bool)   Whether any legacy Gallery Widgets have been converted to the new Core widget.
110
			'sso_first_login',              // (bool)   Is this the first time the user logins via SSO.
111
			'dismissed_hints',              // (array)  Part of Plugin Search Hints. List of cards that have been dismissed.
112
			'first_admin_view',             // (bool)   Set to true the first time the user views the admin. Usually after the initial connection.
113
			'setup_wizard_questionnaire',   // (array)  List of user choices from the setup wizard.
114
			'setup_wizard_status',          // (string) Status of the setup wizard.
115
		);
116
	}
117
118
	/**
119
	 * Is the option name valid?
120
	 *
121
	 * @param string      $name  The name of the option.
122
	 * @param string|null $group The name of the group that the option is in. Default to null, which will search non_compact.
123
	 *
124
	 * @return bool Is the option name valid?
125
	 */
126
	public static function is_valid( $name, $group = null ) {
127
		if ( is_array( $name ) ) {
128
			$compact_names = array();
129
			foreach ( array_keys( self::$grouped_options ) as $_group ) {
130
				$compact_names = array_merge( $compact_names, self::get_option_names( $_group ) );
131
			}
132
133
			$result = array_diff( $name, self::get_option_names( 'non_compact' ), $compact_names );
134
135
			return empty( $result );
136
		}
137
138 View Code Duplication
		if ( is_null( $group ) || 'non_compact' === $group ) {
139
			if ( in_array( $name, self::get_option_names( $group ), true ) ) {
140
				return true;
141
			}
142
		}
143
144
		foreach ( array_keys( self::$grouped_options ) as $_group ) {
145 View Code Duplication
			if ( is_null( $group ) || $group === $_group ) {
146
				if ( in_array( $name, self::get_option_names( $_group ), true ) ) {
147
					return true;
148
				}
149
			}
150
		}
151
152
		return false;
153
	}
154
155
	/**
156
	 * Checks if an option must be saved for the whole network in WP Multisite
157
	 *
158
	 * @param string $option_name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
159
	 *
160
	 * @return bool
161
	 */
162
	public static function is_network_option( $option_name ) {
163
		if ( ! is_multisite() ) {
164
			return false;
165
		}
166
		return in_array( $option_name, self::get_option_names( 'network' ), true );
167
	}
168
169
	/**
170
	 * Filters the requested option.
171
	 * This is a wrapper around `get_option_from_database` so that we can filter the option.
172
	 *
173
	 * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
174
	 * @param mixed  $default (optional).
175
	 *
176
	 * @return mixed
177
	 */
178
	public static function get_option( $name, $default = false ) {
179
		/**
180
		 * Filter Jetpack Options.
181
		 * Can be useful in environments when Jetpack is running with a different setup
182
		 *
183
		 * @since 8.8.0
184
		 *
185
		 * @param string $value The value from the database.
186
		 * @param string $name Option name, _without_ `jetpack_%` prefix.
187
		 * @return string $value, unless the filters modify it.
188
		 */
189
		return apply_filters( 'jetpack_options', self::get_option_from_database( $name, $default ), $name );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $name.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
190
	}
191
192
	/**
193
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
194
	 *
195
	 * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
196
	 * @param mixed  $default (optional).
197
	 *
198
	 * @return mixed
199
	 */
200
	private static function get_option_from_database( $name, $default = false ) {
201 View Code Duplication
		if ( self::is_valid( $name, 'non_compact' ) ) {
202
			if ( self::is_network_option( $name ) ) {
203
				return get_site_option( "jetpack_$name", $default );
204
			}
205
206
			return get_option( "jetpack_$name", $default );
207
		}
208
209
		foreach ( array_keys( self::$grouped_options ) as $group ) {
210
			if ( self::is_valid( $name, $group ) ) {
211
				return self::get_grouped_option( $group, $name, $default );
212
			}
213
		}
214
215
		trigger_error( sprintf( 'Invalid Jetpack option name: %s', esc_html( $name ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Don't wish to change legacy behavior.
216
217
		return $default;
218
	}
219
220
	/**
221
	 * Returns the requested option, and ensures it's autoloaded in the future.
222
	 * This does _not_ adjust the prefix in any way (does not prefix jetpack_%)
223
	 *
224
	 * @param string $name Option name.
225
	 * @param mixed  $default (optional).
226
	 *
227
	 * @return mixed
228
	 */
229
	public static function get_option_and_ensure_autoload( $name, $default ) {
230
		// In this function the name is not adjusted by prefixing jetpack_
231
		// so if it has already prefixed, we'll replace it and then
232
		// check if the option name is a network option or not.
233
		$jetpack_name      = preg_replace( '/^jetpack_/', '', $name, 1 );
234
		$is_network_option = self::is_network_option( $jetpack_name );
235
		$value             = $is_network_option ? get_site_option( $name ) : get_option( $name );
236
237
		if ( false === $value && false !== $default ) {
238
			if ( $is_network_option ) {
239
				add_site_option( $name, $default );
240
			} else {
241
				add_option( $name, $default );
242
			}
243
			$value = $default;
244
		}
245
246
		return $value;
247
	}
248
249
	/**
250
	 * Update grouped option
251
	 *
252
	 * @param string $group Options group.
253
	 * @param string $name Options name.
254
	 * @param mixed  $value Options value.
255
	 *
256
	 * @return bool Success or failure.
257
	 */
258
	private static function update_grouped_option( $group, $name, $value ) {
259
		$options = get_option( self::$grouped_options[ $group ] );
260
		if ( ! is_array( $options ) ) {
261
			$options = array();
262
		}
263
		$options[ $name ] = $value;
264
265
		return update_option( self::$grouped_options[ $group ], $options );
266
	}
267
268
	/**
269
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
270
	 *
271
	 * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
272
	 * @param mixed  $value Option value.
273
	 * @param string $autoload If not compact option, allows specifying whether to autoload or not.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $autoload not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
274
	 *
275
	 * @return bool Was the option successfully updated?
276
	 */
277
	public static function update_option( $name, $value, $autoload = null ) {
278
		/**
279
		 * Fires before Jetpack updates a specific option.
280
		 *
281
		 * @since 3.0.0
282
		 *
283
		 * @param str $name The name of the option being updated.
284
		 * @param mixed $value The new value of the option.
285
		 */
286
		do_action( 'pre_update_jetpack_option_' . $name, $name, $value );
287 View Code Duplication
		if ( self::is_valid( $name, 'non_compact' ) ) {
288
			if ( self::is_network_option( $name ) ) {
289
				return update_site_option( "jetpack_$name", $value );
290
			}
291
292
			return update_option( "jetpack_$name", $value, $autoload );
293
294
		}
295
296
		foreach ( array_keys( self::$grouped_options ) as $group ) {
297
			if ( self::is_valid( $name, $group ) ) {
298
				return self::update_grouped_option( $group, $name, $value );
299
			}
300
		}
301
302
		trigger_error( sprintf( 'Invalid Jetpack option name: %s', esc_html( $name ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Don't want to change legacy behavior.
303
304
		return false;
305
	}
306
307
	/**
308
	 * Updates the multiple given options.  Updates jetpack_options and/or jetpack_$name as appropriate.
309
	 *
310
	 * @param array $array array( option name => option value, ... ).
311
	 */
312
	public static function update_options( $array ) {
313
		$names = array_keys( $array );
314
315
		foreach ( array_diff( $names, self::get_option_names(), self::get_option_names( 'non_compact' ), self::get_option_names( 'private' ) ) as $unknown_name ) {
316
			trigger_error( sprintf( 'Invalid Jetpack option name: %s', esc_html( $unknown_name ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Don't change legacy behavior.
317
			unset( $array[ $unknown_name ] );
318
		}
319
320
		foreach ( $names as $name ) {
321
			self::update_option( $name, $array[ $name ] );
322
		}
323
	}
324
325
	/**
326
	 * Deletes the given option.  May be passed multiple option names as an array.
327
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
328
	 *
329
	 * @param string|array $names Option names. They must come _without_ `jetpack_%` prefix. The method will prefix the option names.
330
	 *
331
	 * @return bool Was the option successfully deleted?
332
	 */
333
	public static function delete_option( $names ) {
334
		$result = true;
335
		$names  = (array) $names;
336
337
		if ( ! self::is_valid( $names ) ) {
0 ignored issues
show
Documentation introduced by
$names is of type array, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
338
			// phpcs:disable -- This line triggers a handful of errors; ignoring to avoid changing legacy behavior.
339
			trigger_error( sprintf( 'Invalid Jetpack option names: %s', print_r( $names, 1 ) ), E_USER_WARNING );
340
			// phpcs:enable
341
			return false;
342
		}
343
344
		foreach ( array_intersect( $names, self::get_option_names( 'non_compact' ) ) as $name ) {
345
			if ( self::is_network_option( $name ) ) {
346
				$result = delete_site_option( "jetpack_$name" );
347
			} else {
348
				$result = delete_option( "jetpack_$name" );
349
			}
350
		}
351
352
		foreach ( array_keys( self::$grouped_options ) as $group ) {
353
			if ( ! self::delete_grouped_option( $group, $names ) ) {
354
				$result = false;
355
			}
356
		}
357
358
		return $result;
359
	}
360
361
	/**
362
	 * Get group option.
363
	 *
364
	 * @param string $group Option group name.
365
	 * @param string $name Option name.
366
	 * @param mixed  $default Default option value.
367
	 *
368
	 * @return mixed Option.
369
	 */
370
	private static function get_grouped_option( $group, $name, $default ) {
371
		$options = get_option( self::$grouped_options[ $group ] );
372
		if ( is_array( $options ) && isset( $options[ $name ] ) ) {
373
			return $options[ $name ];
374
		}
375
376
		return $default;
377
	}
378
379
	/**
380
	 * Delete grouped option.
381
	 *
382
	 * @param string $group Option group name.
383
	 * @param array  $names Option names.
384
	 *
385
	 * @return bool Success or failure.
386
	 */
387
	private static function delete_grouped_option( $group, $names ) {
388
		$options = get_option( self::$grouped_options[ $group ], array() );
389
390
		$to_delete = array_intersect( $names, self::get_option_names( $group ), array_keys( $options ) );
391
		if ( $to_delete ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $to_delete of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
392
			foreach ( $to_delete as $name ) {
393
				unset( $options[ $name ] );
394
			}
395
396
			return update_option( self::$grouped_options[ $group ], $options );
397
		}
398
399
		return true;
400
	}
401
402
	/*
403
	 * Raw option methods allow Jetpack to get / update / delete options via direct DB queries, including options
404
	 * that are not created by the Jetpack plugin. This is helpful only in rare cases when we need to bypass
405
	 * cache and filters.
406
	 */
407
408
	/**
409
	 * Deletes an option via $wpdb query.
410
	 *
411
	 * @param string $name Option name.
412
	 *
413
	 * @return bool Is the option deleted?
414
	 */
415
	public static function delete_raw_option( $name ) {
416
		if ( self::bypass_raw_option( $name ) ) {
417
			return delete_option( $name );
418
		}
419
		global $wpdb;
420
		$result = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->options WHERE option_name = %s", $name ) );
421
		return $result;
422
	}
423
424
	/**
425
	 * Updates an option via $wpdb query.
426
	 *
427
	 * @param string $name Option name.
428
	 * @param mixed  $value Option value.
429
	 * @param bool   $autoload Specifying whether to autoload or not.
430
	 *
431
	 * @return bool Is the option updated?
432
	 */
433
	public static function update_raw_option( $name, $value, $autoload = false ) {
434
		if ( self::bypass_raw_option( $name ) ) {
435
			return update_option( $name, $value, $autoload );
436
		}
437
		global $wpdb;
438
		$autoload_value = $autoload ? 'yes' : 'no';
439
440
		$old_value = $wpdb->get_var(
441
			$wpdb->prepare(
442
				"SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1",
443
				$name
444
			)
445
		);
446
		if ( $old_value === $value ) {
447
			return false;
448
		}
449
450
		$serialized_value = maybe_serialize( $value );
451
		// below we used "insert ignore" to at least suppress the resulting error.
452
		$updated_num = $wpdb->query(
453
			$wpdb->prepare(
454
				"UPDATE $wpdb->options SET option_value = %s WHERE option_name = %s",
455
				$serialized_value,
456
				$name
457
			)
458
		);
459
460
		// Try inserting the option if the value doesn't exits.
461
		if ( ! $updated_num ) {
462
			$updated_num = $wpdb->query(
463
				$wpdb->prepare(
464
					"INSERT IGNORE INTO $wpdb->options ( option_name, option_value, autoload ) VALUES ( %s, %s, %s )",
465
					$name,
466
					$serialized_value,
467
					$autoload_value
468
				)
469
			);
470
		}
471
		return (bool) $updated_num;
472
	}
473
474
	/**
475
	 * Gets an option via $wpdb query.
476
	 *
477
	 * @since 5.4.0
478
	 *
479
	 * @param string $name Option name.
480
	 * @param mixed  $default Default option value if option is not found.
481
	 *
482
	 * @return mixed Option value, or null if option is not found and default is not specified.
483
	 */
484
	public static function get_raw_option( $name, $default = null ) {
485
		if ( self::bypass_raw_option( $name ) ) {
486
			return get_option( $name, $default );
487
		}
488
489
		global $wpdb;
490
		$value = $wpdb->get_var(
491
			$wpdb->prepare(
492
				"SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1",
493
				$name
494
			)
495
		);
496
		$value = maybe_unserialize( $value );
497
498
		if ( null === $value && null !== $default ) {
499
			return $default;
500
		}
501
502
		return $value;
503
	}
504
505
	/**
506
	 * This function checks for a constant that, if present, will disable direct DB queries Jetpack uses to manage certain options and force Jetpack to always use Options API instead.
507
	 * Options can be selectively managed via a blocklist by filtering option names via the jetpack_disabled_raw_option filter.
508
	 *
509
	 * @param string $name Option name.
510
	 *
511
	 * @return bool
512
	 */
513
	public static function bypass_raw_option( $name ) {
514
515
		if ( Constants::get_constant( 'JETPACK_DISABLE_RAW_OPTIONS' ) ) {
516
			return true;
517
		}
518
		/**
519
		 * Allows to disable particular raw options.
520
		 *
521
		 * @since 5.5.0
522
		 *
523
		 * @param array $disabled_raw_options An array of option names that you can selectively blocklist from being managed via direct database queries.
524
		 */
525
		$disabled_raw_options = apply_filters( 'jetpack_disabled_raw_options', array() );
526
		return isset( $disabled_raw_options[ $name ] );
527
	}
528
529
	/**
530
	 * Gets all known options that are used by Jetpack and managed by Jetpack_Options.
531
	 *
532
	 * @since 5.4.0
533
	 *
534
	 * @param boolean $strip_unsafe_options If true, and by default, will strip out options necessary for the connection to WordPress.com.
535
	 * @return array An array of all options managed via the Jetpack_Options class.
536
	 */
537
	public static function get_all_jetpack_options( $strip_unsafe_options = true ) {
538
		$jetpack_options            = self::get_option_names();
539
		$jetpack_options_non_compat = self::get_option_names( 'non_compact' );
540
		$jetpack_options_private    = self::get_option_names( 'private' );
541
542
		$all_jp_options = array_merge( $jetpack_options, $jetpack_options_non_compat, $jetpack_options_private );
543
544
		if ( $strip_unsafe_options ) {
545
			// Flag some Jetpack options as unsafe.
546
			$unsafe_options = array(
547
				'id',                           // (int)    The Client ID/WP.com Blog ID of this site.
548
				'master_user',                  // (int)    The local User ID of the user who connected this site to jetpack.wordpress.com.
549
				'version',                      // (string) Used during upgrade procedure to auto-activate new modules. version:time
550
551
				// non_compact.
552
				'activated',
553
554
				// private.
555
				'register',
556
				'blog_token',                  // (string) The Client Secret/Blog Token of this site.
557
				'user_token',                  // (string) The User Token of this site. (deprecated)
558
				'user_tokens',
559
			);
560
561
			// Remove the unsafe Jetpack options.
562
			foreach ( $unsafe_options as $unsafe_option ) {
563
				$key = array_search( $unsafe_option, $all_jp_options, true );
564
				if ( false !== $key ) {
565
					unset( $all_jp_options[ $key ] );
566
				}
567
			}
568
		}
569
570
		return $all_jp_options;
571
	}
572
573
	/**
574
	 * Get all options that are not managed by the Jetpack_Options class that are used by Jetpack.
575
	 *
576
	 * @since 5.4.0
577
	 *
578
	 * @return array
579
	 */
580
	public static function get_all_wp_options() {
581
		// A manual build of the wp options.
582
		return array(
583
			'sharing-options',
584
			'disabled_likes',
585
			'disabled_reblogs',
586
			'jetpack_comments_likes_enabled',
587
			'stats_options',
588
			'stats_dashboard_widget',
589
			'safecss_preview_rev',
590
			'safecss_rev',
591
			'safecss_revision_migrated',
592
			'nova_menu_order',
593
			'jetpack_portfolio',
594
			'jetpack_portfolio_posts_per_page',
595
			'jetpack_testimonial',
596
			'jetpack_testimonial_posts_per_page',
597
			'sharedaddy_disable_resources',
598
			'sharing-options',
599
			'sharing-services',
600
			'site_icon_temp_data',
601
			'featured-content',
602
			'site_logo',
603
			'jetpack_dismissed_notices',
604
			'jetpack-twitter-cards-site-tag',
605
			'jetpack-sitemap-state',
606
			'jetpack_sitemap_post_types',
607
			'jetpack_sitemap_location',
608
			'jetpack_protect_key',
609
			'jetpack_protect_blocked_attempts',
610
			'jetpack_protect_activating',
611
			'jetpack_connection_banner_ab',
612
			'jetpack_active_plan',
613
			'jetpack_activation_source',
614
			'jetpack_sso_match_by_email',
615
			'jetpack_sso_require_two_step',
616
			'jetpack_sso_remove_login_form',
617
			'jetpack_last_connect_url_check',
618
			'jpo_business_address',
619
			'jpo_site_type',
620
			'jpo_homepage_format',
621
			'jpo_contact_page',
622
			'jetpack_excluded_extensions',
623
		);
624
	}
625
626
	/**
627
	 * Gets all options that can be safely reset by CLI.
628
	 *
629
	 * @since 5.4.0
630
	 *
631
	 * @return array array Associative array containing jp_options which are managed by the Jetpack_Options class and wp_options which are not.
632
	 */
633
	public static function get_options_for_reset() {
634
		$all_jp_options = self::get_all_jetpack_options();
635
636
		$wp_options = self::get_all_wp_options();
637
638
		$options = array(
639
			'jp_options' => $all_jp_options,
640
			'wp_options' => $wp_options,
641
		);
642
643
		return $options;
644
	}
645
646
	/**
647
	 * Delete all known options
648
	 *
649
	 * @since 5.4.0
650
	 *
651
	 * @return void
652
	 */
653
	public static function delete_all_known_options() {
654
		// Delete all compact options.
655
		foreach ( (array) self::$grouped_options as $option_name ) {
656
			delete_option( $option_name );
657
		}
658
659
		// Delete all non-compact Jetpack options.
660
		foreach ( (array) self::get_option_names( 'non-compact' ) as $option_name ) {
661
			self::delete_option( $option_name );
662
		}
663
664
		// Delete all options that can be reset via CLI, that aren't Jetpack options.
665
		foreach ( (array) self::get_all_wp_options() as $option_name ) {
666
			delete_option( $option_name );
667
		}
668
	}
669
}
670