Completed
Push — revert-12562-try/jetpack-optio... ( 0c0b65 )
by Marin
27:55 queued 19:47
created

Manager::is_valid()   B

Complexity

Conditions 10
Paths 11

Size

Total Lines 28

Duplication

Lines 10
Ratio 35.71 %

Importance

Changes 0
Metric Value
cc 10
nc 11
nop 2
dl 10
loc 28
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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