Completed
Push — kraftbj-patch-2 ( 82c983...ae9d16 )
by
unknown
513:58 queued 503:20
created

Jetpack_Options   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 325
Duplicated Lines 7.69 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 0
Metric Value
wmc 49
lcom 1
cbo 0
dl 25
loc 325
rs 8.5454
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
B get_option_names() 0 71 5
D is_valid() 10 28 10
A is_network_option() 0 6 2
B get_option() 7 19 5
B get_option_and_ensure_autoload() 0 19 5
A update_grouped_option() 0 9 2
B update_option() 8 29 5
A update_options() 0 12 3
B delete_option() 0 26 6
A get_grouped_option() 0 8 3
A delete_grouped_option() 0 14 3

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_Options 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_Options, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
class Jetpack_Options {
4
5
	/**
6
	 * An array that maps a grouped option type to an option name.
7
	 * @var array
8
	 */
9
	private static $grouped_options = array(
10
		'compact' => 'jetpack_options',
11
		'private' => 'jetpack_private_options'
12
	);
13
14
	/**
15
	 * Returns an array of option names for a given type.
16
	 *
17
	 * @param string $type The type of option to return. Defaults to 'compact'.
18
	 *
19
	 * @return array
20
	 */
21
	public static function get_option_names( $type = 'compact' ) {
22
		switch ( $type ) {
23
		case 'non-compact' :
24
		case 'non_compact' :
25
			return array(
26
				'activated',
27
				'active_modules',
28
				'available_modules',
29
				'do_activate',
30
				'log',
31
				'publicize',
32
				'slideshow_background_color',
33
				'widget_twitter',
34
				'wpcc_options',
35
				'relatedposts',
36
				'file_data',
37
				'autoupdate_plugins',          // (array)  An array of plugin ids ( eg. jetpack/jetpack ) that should be autoupdated
38
				'autoupdate_plugins_translations', // (array)  An array of plugin ids ( eg. jetpack/jetpack ) that should be autoupdated translation files.
39
				'autoupdate_themes',           // (array)  An array of theme ids ( eg. twentyfourteen ) that should be autoupdated
40
				'autoupdate_themes_translations', // (array)  An array of theme ids ( eg. twentyfourteen ) that should autoupdated translation files.
41
				'autoupdate_core',             // (bool)   Whether or not to autoupdate core
42
				'autoupdate_translations',     // (bool)   Whether or not to autoupdate all translations
43
				'json_api_full_management',    // (bool)   Allow full management (eg. Activate, Upgrade plugins) of the site via the JSON API.
44
				'sync_non_public_post_stati',  // (bool)   Allow synchronisation of posts and pages with non-public status.
45
				'site_icon_url',               // (string) url to the full site icon
46
				'site_icon_id',                // (int)    Attachment id of the site icon file
47
				'dismissed_manage_banner',     // (bool) Dismiss Jetpack manage banner allows the user to dismiss the banner permanently
48
				'restapi_stats_cache',         // (array) Stats Cache data.
49
				'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
50
				'protect_whitelist',           // (array) IP Address for the Protect module to ignore
51
				'sync_error_idc',              // (bool|array) false or array containing the site's home and siteurl at time of IDC error
52
				'safe_mode_confirmed',         // (bool) True if someone confirms that this site was correctly put into safe mode automatically after an identity crisis is discovered.
53
				'migrate_for_idc',             // (bool) True if someone confirms that this site should migrate stats and subscribers from its previous URL
54
			);
55
56
		case 'private' :
57
			return array(
58
				'register',
59
				'authorize',
60
				'activate_manage',
61
				'blog_token',                  // (string) The Client Secret/Blog Token of this site.
62
				'user_token',                  // (string) The User Token of this site. (deprecated)
63
				'user_tokens'                  // (array)  User Tokens for each user of this site who has connected to jetpack.wordpress.com.
64
			);
65
66
		case 'network' :
67
			return array(
68
				'file_data'                     // (array) List of absolute paths to all Jetpack modules
69
			);
70
		}
71
72
		return array(
73
			'id',                           // (int)    The Client ID/WP.com Blog ID of this site.
74
			'publicize_connections',        // (array)  An array of Publicize connections from WordPress.com
75
			'master_user',                  // (int)    The local User ID of the user who connected this site to jetpack.wordpress.com.
76
			'version',                      // (string) Used during upgrade procedure to auto-activate new modules. version:time
77
			'old_version',                  // (string) Used to determine which modules are the most recently added. previous_version:time
78
			'fallback_no_verify_ssl_certs', // (int)    Flag for determining if this host must skip SSL Certificate verification due to misconfigured SSL.
79
			'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' )
80
			'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.
81
			'videopress',                   // (array)  VideoPress options array.
82
			'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.
83
			'social_links',                 // (array)  The specified links for each social networking site.
84
			'identity_crisis_whitelist',    // (array)  An array of options, each having an array of the values whitelisted for it.
85
			'gplus_authors',                // (array)  The Google+ authorship information for connected users.
86
			'last_heartbeat',               // (int)    The timestamp of the last heartbeat that fired.
87
			'jumpstart',                    // (string) A flag for whether or not to show the Jump Start.  Accepts: new_connection, jumpstart_activated, jetpack_action_taken, jumpstart_dismissed.
88
			'hide_jitm',                    // (array)  A list of just in time messages that we should not show because they have been dismissed by the user
89
			'custom_css_4.7_migration',     // (bool)   Whether Custom CSS has scanned for and migrated any legacy CSS CPT entries to the new Core format.
90
		);
91
	}
