Completed
Push — update/import-sync-detection ( 6bda70 )
by
unknown
51:18 queued 44:47
created

Jetpack_Sync_Functions   F

Complexity

Total Complexity 89

Size/Duplication

Total Lines 446
Duplicated Lines 1.79 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 89
lcom 1
cbo 4
dl 8
loc 446
rs 2
c 0
b 0
f 0

29 Methods

Rating   Name   Duplication   Size   Complexity  
A get_modules() 0 5 1
A get_taxonomies() 0 13 3
A get_shortcodes() 0 4 1
B sanitize_taxonomy() 0 26 8
A get_post_types() 0 14 3
A sanitize_post_type() 0 10 3
A expand_synced_post_type() 0 9 1
A get_post_type_features() 0 5 1
B get_hosting_provider() 0 18 9
A rest_api_allowed_post_types() 0 4 1
A rest_api_allowed_public_metadata() 0 4 1
A roles() 0 4 1
A get_timezone() 0 20 2
A get_paused_themes() 0 7 2
A get_paused_plugins() 0 7 2
A is_version_controlled() 0 9 2
A file_system_write_access() 0 26 5
A get_raw_or_filtered_url() 0 18 5
A home_url() 0 12 1
A site_url() 0 12 1
A main_network_site_url() 0 3 1
A get_protocol_normalized_url() 0 24 4
A get_raw_url() 0 18 4
B normalize_www_in_url() 8 28 7
A get_plugins() 0 8 2
A get_plugins_action_links() 0 14 5
A wp_version() 0 4 1
A site_icon_url() 0 4 2
B get_calling_importer_class() 0 42 10

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

1
<?php
2
3
/*
4
 * Utility functions to generate data synced to wpcom
5
 */
