Completed
Push — add/is-active-to-connection-pa... ( d8a57d...400dfb )
by
unknown
169:50 queued 162:40
created

Manager   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 282
Duplicated Lines 26.95 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
dl 76
loc 282
rs 8.96
c 0
b 0
f 0
wmc 43
lcom 1
cbo 1

11 Methods

Rating   Name   Duplication   Size   Complexity  
A get_option() 7 19 5
A get_grouped_option() 8 8 3
A update_option() 8 29 5
A update_grouped_option() 0 9 2
B delete_option() 5 25 6
A delete_grouped_option() 7 14 3
B is_valid() 10 28 10
A is_network_option() 0 6 2
A get_raw_option() 17 17 4
A bypass_raw_option() 14 14 2
A get_option_names() 0 3 1

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

1
<?php
2
/**
3
 * The Jetpack Options manager class file.
4
 *
5
 * @package jetpack-options
6
 */
7
8
namespace Automattic\Jetpack\Options;
9
10
/**
11
 * The Jetpack Options Manager class that is used as a single gateway between WordPress options API
12
 * and Jetpack.
13
 */
14
class Manager {
15
16
	/**
17
	 * An array that maps a grouped option type to an option name.
18
	 *
19
	 * @var array
20
	 */
21
	protected $grouped_options = array(
22
		'compact' => 'jetpack_options',
23
		'private' => 'jetpack_private_options',
24
	);
25
26
	/**
27
	 * Returns an array of option names for a given type.
28
	 *
29
	 * @param string $type The type of option to return. Defaults to 'compact'.
30
	 *
31
	 * @return array
32
	 */
33
	public function get_option_names( $type ) {
34
		return array();
35
	}
36
37
	/**
38
	 * Returns the requested option.  Looks in jetpack_options or jetpack_$name as appropriate.
39
	 *
40
	 * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
41
	 * @param mixed  $default (optional) the default value.
42
	 *
43
	 * @return mixed
44
	 */
45
	public function get_option( $name, $default = false ) {
46 View Code Duplication
		if ( $this->is_valid( $name, 'non_compact' ) ) {
47
			if ( $this->is_network_option( $name ) ) {
48
				return get_site_option( "jetpack_$name", $default );
49
			}
50
51
			return get_option( "jetpack_$name", $default );
52
		}
53
54
		foreach ( array_keys( $this->grouped_options ) as $group ) {
55
			if ( $this->is_valid( $name, $group ) ) {
56
				return $this->get_grouped_option( $group, $name, $default );
57
			}
58
		}
59
60
		// TODO: throw an exception here?
61
62
		return $default;
63
	}
64
65
	/**
66
	 * Returns a single value from a grouped option.
67
	 *
68
	 * @param String $group   name of the group, i.e., 'private'.
69
	 * @param String $name    the name of the option to return.
70
	 * @param Mixed  $default a default value in case the option is not found.
71
	 * @return Mixed the option value or default if not found.
72
	 */
73 View Code Duplication
	protected function get_grouped_option( $group, $name, $default ) {
74
		$options = get_option( $this->grouped_options[ $group ] );
75
		if ( is_array( $options ) && isset( $options[ $name ] ) ) {
76
			return $options[ $name ];
77
		}
78
79
		return $default;
80
	}
81
82
	/**
83
	 * Updates the single given option.  Updates jetpack_options or jetpack_$name as appropriate.
84
	 *
85
	 * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
86
	 * @param mixed  $value Option value.
87
	 * @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...
88
	 *
89
	 * @return bool Was the option successfully updated?
90
	 */
91
	public function update_option( $name, $value, $autoload = null ) {
92
		/**
93
		 * Fires before Jetpack updates a specific option.
94
		 *
95
		 * @since 3.0.0
96
		 *
97
		 * @param str $name The name of the option being updated.
98
		 * @param mixed $value The new value of the option.
99
		 */
100
		do_action( 'pre_update_jetpack_option_' . $name, $name, $value );
101 View Code Duplication
		if ( $this->is_valid( $name, 'non_compact' ) ) {
102
			if ( $this->is_network_option( $name ) ) {
103
				return update_site_option( "jetpack_$name", $value );
104
			}
105
106
			return update_option( "jetpack_$name", $value, $autoload );
107
108
		}
109
110
		foreach ( array_keys( $this->grouped_options ) as $group ) {
111
			if ( $this->is_valid( $name, $group ) ) {
112
				return $this->update_grouped_option( $group, $name, $value );
113
			}
114
		}
115
116
		// TODO: throw an exception here?
117
118
		return false;
119
	}
120
121
	/**
122
	 * Updates a single value from a grouped option.
123
	 *
124
	 * @param String $group name of the group, i.e., 'private'.
125
	 * @param String $name  the name of the option to update.
126
	 * @param Mixed  $value the to update the option with.
127
	 * @return Boolean was the update successful?
128
	 */
129
	protected function update_grouped_option( $group, $name, $value ) {
130
		$options = get_option( $this->grouped_options[ $group ] );
131
		if ( ! is_array( $options ) ) {
132
			$options = array();
133
		}
134
		$options[ $name ] = $value;
135
136
		return update_option( $this->grouped_options[ $group ], $options );
137
	}
138
139
	/**
140
	 * Deletes the given option.  May be passed multiple option names as an array.
141
	 * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
142
	 *
143
	 * @param string|array $names Option names. They must come _without_ `jetpack_%` prefix. The method will prefix the option names.
144
	 *
145
	 * @return bool Was the option successfully deleted?
146
	 */
147
	public function delete_option( $names ) {
148
		$result = true;
149
		$names  = (array) $names;
150
151
		if ( ! $this->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...
152
			// TODO: issue a warning here?
153
			return false;
154
		}
155
156
		foreach ( array_intersect( $names, $this->get_option_names( 'non_compact' ) ) as $name ) {
157 View Code Duplication
			if ( $this->is_network_option( $name ) ) {
158
				$result = delete_site_option( "jetpack_$name" );
159
			} else {
160
				$result = delete_option( "jetpack_$name" );
161
			}
162
		}
163
164
		foreach ( array_keys( $this->grouped_options ) as $group ) {
165
			if ( ! $this->delete_grouped_option( $group, $names ) ) {
166
				$result = false;
167
			}
168
		}
169
170
		return $result;
171
	}
172
173
	/**
174
	 * Deletes a single value from a grouped option.
175
	 *
176
	 * @param String $group   name of the group, i.e., 'private'.
177
	 * @param Array  $names   the names of the option to delete.
178
	 * @return Mixed the option value or default if not found.
179
	 */
180
	protected function delete_grouped_option( $group, $names ) {
181
		$options = get_option( $this->grouped_options[ $group ], array() );
182
183
		$to_delete = array_intersect( $names, $this->get_option_names( $group ), array_keys( $options ) );
184 View Code Duplication
		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...
185
			foreach ( $to_delete as $name ) {
186
				unset( $options[ $name ] );
187
			}
188
189
			return update_option( $this->grouped_options[ $group ], $options );
190
		}
191
192
		return true;
193
	}