92
93
	/**
94
	 * Is the option name valid?
95
	 *
96
	 * @param string      $name  The name of the option
97
	 * @param string|null $group The name of the group that the option is in. Default to null, which will search non_compact.
98
	 *
99
	 * @return bool Is the option name valid?
100
	 */
101
	public static function is_valid( $name, $group = null ) {
102
		if ( is_array( $name ) ) {
103
			$compact_names = array();
104
			foreach ( array_keys( self::$grouped_options ) as $_group ) {
105
				$compact_names = array_merge( $compact_names, self::get_option_names( $_group ) );
106
			}
107
108
			$result = array_diff( $name, self::get_option_names( 'non_compact' ), $compact_names );
109
110
			return empty( $result );
111
		}
112
113 View Code Duplication
		if ( is_null( $group ) || 'non_compact' === $group ) {
114
			if ( in_array( $name, self::get_option_names( $group ) ) ) {
115
				return true;
116
			}
117
		}
118
119
		foreach ( array_keys( self::$grouped_options ) as $_group ) {
120 View Code Duplication
			if ( is_null( $group ) || $group === $_group ) {
121
				if ( in_array( $name, self::get_option_names( $_group ) ) ) {
122
					return true;
123
				}
124
			}
125
		}
126
127
		return false;
128
	}
129
130
	/**
131
	 * Checks if an option must be saved for the whole network in WP Multisite
132
	 *
133
	 * @param string $option_name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
134
	 *
135
	 * @return bool
136
	 */
137
	public static function is_network_option( $option_name ) {
138
		if ( ! is_multisite() ) {
139
			return false;
140
		}
141
		return in_array( $option_name, self::get_option_names( 'network' ) );
142
	}
143
144
	/**
145
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
146
	 *
147
	 * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
148
	 * @param mixed $default (optional)
149
	 *
150
	 * @return mixed
151
	 */
152
	public static function get_option( $name, $default = false ) {
153 View Code Duplication
		if ( self::is_valid( $name, 'non_compact' ) ) {
154
			if ( self::is_network_option( $name ) ) {
155
				return get_site_option( "jetpack_$name", $default );
156
			}
157
158
			return get_option( "jetpack_$name", $default );
159
		}
160
161
		foreach ( array_keys( self::$grouped_options ) as $group ) {
162
			if ( self::is_valid( $name, $group ) ) {
163
				return self::get_grouped_option( $group, $name, $default );
164
			}
165
		}
166
167
		trigger_error( sprintf( 'Invalid Jetpack option name: %s', $name ), E_USER_WARNING );
168
169
		return $default;
170
	}
171
172
	/**
173
	 * Returns the requested option, and ensures it's autoloaded in the future.
174
	 * This does _not_ adjust the prefix in any way (does not prefix jetpack_%)
175
	 *
176
	 * @param string $name Option name
177
	 * @param mixed $default (optional)
178
	 *
179
	 * @return mixed
180
	 */
181
	public static function get_option_and_ensure_autoload( $name, $default ) {
182
		// In this function the name is not adjusted by prefixing jetpack_
183
		// so if it has already prefixed, we'll replace it and then
184
		// check if the option name is a network option or not
185
		$jetpack_name = preg_replace( '/^jetpack_/', '', $name, 1 );
186
		$is_network_option = self::is_network_option( $jetpack_name );
187
		$value = $is_network_option ? get_site_option( $name ) : get_option( $name );
188
189
		if ( $value === false && $default !== false ) {
190
			if ( $is_network_option ) {
191
				update_site_option( $name, $default );
192
			} else {
193
				update_option( $name, $default );
194
			}
195
			$value = $default;
196
		}
197
198
		return $value;
199
	}