6
7
class Jetpack_Sync_Functions {
8
	const HTTPS_CHECK_OPTION_PREFIX = 'jetpack_sync_https_history_';
9
	const HTTPS_CHECK_HISTORY       = 5;
10
11
	public static function get_modules() {
12
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-admin.php';
13
14
		return Jetpack_Admin::init()->get_modules();
15
	}
16
17
	public static function get_taxonomies() {
18
		global $wp_taxonomies;
19
		$wp_taxonomies_without_callbacks = array();
20
		foreach ( $wp_taxonomies as $taxonomy_name => $taxonomy ) {
21
			$sanitized_taxonomy = self::sanitize_taxonomy( $taxonomy );
22
			if ( ! empty( $sanitized_taxonomy ) ) {
23
				$wp_taxonomies_without_callbacks[ $taxonomy_name ] = $sanitized_taxonomy;
24
			} else {
25
				error_log( 'Jetpack: Encountered a recusive taxonomy:' . $taxonomy_name );
26
			}
27
		}
28
		return $wp_taxonomies_without_callbacks;
29
	}
30
31
	public static function get_shortcodes() {
32
		global $shortcode_tags;
33
		return array_keys( $shortcode_tags );
34
	}
35
36
	/**
37
	 * Removes any callback data since we will not be able to process it on our side anyways.
38
	 */
39
	public static function sanitize_taxonomy( $taxonomy ) {
40
41
		// Lets clone the taxonomy object instead of modifing the global one.
42
		$cloned_taxonomy = json_decode( wp_json_encode( $taxonomy ) );
43
44
		// recursive taxonomies are no fun.
45
		if ( is_null( $cloned_taxonomy ) ) {
46
			return null;
47
		}
48
		// Remove any meta_box_cb if they are not the default wp ones.
49
		if ( isset( $cloned_taxonomy->meta_box_cb ) &&
50
			 ! in_array( $cloned_taxonomy->meta_box_cb, array( 'post_tags_meta_box', 'post_categories_meta_box' ) ) ) {
51
			$cloned_taxonomy->meta_box_cb = null;
52
		}
53
		// Remove update call back
54
		if ( isset( $cloned_taxonomy->update_count_callback ) &&
55
			 ! is_null( $cloned_taxonomy->update_count_callback ) ) {
56
			$cloned_taxonomy->update_count_callback = null;
57
		}
58
		// Remove rest_controller_class if it something other then the default.
59
		if ( isset( $cloned_taxonomy->rest_controller_class ) &&
60
			 'WP_REST_Terms_Controller' !== $cloned_taxonomy->rest_controller_class ) {
61
			$cloned_taxonomy->rest_controller_class = null;
62
		}
63
		return $cloned_taxonomy;
64
	}
65
66
	public static function get_post_types() {
67
		global $wp_post_types;
68
69
		$post_types_without_callbacks = array();
70
		foreach ( $wp_post_types as $post_type_name => $post_type ) {
71
			$sanitized_post_type = self::sanitize_post_type( $post_type );
72
			if ( ! empty( $sanitized_post_type ) ) {
73
				$post_types_without_callbacks[ $post_type_name ] = $sanitized_post_type;
74
			} else {
75
				error_log( 'Jetpack: Encountered a recusive post_type:' . $post_type_name );
76
			}
77
		}
78
		return $post_types_without_callbacks;
79
	}
80
81
	public static function sanitize_post_type( $post_type ) {
82
		// Lets clone the post type object instead of modifing the global one.
83
		$sanitized_post_type = array();
84
		foreach ( Jetpack_Sync_Defaults::$default_post_type_attributes as $attribute_key => $default_value ) {
0 ignored issues
show
Bug introduced by
The property default_post_type_attributes cannot be accessed from this context as it is declared private in class Jetpack_Sync_Defaults.

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...
85
			if ( isset( $post_type->{ $attribute_key } ) ) {
86
				$sanitized_post_type[ $attribute_key ] = $post_type->{ $attribute_key };
87
			}
88
		}
89
		return (object) $sanitized_post_type;
90
	}
91
92
	public static function expand_synced_post_type( $sanitized_post_type, $post_type ) {
93
		$post_type        = sanitize_key( $post_type );
94
		$post_type_object = new WP_Post_Type( $post_type, $sanitized_post_type );
95
		$post_type_object->add_supports();
96
		$post_type_object->add_rewrite_rules();
97
		$post_type_object->add_hooks();
98
		$post_type_object->register_taxonomies();
99
		return (object) $post_type_object;
100
	}
101
102
	public static function get_post_type_features() {
103
		global $_wp_post_type_features;
104
105
		return $_wp_post_type_features;
106
	}
107
108
	public static function get_hosting_provider() {
109
		if ( defined( 'GD_SYSTEM_PLUGIN_DIR' ) || class_exists( '\\WPaaS\\Plugin' ) ) {
110
			return 'gd-managed-wp';
111
		}
112
		if ( defined( 'MM_BASE_DIR' ) ) {
113
			return 'bh';
114
		}
115
		if ( defined( 'IS_PRESSABLE' ) ) {
116
			return 'pressable';
117
		}
118
		if ( function_exists( 'is_wpe' ) || function_exists( 'is_wpe_snapshot' ) ) {
119
			return 'wpe';
120
		}
121
		if ( defined( 'VIP_GO_ENV' ) && false !== VIP_GO_ENV ) {
122
			return 'vip-go';
123
		}
124
		return 'unknown';
125
	}
126
127
	public static function rest_api_allowed_post_types() {
128
		/** This filter is already documented in class.json-api-endpoints.php */
129
		return apply_filters( 'rest_api_allowed_post_types', array( 'post', 'page', 'revision' ) );
130
	}
131
132
	public static function rest_api_allowed_public_metadata() {
133
		/** This filter is documented in json-endpoints/class.wpcom-json-api-post-endpoint.php */
134
		return apply_filters( 'rest_api_allowed_public_metadata', array() );
135
	}
136
137
	/**
138
	 * Finds out if a site is using a version control system.
139
	 *
140
	 * @return bool
141
	 **/
142
	public static function is_version_controlled() {
143
144
		if ( ! class_exists( 'WP_Automatic_Updater' ) ) {
145
			require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
146
		}
147
		$updater = new WP_Automatic_Updater();
148
149
		return (bool) strval( $updater->is_vcs_checkout( $context = ABSPATH ) );
150
	}
151
152
	/**
153
	 * Returns true if the site has file write access false otherwise.
154
	 *
155
	 * @return bool
156
	 **/
157
	public static function file_system_write_access() {
158
		if ( ! function_exists( 'get_filesystem_method' ) ) {
159
			require_once ABSPATH . 'wp-admin/includes/file.php';
160
		}
161
162
		require_once ABSPATH . 'wp-admin/includes/template.php';
163
164
		$filesystem_method = get_filesystem_method();
165
		if ( 'direct' === $filesystem_method ) {
166
			return true;
167
		}
168
169
		ob_start();
170
171
		if ( ! function_exists( 'request_filesystem_credentials' ) ) {
172
			require_once ABSPATH . 'wp-admin/includes/file.php';
173
		}
174
175
		$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
176
		ob_end_clean();
177
		if ( $filesystem_credentials_are_stored ) {
178
			return true;
179
		}
180
181
		return false;
182
	}
183
184
	/**
185
	 * Helper function that is used when getting home or siteurl values. Decides
186
	 * whether to get the raw or filtered value.
187
	 *
188
	 * @return string
189
	 */
190
	public static function get_raw_or_filtered_url( $url_type ) {
191
		$url_function = ( 'home' == $url_type )
192
			? 'home_url'
193
			: 'site_url';
194
195
		if (
196
			! Jetpack_Constants::is_defined( 'JETPACK_SYNC_USE_RAW_URL' ) ||
197
			Jetpack_Constants::get_constant( 'JETPACK_SYNC_USE_RAW_URL' )
198
		) {
199
			$scheme = is_ssl() ? 'https' : 'http';
200
			$url    = self::get_raw_url( $url_type );
201
			$url    = set_url_scheme( $url, $scheme );
202
		} else {
203
			$url = self::normalize_www_in_url( $url_type, $url_function );
204
		}
205
206
		return self::get_protocol_normalized_url( $url_function, $url );
207
	}
208
209
	public static function home_url() {
210
		$url = self::get_raw_or_filtered_url( 'home' );
211
212
		/**
213
		 * Allows overriding of the home_url value that is synced back to WordPress.com.
214
		 *
215
		 * @since 5.2.0
216
		 *
217
		 * @param string $home_url
218
		 */
219
		return esc_url_raw( apply_filters( 'jetpack_sync_home_url', $url ) );
220
	}
221
222
	public static function site_url() {
223
		$url = self::get_raw_or_filtered_url( 'siteurl' );
224
225
		/**
226
		 * Allows overriding of the site_url value that is synced back to WordPress.com.
227
		 *
228
		 * @since 5.2.0
229
		 *
230
		 * @param string $site_url
231
		 */
232
		return esc_url_raw( apply_filters( 'jetpack_sync_site_url', $url ) );
233
	}
234
235
	public static function main_network_site_url() {
236
		return self::get_protocol_normalized_url( 'main_network_site_url', network_site_url() );
237
	}
238
239
	public static function get_protocol_normalized_url( $callable, $new_value ) {
240
		$option_key = self::HTTPS_CHECK_OPTION_PREFIX . $callable;
241
242
		$parsed_url = wp_parse_url( $new_value );
243
		if ( ! $parsed_url ) {
244
			return $new_value;
245
		}
246
		if ( array_key_exists( 'scheme', $parsed_url ) ) {
247
			$scheme = $parsed_url['scheme'];
248
		} else {
249
			$scheme = '';
250
		}
251
		$scheme_history   = get_option( $option_key, array() );
252
		$scheme_history[] = $scheme;
253
254
		// Limit length to self::HTTPS_CHECK_HISTORY
255
		$scheme_history = array_slice( $scheme_history, ( self::HTTPS_CHECK_HISTORY * -1 ) );
256
257
		update_option( $option_key, $scheme_history );
258
259
		$forced_scheme = in_array( 'https', $scheme_history ) ? 'https' : 'http';
260
261
		return set_url_scheme( $new_value, $forced_scheme );
262
	}
263
264
	public static function get_raw_url( $option_name ) {
265
		$value    = null;
0 ignored issues
show
Unused Code introduced by
$value 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...
266
		$constant = ( 'home' == $option_name )
267
			? 'WP_HOME'
268
			: 'WP_SITEURL';
269
270
		// Since we disregard the constant for multisites in ms-default-filters.php,
271
		// let's also use the db value if this is a multisite.
272
		if ( ! is_multisite() && Jetpack_Constants::is_defined( $constant ) ) {
273
			$value = Jetpack_Constants::get_constant( $constant );
274
		} else {
275
			// Let's get the option from the database so that we can bypass filters. This will help
276
			// ensure that we get more uniform values.
277
			$value = Jetpack_Options::get_raw_option( $option_name );
278
		}
279
280
		return $value;
281
	}
282
283
	public static function normalize_www_in_url( $option, $url_function ) {
284
		$url        = wp_parse_url( call_user_func( $url_function ) );
285
		$option_url = wp_parse_url( get_option( $option ) );
286
287
		if ( ! $option_url || ! $url ) {
288
			return $url;
289
		}
290
291 View Code Duplication
		if ( $url['host'] === "www.{$option_url[ 'host' ]}" ) {
292
			// remove www if not present in option URL
293
			$url['host'] = $option_url['host'];
294
		}
295 View Code Duplication
		if ( $option_url['host'] === "www.{$url[ 'host' ]}" ) {
296
			// add www if present in option URL
297
			$url['host'] = $option_url['host'];
298
		}
299
300
		$normalized_url = "{$url['scheme']}://{$url['host']}";
301
		if ( isset( $url['path'] ) ) {
302
			$normalized_url .= "{$url['path']}";
303
		}
304
305
		if ( isset( $url['query'] ) ) {
306
			$normalized_url .= "?{$url['query']}";
307
		}
308
309
		return $normalized_url;
310
	}
311
312
	public static function get_plugins() {
313
		if ( ! function_exists( 'get_plugins' ) ) {
314
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
315
		}
316
317
		/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
318
		return apply_filters( 'all_plugins', get_plugins() );
319
	}
320
321
	/**
322
	 * Get custom action link tags that the plugin is using
323
	 * Ref: https://codex.wordpress.org/Plugin_API/Filter_Reference/plugin_action_links_(plugin_file_name)
324
	 *
325
	 * @return array of plugin action links (key: link name value: url)
326
	 */
327
	public static function get_plugins_action_links( $plugin_file_singular = null ) {
328
		// Some sites may have DOM disabled in PHP fail early
329
		if ( ! class_exists( 'DOMDocument' ) ) {
330
			return array();
331
		}
332
		$plugins_action_links = get_option( 'jetpack_plugin_api_action_links', array() );
333
		if ( ! empty( $plugins_action_links ) ) {
334
			if ( is_null( $plugin_file_singular ) ) {
335
				return $plugins_action_links;
336
			}
337
			return ( isset( $plugins_action_links[ $plugin_file_singular ] ) ? $plugins_action_links[ $plugin_file_singular ] : null );
338
		}
339
		return array();
340
	}
341
342
	public static function wp_version() {
343
		global $wp_version;
344
		return $wp_version;
345
	}
346
347
	public static function site_icon_url( $size = 512 ) {
348
		$site_icon = get_site_icon_url( $size );
349
		return $site_icon ? $site_icon : get_option( 'jetpack_site_icon_url' );
350
	}
351
352
	public static function roles() {
353
		$wp_roles = wp_roles();
354
		return $wp_roles->roles;
355
	}
356
357
	/**
358
	 * Determine time zone from WordPress' options "timezone_string"
359
	 * and "gmt_offset".
360
	 *
361
	 * 1. Check if `timezone_string` is set and return it.
362
	 * 2. Check if `gmt_offset` is set, formats UTC-offset from it and return it.
363
	 * 3. Default to "UTC+0" if nothing is set.
364
	 *
365
	 * @return string
366
	 */
367
	public static function get_timezone() {
368
		$timezone_string = get_option( 'timezone_string' );
369
370
		if ( ! empty( $timezone_string ) ) {
371
			return str_replace( '_', ' ', $timezone_string );
372
		}
373
374
		$gmt_offset = get_option( 'gmt_offset', 0 );
375
376
		$formatted_gmt_offset = sprintf( '%+g', floatval( $gmt_offset ) );
377
378
		$formatted_gmt_offset = str_replace(
379
			array( '.25', '.5', '.75' ),
380
			array( ':15', ':30', ':45' ),
381
			(string) $formatted_gmt_offset
382
		);
383
384
		/* translators: %s is UTC offset, e.g. "+1" */
385
		return sprintf( __( 'UTC%s', 'jetpack' ), $formatted_gmt_offset );
386
	}
387
	// New in WP 5.1
388
	public static function get_paused_themes() {
389
		if ( function_exists( 'wp_paused_themes' ) ) {
390
			$paused_themes = wp_paused_themes();
391
			return $paused_themes->get_all();
392
		}
393
		return false;
394
	}
395
	// New in WP 5.1
396
	public static function get_paused_plugins() {
397
		if ( function_exists( 'wp_paused_plugins' ) ) {
398
			$paused_plugins = wp_paused_plugins();
399
			return $paused_plugins->get_all();
400
		}
401
		return false;
402
	}
403
404
	/**
405
	 * Determine the class that extends `WP_Importer` which is responsible for
406
	 * the current action. Designed to be used within an action handler.
407
	 *
408
	 * @return string  The name of the calling class, or 'unknown'.
409
	 */
410
	public static function get_calling_importer_class() {
411
		// If WP_Importer doesn't exist, neither will any importer that extends it
412
		if ( ! class_exists( 'WP_Importer' ) ){
413
			return 'unknown';
414
		}
415
416
		$action = current_filter();
417
		$backtrace = wp_debug_backtrace_summary( null, 0, false );
418
419
		$do_action_pos = -1;
420
		for ( $i = 0; $i < count( $backtrace ); $i++ ) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
421
			// Find the location in the stack of the calling action
422
			if ( preg_match( "/^do_action\\(\'([^\']+)/", $backtrace[ $i ], $matches ) ) {
423
				if ( $matches[1] === $action ) {
424
					$do_action_pos = $i;
425
					break;
426
				}
427
			}
428
		}
429
430
		// if the action wasn't called, the calling class is unknown
431
		if ( -1 === $do_action_pos ) {
432
			return 'unknown';
433
		}
434
435
		// continue iterating the stack looking for a caller that extends WP_Importer
436
		for ( $i = $do_action_pos + 1; $i < count( $backtrace ); $i++ ) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
437
			// grab only class_name from the trace
438
			list( $class_name ) = explode( '->', $backtrace[ $i ] );
439
440
			// check if the class extends WP_Importer
441
			if ( class_exists( $class_name ) ) {
442
				$parents = class_parents( $class_name );
443
				if ( $parents && in_array( 'WP_Importer', $parents ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parents 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...
444
					return $class_name;
445
				}
446
			}
447
		}
448
449
		// If we've exhausted the stack without a match, the calling class is unknown
450
		return 'unknown';
451
	}
452
}
453