194
195
	/**
196
	 * Is the option name valid?
197
	 *
198
	 * @param string      $name  The name of the option.
199
	 * @param string|null $group The name of the group that the option is in. Default to null, which will search non_compact.
200
	 *
201
	 * @return bool Is the option name valid?
202
	 */
203
	public function is_valid( $name, $group = null ) {
204
		if ( is_array( $name ) ) {
205
			$compact_names = array();
206
			foreach ( array_keys( $this->grouped_options ) as $_group ) {
207
				$compact_names = array_merge( $compact_names, $this->get_option_names( $_group ) );
208
			}
209
210
			$result = array_diff( $name, $this->get_option_names( 'non_compact' ), $compact_names );
211
212
			return empty( $result );
213
		}
214
215 View Code Duplication
		if ( is_null( $group ) || 'non_compact' === $group ) {
216
			if ( in_array( $name, $this->get_option_names( $group ), true ) ) {
217
				return true;
218
			}
219
		}
220
221
		foreach ( array_keys( $this->grouped_options ) as $_group ) {
222 View Code Duplication
			if ( is_null( $group ) || $group === $_group ) {
223
				if ( in_array( $name, $this->get_option_names( $_group ), true ) ) {
224
					return true;
225
				}
226
			}
227
		}
228
229
		return false;
230
	}
231
232
	/**
233
	 * Checks if an option must be saved for the whole network in WP Multisite
234
	 *
235
	 * @param string $option_name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
236
	 *
237
	 * @return bool
238
	 */
239
	public function is_network_option( $option_name ) {
240
		if ( ! is_multisite() ) {
241
			return false;
242
		}
243
		return in_array( $option_name, $this->get_option_names( 'network' ), true );
244
	}
245
246
	/**
247
	 * Gets an option via $wpdb query.
248
	 *
249
	 * @since 5.4.0
250
	 *
251
	 * @param string $name Option name.
252
	 * @param mixed  $default Default option value if option is not found.
253
	 *
254
	 * @return mixed Option value, or null if option is not found and default is not specified.
255
	 */
256 View Code Duplication
	function get_raw_option( $name, $default = null ) {
257
		if ( $this->bypass_raw_option( $name ) ) {
258
			return get_option( $name, $default );
259
		}
260
		global $wpdb;
261
		$value = $wpdb->get_var(
262
			$wpdb->prepare(
263
				"SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1",
264
				$name
265
			)
266
		);
267
		$value = maybe_unserialize( $value );
268
		if ( $value === null && $default !== null ) {
269
			return $default;
270
		}
271
		return $value;
272
	}
273
	/**
274
	 * 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.
275
	 * Options can be selectively managed via a blacklist by filtering option names via the jetpack_disabled_raw_option filter.
276
	 *
277
	 * @param $name Option name
278
	 *
279
	 * @return bool
280
	 */
281 View Code Duplication
	function bypass_raw_option( $name ) {
282
		if ( \Jetpack_Constants::get_constant( 'JETPACK_DISABLE_RAW_OPTIONS' ) ) {
283
			return true;
284
		}
285
		/**
286
		 * Allows to disable particular raw options.
287
		 *
288
		 * @since 5.5.0
289
		 *
290
		 * @param array $disabled_raw_options An array of option names that you can selectively blacklist from being managed via direct database queries.
291
		 */
292
		$disabled_raw_options = apply_filters( 'jetpack_disabled_raw_options', array() );
293
		return isset( $disabled_raw_options[ $name ] );
294
	}
295
}
296