200
201
	private static function update_grouped_option( $group, $name, $value ) {
202
		$options = get_option( self::$grouped_options[ $group ] );
203
		if ( ! is_array( $options ) ) {
204
			$options = array();
205
		}
206
		$options[ $name ] = $value;
207
208
		return update_option( self::$grouped_options[ $group ], $options );
209
	}
210
211
	/**
212
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
213
	 *
214
	 * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
215
	 * @param mixed $value Option value
216
	 * @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...
217
	 *
218
	 * @return bool Was the option successfully updated?
219
	 */
220
	public static function update_option( $name, $value, $autoload = null ) {
221
		/**
222
		 * Fires before Jetpack updates a specific option.
223
		 *
224
		 * @since 3.0.0
225
		 *
226
		 * @param str $name The name of the option being updated.
227
		 * @param mixed $value The new value of the option.
228
		 */
229
		do_action( 'pre_update_jetpack_option_' . $name, $name, $value );
230 View Code Duplication
		if ( self::is_valid( $name, 'non_compact' ) ) {
231
			if ( self::is_network_option( $name ) ) {
232
				return update_site_option( "jetpack_$name", $value );
233
			}
234
235
			return update_option( "jetpack_$name", $value, $autoload );
236
237
		}
238
239
		foreach ( array_keys( self::$grouped_options ) as $group ) {
240
			if ( self::is_valid( $name, $group ) ) {
241
				return self::update_grouped_option( $group, $name, $value );
242
			}
243
		}
244
245
		trigger_error( sprintf( 'Invalid Jetpack option name: %s', $name ), E_USER_WARNING );
246
247
		return false;
248
	}
249
250
	/**
251
	 * Updates the multiple given options.  Updates jetpack_options and/or jetpack_$name as appropriate.
252
	 *
253
	 * @param array $array array( option name => option value, ... )
254
	 */
255
	public static function update_options( $array ) {
256
		$names = array_keys( $array );
257
258
		foreach ( array_diff( $names, self::get_option_names(), self::get_option_names( 'non_compact' ), self::get_option_names( 'private' ) ) as $unknown_name ) {
259
			trigger_error( sprintf( 'Invalid Jetpack option name: %s', $unknown_name ), E_USER_WARNING );
260
			unset( $array[ $unknown_name ] );
261
		}
262
263
		foreach ( $names as $name ) {
264
			self::update_option( $name, $array[ $name ] );
265
		}
266
	}
267
268
	/**
269
	 * Deletes the given option.  May be passed multiple option names as an array.
270
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
271
	 *
272
	 * @param string|array $names Option names. They must come _without_ `jetpack_%` prefix. The method will prefix the option names.
273
	 *
274
	 * @return bool Was the option successfully deleted?
275
	 */
276
	public static function delete_option( $names ) {
277
		$result = true;
278
		$names  = (array) $names;
279
280
		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...
281
			trigger_error( sprintf( 'Invalid Jetpack option names: %s', print_r( $names, 1 ) ), E_USER_WARNING );
282
			return false;
283
		}
284
285
		foreach ( array_intersect( $names, self::get_option_names( 'non_compact' ) ) as $name ) {
286
			if ( self::is_network_option( $name ) ) {
287
				$result = delete_site_option( "jetpack_$name" );
288
			} else {
289
				$result = delete_option( "jetpack_$name" );
290
			}
291
292
		}
293
294
		foreach ( array_keys( self::$grouped_options ) as $group ) {
295
			if ( ! self::delete_grouped_option( $group, $names ) ) {
296
				$result = false;
297
			}
298
		}
299
300
		return $result;
301
	}
302
303
	private static function get_grouped_option( $group, $name, $default ) {
304
		$options = get_option( self::$grouped_options[ $group ] );
305
		if ( is_array( $options ) && isset( $options[ $name ] ) ) {
306
			return $options[ $name ];
307
		}
308
309
		return $default;
310
	}
311
312
	private static function delete_grouped_option( $group, $names ) {
313
		$options = get_option( self::$grouped_options[ $group ], array() );
314
315
		$to_delete = array_intersect( $names, self::get_option_names( $group ), array_keys( $options ) );
316
		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...
317
			foreach ( $to_delete as $name ) {
318
				unset( $options[ $name ] );
319
			}
320
321
			return update_option( self::$grouped_options[ $group ], $options );
322
		}
323
324
		return true;
325
	}
326
327
}
328