Completed
Push — add/group-id-search-field ( 58d422 )
by
unknown
10:36
created

Functions::get_hosting_provider_by_known_class()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
1
<?php
2
/**
3
 * Utility functions to generate data synced to wpcom
4
 *
5
 * @package automattic/jetpack-sync
6
 */
7
8
namespace Automattic\Jetpack\Sync;
9
10
use Automattic\Jetpack\Constants;
11
12
/**
13
 * Utility functions to generate data synced to wpcom
14
 */
15
class Functions {
16
	const HTTPS_CHECK_OPTION_PREFIX = 'jetpack_sync_https_history_';
17
	const HTTPS_CHECK_HISTORY       = 5;
18
19
	/**
20
	 * Return array of Jetpack modules.
21
	 *
22
	 * @return array
23
	 */
24
	public static function get_modules() {
25
		require_once JETPACK__PLUGIN_DIR . 'class.jetpack-admin.php';
26
27
		return \Jetpack_Admin::init()->get_modules();
28
	}
29
30
	/**
31
	 * Return array of taxonomies registered on the site.
32
	 *
33
	 * @return array
34
	 */
35
	public static function get_taxonomies() {
36
		global $wp_taxonomies;
37
		$wp_taxonomies_without_callbacks = array();
38
		foreach ( $wp_taxonomies as $taxonomy_name => $taxonomy ) {
39
			$sanitized_taxonomy = self::sanitize_taxonomy( $taxonomy );
40
			if ( ! empty( $sanitized_taxonomy ) ) {
41
				$wp_taxonomies_without_callbacks[ $taxonomy_name ] = $sanitized_taxonomy;
42
			}
43
		}
44
		return $wp_taxonomies_without_callbacks;
45
	}
46
47
	/**
48
	 * Return array of registered shortcodes.
49
	 *
50
	 * @return array
51
	 */
52
	public static function get_shortcodes() {
53
		global $shortcode_tags;
54
		return array_keys( $shortcode_tags );
55
	}
56
57
	/**
58
	 * Removes any callback data since we will not be able to process it on our side anyways.
59
	 *
60
	 * @param \WP_Taxonomy $taxonomy \WP_Taxonomy item.
61
	 *
62
	 * @return mixed|null
63
	 */
64
	public static function sanitize_taxonomy( $taxonomy ) {
65
66
		// Lets clone the taxonomy object instead of modifing the global one.
67
		$cloned_taxonomy = json_decode( wp_json_encode( $taxonomy ) );
68
69
		// recursive taxonomies are no fun.
70
		if ( is_null( $cloned_taxonomy ) ) {
71
			return null;
72
		}
73
		// Remove any meta_box_cb if they are not the default wp ones.
74
		if ( isset( $cloned_taxonomy->meta_box_cb ) &&
75
			! in_array( $cloned_taxonomy->meta_box_cb, array( 'post_tags_meta_box', 'post_categories_meta_box' ), true ) ) {
76
			$cloned_taxonomy->meta_box_cb = null;
77
		}
78
		// Remove update call back.
79
		if ( isset( $cloned_taxonomy->update_count_callback ) &&
80
			! is_null( $cloned_taxonomy->update_count_callback ) ) {
81
			$cloned_taxonomy->update_count_callback = null;
82
		}
83
		// Remove rest_controller_class if it something other then the default.
84
		if ( isset( $cloned_taxonomy->rest_controller_class ) &&
85
			'WP_REST_Terms_Controller' !== $cloned_taxonomy->rest_controller_class ) {
86
			$cloned_taxonomy->rest_controller_class = null;
87
		}
88
		return $cloned_taxonomy;
89
	}
90
91
	/**
92
	 * Return array of registered post types.
93
	 *
94
	 * @return array
95
	 */
96
	public static function get_post_types() {
97
		global $wp_post_types;
98
99
		$post_types_without_callbacks = array();
100
		foreach ( $wp_post_types as $post_type_name => $post_type ) {
101
			$sanitized_post_type = self::sanitize_post_type( $post_type );
102
			if ( ! empty( $sanitized_post_type ) ) {
103
				$post_types_without_callbacks[ $post_type_name ] = $sanitized_post_type;
104
			}
105
		}
106
		return $post_types_without_callbacks;
107
	}
108
109
	/**
110
	 * Sanitizes by cloning post type object.
111
	 *
112
	 * @param object $post_type \WP_Post_Type.
113
	 *
114
	 * @return object
115
	 */
116
	public static function sanitize_post_type( $post_type ) {
117
		// Lets clone the post type object instead of modifing the global one.
118
		$sanitized_post_type = array();
119
		foreach ( Defaults::$default_post_type_attributes as $attribute_key => $default_value ) {
120
			if ( isset( $post_type->{ $attribute_key } ) ) {
121
				$sanitized_post_type[ $attribute_key ] = $post_type->{ $attribute_key };
122
			}
123
		}
124
		return (object) $sanitized_post_type;
125
	}
126
127
	/**
128
	 * Return information about a synced post type.
129
	 *
130
	 * @param array  $sanitized_post_type Array of args used in constructing \WP_Post_Type.
131
	 * @param string $post_type Post type name.
132
	 *
133
	 * @return object \WP_Post_Type
134
	 */
135
	public static function expand_synced_post_type( $sanitized_post_type, $post_type ) {
136
		$post_type        = sanitize_key( $post_type );
137
		$post_type_object = new \WP_Post_Type( $post_type, $sanitized_post_type );
138
		$post_type_object->add_supports();
139
		$post_type_object->add_rewrite_rules();
140
		$post_type_object->add_hooks();
141
		$post_type_object->register_taxonomies();
142
		return (object) $post_type_object;
143
	}
144
145
	/**
146
	 * Returns site's post_type_features.
147
	 *
148
	 * @return array
149
	 */
150
	public static function get_post_type_features() {
151
		global $_wp_post_type_features;
152
153
		return $_wp_post_type_features;
154
	}
155
156
	/**
157
	 * Return hosting provider.
158
	 *
159
	 * Uses a set of known constants, classes, or functions to help determine the hosting platform.
160
	 *
161
	 * @return string Hosting provider.
162
	 */
163
	public static function get_hosting_provider() {
164
		$hosting_provider_detection_methods = array(
165
			'get_hosting_provider_by_known_constant',
166
			'get_hosting_provider_by_known_class',
167
			'get_hosting_provider_by_known_function',
168
		);
169
170
		$functions = new Functions();
171
		foreach ( $hosting_provider_detection_methods as $method ) {
172
			$hosting_provider = call_user_func( array( $functions, $method ) );
173
			if ( false !== $hosting_provider ) {
174
				return $hosting_provider;
175
			}
176
		}
177
178
		return 'unknown';
179
	}
180
181
	/**
182
	 * Return a hosting provider using a set of known constants.
183
	 *
184
	 * @return mixed A host identifier string or false.
185
	 */
186
	public function get_hosting_provider_by_known_constant() {
187
		$hosting_provider_constants = array(
188
			'GD_SYSTEM_PLUGIN_DIR' => 'gd-managed-wp',
189
			'MM_BASE_DIR'          => 'bh',
190
			'PAGELYBIN'            => 'pagely',
191
			'KINSTAMU_VERSION'     => 'kinsta',
192
			'FLYWHEEL_CONFIG_DIR'  => 'flywheel',
193
			'IS_PRESSABLE'         => 'pressable',
194
			'VIP_GO_ENV'           => 'vip-go',
195
		);
196
197
		foreach ( $hosting_provider_constants as $constant => $constant_value ) {
198
			if ( Constants::is_defined( $constant ) ) {
199
				if ( 'VIP_GO_ENV' === $constant && false === Constants::get_constant( 'VIP_GO_ENV' ) ) {
200
					continue;
201
				}
202
				return $constant_value;
203
			}
204
		}
205
206
		return false;
207
	}
208
209
	/**
210
	 * Return a hosting provider using a set of known classes.
211
	 *
212
	 * @return mixed A host identifier string or false.
213
	 */
214
	public function get_hosting_provider_by_known_class() {
215
		$hosting_provider = false;
216
217
		switch ( true ) {
218
			case ( class_exists( '\\WPaaS\\Plugin' ) ):
219
				$hosting_provider = 'gd-managed-wp';
220
				break;
221
		}
222
223
		return $hosting_provider;
224
	}
225
226
	/**
227
	 * Return a hosting provider using a set of known functions.
228
	 *
229
	 * @return mixed A host identifier string or false.
230
	 */
231
	public function get_hosting_provider_by_known_function() {
232
		$hosting_provider = false;
233
234
		switch ( true ) {
235
			case ( function_exists( 'is_wpe' ) || function_exists( 'is_wpe_snapshot' ) ):
236
				$hosting_provider = 'wpe';
237
				break;
238
		}
239
240
		return $hosting_provider;
241
	}
242
243
	/**
244
	 * Return array of allowed REST API post types.
245
	 *
246
	 * @return array Array of allowed post types.
247
	 */
248
	public static function rest_api_allowed_post_types() {
249
		/** This filter is already documented in class.json-api-endpoints.php */
250
		return apply_filters( 'rest_api_allowed_post_types', array( 'post', 'page', 'revision' ) );
251
	}
252
253
	/**
254
	 * Return array of allowed REST API public metadata.
255
	 *
256
	 * @return array Array of allowed metadata.
257
	 */
258
	public static function rest_api_allowed_public_metadata() {
259
		/**
260
		 * Filters the meta keys accessible by the REST API.
261
		 *
262
		 * @see https://developer.wordpress.com/2013/04/26/custom-post-type-and-metadata-support-in-the-rest-api/
263
		 *
264
		 * @module json-api
265
		 *
266
		 * @since 2.2.3
267
		 *
268
		 * @param array $whitelisted_meta Array of metadata that is accessible by the REST API.
269
		 */
270
		return apply_filters( 'rest_api_allowed_public_metadata', array() );
271
	}
272
273
	/**
274
	 * Finds out if a site is using a version control system.
275
	 *
276
	 * @return bool
277
	 **/
278
	public static function is_version_controlled() {
279
280
		if ( ! class_exists( 'WP_Automatic_Updater' ) ) {
281
			require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
282
		}
283
		$updater = new \WP_Automatic_Updater();
284
285
		return (bool) (string) $updater->is_vcs_checkout( ABSPATH );
286
	}
287
288
	/**
289
	 * Returns true if the site has file write access false otherwise.
290
	 *
291
	 * @return bool
292
	 **/
293
	public static function file_system_write_access() {
294
		if ( ! function_exists( 'get_filesystem_method' ) ) {
295
			require_once ABSPATH . 'wp-admin/includes/file.php';
296
		}
297
298
		require_once ABSPATH . 'wp-admin/includes/template.php';
299
300
		$filesystem_method = get_filesystem_method();
301
		if ( 'direct' === $filesystem_method ) {
302
			return true;
303
		}
304
305
		ob_start();
306
307
		if ( ! function_exists( 'request_filesystem_credentials' ) ) {
308
			require_once ABSPATH . 'wp-admin/includes/file.php';
309
		}
310
311
		$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
312
		ob_end_clean();
313
		if ( $filesystem_credentials_are_stored ) {
314
			return true;
315
		}
316
317
		return false;
318
	}
319
320
	/**
321
	 * Helper function that is used when getting home or siteurl values. Decides
322
	 * whether to get the raw or filtered value.
323
	 *
324
	 * @param string $url_type URL to get, home or siteurl.
325
	 * @return string
326
	 */
327
	public static function get_raw_or_filtered_url( $url_type ) {
328
		$url_function = ( 'home' === $url_type )
329
			? 'home_url'
330
			: 'site_url';
331
332
		if (
333
			! Constants::is_defined( 'JETPACK_SYNC_USE_RAW_URL' ) ||
334
			Constants::get_constant( 'JETPACK_SYNC_USE_RAW_URL' )
335
		) {
336
			$scheme = is_ssl() ? 'https' : 'http';
337
			$url    = self::get_raw_url( $url_type );
338
			$url    = set_url_scheme( $url, $scheme );
339
		} else {
340
			$url = self::normalize_www_in_url( $url_type, $url_function );
341
		}
342
343
		return self::get_protocol_normalized_url( $url_function, $url );
344
	}
345
346
	/**
347
	 * Return the escaped home_url.
348
	 *
349
	 * @return string
350
	 */
351
	public static function home_url() {
352
		$url = self::get_raw_or_filtered_url( 'home' );
353
354
		/**
355
		 * Allows overriding of the home_url value that is synced back to WordPress.com.
356
		 *
357
		 * @since 5.2.0
358
		 *
359
		 * @param string $home_url
360
		 */
361
		return esc_url_raw( apply_filters( 'jetpack_sync_home_url', $url ) );
362
	}
363
364
	/**
365
	 * Return the escaped siteurl.
366
	 *
367
	 * @return string
368
	 */
369
	public static function site_url() {
370
		$url = self::get_raw_or_filtered_url( 'siteurl' );
371
372
		/**
373
		 * Allows overriding of the site_url value that is synced back to WordPress.com.
374
		 *
375
		 * @since 5.2.0
376
		 *
377
		 * @param string $site_url
378
		 */
379
		return esc_url_raw( apply_filters( 'jetpack_sync_site_url', $url ) );
380
	}
381
382
	/**
383
	 * Return main site URL with a normalized protocol.
384
	 *
385
	 * @return string
386
	 */
387
	public static function main_network_site_url() {
388
		return self::get_protocol_normalized_url( 'main_network_site_url', network_site_url() );
389
	}
390
391
	/**
392
	 * Return main site WordPress.com site ID.
393
	 *
394
	 * @return string
395
	 */
396
	public static function main_network_site_wpcom_id() {
397
		/**
398
		 * Return the current site WPCOM ID for single site installs
399
		 */
400
		if ( ! is_multisite() ) {
401
			return \Jetpack_Options::get_option( 'id' );
402
		}
403
404
		/**
405
		 * Return the main network site WPCOM ID for multi-site installs
406
		 */
407
		$current_network = get_network();
408
		switch_to_blog( $current_network->blog_id );
409
		$wpcom_blog_id = \Jetpack_Options::get_option( 'id' );
410
		restore_current_blog();
411
		return $wpcom_blog_id;
412
	}
413
414
	/**
415
	 * Return P2 Workspace Hub WordPress.com blog id.
416
	 *
417
	 * @return int
418
	 */
419
	public static function p2_workspace_hub_blog_id() {
420
		$p2_options = \Jetpack_Options::get_option( 'p2_options' );
421
422
		if ( ! is_array( $p2_options ) ) {
423
			// No support for sites that are not part of a Workspace.
424
			return 0;
425
		}
426
427
		$is_hub_option_key = 'is_p2_hub_site'; // keep in sync with WPForTeams\Constants\OPTION_KEY_IS_HUB.
428
		if ( array_key_exists( $is_hub_option_key, $p2_options ) ) {
429
			// Current site is a hub, return its WPCOM ID.
430
			return \Jetpack_Options::get_option( 'id' );
431
		}
432
433
		$has_hub_option_key = 'p2_hub_blog_id'; // in sync with WPForTeams\Constants\OPTION_KEY_PARENT_HUB_ID.
434
		if ( array_key_exists( $has_hub_option_key, $p2_options ) ) {
435
			// Current site is part of a Workspace, return its hub WPCOM ID.
436
			return $p2_options[ $has_hub_option_key ];
437
		}
438
439
		// Site doesnt have proper p2_options, it is neither a Workspace site or hub.
440
		return 0;
441
	}
442
443
	/**
444
	 * Return URL with a normalized protocol.
445
	 *
446
	 * @param callable $callable Function to retrieve URL option.
447
	 * @param string   $new_value URL Protocol to set URLs to.
448
	 * @return string Normalized URL.
449
	 */
450
	public static function get_protocol_normalized_url( $callable, $new_value ) {
451
		$option_key = self::HTTPS_CHECK_OPTION_PREFIX . $callable;
452
453
		$parsed_url = wp_parse_url( $new_value );
454
		if ( ! $parsed_url ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parsed_url of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
455
			return $new_value;
456
		}
457
		if ( array_key_exists( 'scheme', $parsed_url ) ) {
458
			$scheme = $parsed_url['scheme'];
459
		} else {
460
			$scheme = '';
461
		}
462
		$scheme_history   = get_option( $option_key, array() );
463
		$scheme_history[] = $scheme;
464
465
		// Limit length to self::HTTPS_CHECK_HISTORY.
466
		$scheme_history = array_slice( $scheme_history, ( self::HTTPS_CHECK_HISTORY * -1 ) );
467
468
		update_option( $option_key, $scheme_history );
469
470
		$forced_scheme = in_array( 'https', $scheme_history, true ) ? 'https' : 'http';
471
472
		return set_url_scheme( $new_value, $forced_scheme );
473
	}
474
475
	/**
476
	 * Return URL from option or PHP constant.
477
	 *
478
	 * @param string $option_name (e.g. 'home').
479
	 *
480
	 * @return mixed|null URL.
481
	 */
482
	public static function get_raw_url( $option_name ) {
483
		$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...
484
		$constant = ( 'home' === $option_name )
485
			? 'WP_HOME'
486
			: 'WP_SITEURL';
487
488
		// Since we disregard the constant for multisites in ms-default-filters.php,
489
		// let's also use the db value if this is a multisite.
490
		if ( ! is_multisite() && Constants::is_defined( $constant ) ) {
491
			$value = Constants::get_constant( $constant );
492
		} else {
493
			// Let's get the option from the database so that we can bypass filters. This will help
494
			// ensure that we get more uniform values.
495
			$value = \Jetpack_Options::get_raw_option( $option_name );
496
		}
497
498
		return $value;
499
	}
500
501
	/**
502
	 * Normalize domains by removing www unless declared in the site's option.
503
	 *
504
	 * @param string   $option Option value from the site.
505
	 * @param callable $url_function Function retrieving the URL to normalize.
506
	 * @return mixed|string URL.
507
	 */
508
	public static function normalize_www_in_url( $option, $url_function ) {
509
		$url        = wp_parse_url( call_user_func( $url_function ) );
510
		$option_url = wp_parse_url( get_option( $option ) );
511
512
		if ( ! $option_url || ! $url ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $option_url of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
513
			return $url;
514
		}
515
516 View Code Duplication
		if ( "www.{$option_url[ 'host' ]}" === $url['host'] ) {
517
			// remove www if not present in option URL.
518
			$url['host'] = $option_url['host'];
519
		}
520 View Code Duplication
		if ( "www.{$url[ 'host' ]}" === $option_url['host'] ) {
521
			// add www if present in option URL.
522
			$url['host'] = $option_url['host'];
523
		}
524
525
		$normalized_url = "{$url['scheme']}://{$url['host']}";
526
		if ( isset( $url['path'] ) ) {
527
			$normalized_url .= "{$url['path']}";
528
		}
529
530
		if ( isset( $url['query'] ) ) {
531
			$normalized_url .= "?{$url['query']}";
532
		}
533
534
		return $normalized_url;
535
	}
536
537
	/**
538
	 * Return filtered value of get_plugins.
539
	 *
540
	 * @return mixed|void
541
	 */
542
	public static function get_plugins() {
543
		if ( ! function_exists( 'get_plugins' ) ) {
544
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
545
		}
546
547
		/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
548
		return apply_filters( 'all_plugins', get_plugins() );
549
	}
550
551
	/**
552
	 * Get custom action link tags that the plugin is using
553
	 * Ref: https://codex.wordpress.org/Plugin_API/Filter_Reference/plugin_action_links_(plugin_file_name)
554
	 *
555
	 * @param string $plugin_file_singular Particular plugin.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $plugin_file_singular 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...
556
	 * @return array of plugin action links (key: link name value: url)
557
	 */
558
	public static function get_plugins_action_links( $plugin_file_singular = null ) {
559
		// Some sites may have DOM disabled in PHP fail early.
560
		if ( ! class_exists( 'DOMDocument' ) ) {
561
			return array();
562
		}
563
		$plugins_action_links = get_option( 'jetpack_plugin_api_action_links', array() );
564
		if ( ! empty( $plugins_action_links ) ) {
565
			if ( is_null( $plugin_file_singular ) ) {
566
				return $plugins_action_links;
567
			}
568
			return ( isset( $plugins_action_links[ $plugin_file_singular ] ) ? $plugins_action_links[ $plugin_file_singular ] : null );
569
		}
570
		return array();
571
	}
572
573
	/**
574
	 * Return the WP version as defined in the $wp_version global.
575
	 *
576
	 * @return string
577
	 */
578
	public static function wp_version() {
579
		global $wp_version;
580
		return $wp_version;
581
	}
582
583
	/**
584
	 * Return site icon url used on the site.
585
	 *
586
	 * @param int $size Size of requested icon in pixels.
587
	 * @return mixed|string|void
588
	 */
589
	public static function site_icon_url( $size = 512 ) {
590
		$site_icon = get_site_icon_url( $size );
591
		return $site_icon ? $site_icon : get_option( 'jetpack_site_icon_url' );
592
	}
593
594
	/**
595
	 * Return roles registered on the site.
596
	 *
597
	 * @return array
598
	 */
599
	public static function roles() {
600
		$wp_roles = wp_roles();
601
		return $wp_roles->roles;
602
	}
603
604
	/**
605
	 * Determine time zone from WordPress' options "timezone_string"
606
	 * and "gmt_offset".
607
	 *
608
	 * 1. Check if `timezone_string` is set and return it.
609
	 * 2. Check if `gmt_offset` is set, formats UTC-offset from it and return it.
610
	 * 3. Default to "UTC+0" if nothing is set.
611
	 *
612
	 * Note: This function is specifically not using wp_timezone() to keep consistency with
613
	 * the existing formatting of the timezone string.
614
	 *
615
	 * @return string
616
	 */
617
	public static function get_timezone() {
618
		$timezone_string = get_option( 'timezone_string' );
619
620
		if ( ! empty( $timezone_string ) ) {
621
			return str_replace( '_', ' ', $timezone_string );
622
		}
623
624
		$gmt_offset = get_option( 'gmt_offset', 0 );
625
626
		$formatted_gmt_offset = sprintf( '%+g', (float) $gmt_offset );
627
628
		$formatted_gmt_offset = str_replace(
629
			array( '.25', '.5', '.75' ),
630
			array( ':15', ':30', ':45' ),
631
			(string) $formatted_gmt_offset
632
		);
633
634
		/* translators: %s is UTC offset, e.g. "+1" */
635
		return sprintf( __( 'UTC%s', 'jetpack' ), $formatted_gmt_offset );
636
	}
637
638
	/**
639
	 * Return list of paused themes.
640
	 *
641
	 * @return array|bool Array of paused themes or false if unsupported.
642
	 */
643
	public static function get_paused_themes() {
644
		$paused_themes = wp_paused_themes();
645
		return $paused_themes->get_all();
646
	}
647
648
	/**
649
	 * Return list of paused plugins.
650
	 *
651
	 * @return array|bool Array of paused plugins or false if unsupported.
652
	 */
653
	public static function get_paused_plugins() {
654
		$paused_plugins = wp_paused_plugins();
655
		return $paused_plugins->get_all();
656
	}
657
658
	/**
659
	 * Return the theme's supported features.
660
	 * Used for syncing the supported feature that we care about.
661
	 *
662
	 * @return array List of features that the theme supports.
663
	 */
664
	public static function get_theme_support() {
665
		global $_wp_theme_features;
666
667
		$theme_support = array();
668
		foreach ( Defaults::$default_theme_support_whitelist as $theme_feature ) {
669
			$has_support = current_theme_supports( $theme_feature );
670
			if ( $has_support ) {
671
				$theme_support[ $theme_feature ] = $_wp_theme_features[ $theme_feature ];
672
			}
673
		}
674
675
		return $theme_support;
676
	}
677
678
	/**
679
	 * Wraps data in a way so that we can distinguish between objects and array and also prevent object recursion.
680
	 *
681
	 * @since 9.5.0
682
	 *
683
	 * @param array|obj $any        Source data to be cleaned up.
684
	 * @param array     $seen_nodes Built array of nodes.
685
	 *
686
	 * @return array
687
	 */
688
	public static function json_wrap( &$any, $seen_nodes = array() ) {
689
		if ( is_object( $any ) ) {
690
			$input        = get_object_vars( $any );
691
			$input['__o'] = 1;
692
		} else {
693
			$input = &$any;
694
		}
695
696
		if ( is_array( $input ) ) {
697
			$seen_nodes[] = &$any;
698
699
			$return = array();
700
701
			foreach ( $input as $k => &$v ) {
702
				if ( ( is_array( $v ) || is_object( $v ) ) ) {
703
					if ( in_array( $v, $seen_nodes, true ) ) {
704
						continue;
705
					}
706
					$return[ $k ] = self::json_wrap( $v, $seen_nodes );
0 ignored issues
show
Bug introduced by
It seems like $v can also be of type object; however, Automattic\Jetpack\Sync\Functions::json_wrap() does only seem to accept array|object<Automattic\Jetpack\Sync\obj>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
707
				} else {
708
					$return[ $k ] = $v;
709
				}
710
			}
711
712
			return $return;
713
		}
714
715
		return $any;
716
717
	}
718
}
719