Completed
Push — add/cli-clean ( c51287...a1b904 )
by
unknown
30:43 queued 20:07
created

Functions::normalize_www_in_url()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 28

Duplication

Lines 8
Ratio 28.57 %

Importance

Changes 0
Metric Value
cc 7
nc 17
nop 2
dl 8
loc 28
rs 8.5386
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
		if ( defined( 'JETPACK__PLUGIN_DIR' ) ) {
26
			require_once JETPACK__PLUGIN_DIR . 'class.jetpack-admin.php';
27
28
			return \Jetpack_Admin::init()->get_modules();
29
		}
30
31
		return array();
32
	}
33
34
	/**
35
	 * Return array of taxonomies registered on the site.
36
	 *
37
	 * @return array
38
	 */
39
	public static function get_taxonomies() {
40
		global $wp_taxonomies;
41
		$wp_taxonomies_without_callbacks = array();
42
		foreach ( $wp_taxonomies as $taxonomy_name => $taxonomy ) {
43
			$sanitized_taxonomy = self::sanitize_taxonomy( $taxonomy );
44
			if ( ! empty( $sanitized_taxonomy ) ) {
45
				$wp_taxonomies_without_callbacks[ $taxonomy_name ] = $sanitized_taxonomy;
46
			}
47
		}
48
		return $wp_taxonomies_without_callbacks;
49
	}
50
51
	/**
52
	 * Return array of registered shortcodes.
53
	 *
54
	 * @return array
55
	 */
56
	public static function get_shortcodes() {
57
		global $shortcode_tags;
58
		return array_keys( $shortcode_tags );
59
	}
60
61
	/**
62
	 * Removes any callback data since we will not be able to process it on our side anyways.
63
	 *
64
	 * @param \WP_Taxonomy $taxonomy \WP_Taxonomy item.
65
	 *
66
	 * @return mixed|null
67
	 */
68
	public static function sanitize_taxonomy( $taxonomy ) {
69
70
		// Lets clone the taxonomy object instead of modifing the global one.
71
		$cloned_taxonomy = json_decode( wp_json_encode( $taxonomy ) );
72
73
		// recursive taxonomies are no fun.
74
		if ( is_null( $cloned_taxonomy ) ) {
75
			return null;
76
		}
77
		// Remove any meta_box_cb if they are not the default wp ones.
78
		if ( isset( $cloned_taxonomy->meta_box_cb ) &&
79
			! in_array( $cloned_taxonomy->meta_box_cb, array( 'post_tags_meta_box', 'post_categories_meta_box' ), true ) ) {
80
			$cloned_taxonomy->meta_box_cb = null;
81
		}
82
		// Remove update call back.
83
		if ( isset( $cloned_taxonomy->update_count_callback ) &&
84
			! is_null( $cloned_taxonomy->update_count_callback ) ) {
85
			$cloned_taxonomy->update_count_callback = null;
86
		}
87
		// Remove rest_controller_class if it something other then the default.
88
		if ( isset( $cloned_taxonomy->rest_controller_class ) &&
89
			'WP_REST_Terms_Controller' !== $cloned_taxonomy->rest_controller_class ) {
90
			$cloned_taxonomy->rest_controller_class = null;
91
		}
92
		return $cloned_taxonomy;
93
	}
94
95
	/**
96
	 * Return array of registered post types.
97
	 *
98
	 * @return array
99
	 */
100
	public static function get_post_types() {
101
		global $wp_post_types;
102
103
		$post_types_without_callbacks = array();
104
		foreach ( $wp_post_types as $post_type_name => $post_type ) {
105
			$sanitized_post_type = self::sanitize_post_type( $post_type );
106
			if ( ! empty( $sanitized_post_type ) ) {
107
				$post_types_without_callbacks[ $post_type_name ] = $sanitized_post_type;
108
			}
109
		}
110
		return $post_types_without_callbacks;
111
	}
112
113
	/**
114
	 * Sanitizes by cloning post type object.
115
	 *
116
	 * @param object $post_type \WP_Post_Type.
117
	 *
118
	 * @return object
119
	 */
120
	public static function sanitize_post_type( $post_type ) {
121
		// Lets clone the post type object instead of modifing the global one.
122
		$sanitized_post_type = array();
123
		foreach ( Defaults::$default_post_type_attributes as $attribute_key => $default_value ) {
124
			if ( isset( $post_type->{ $attribute_key } ) ) {
125
				$sanitized_post_type[ $attribute_key ] = $post_type->{ $attribute_key };
126
			}
127
		}
128
		return (object) $sanitized_post_type;
129
	}
130
131
	/**
132
	 * Return information about a synced post type.
133
	 *
134
	 * @param array  $sanitized_post_type Array of args used in constructing \WP_Post_Type.
135
	 * @param string $post_type Post type name.
136
	 *
137
	 * @return object \WP_Post_Type
138
	 */
139
	public static function expand_synced_post_type( $sanitized_post_type, $post_type ) {
140
		$post_type        = sanitize_key( $post_type );
141
		$post_type_object = new \WP_Post_Type( $post_type, $sanitized_post_type );
142
		$post_type_object->add_supports();
143
		$post_type_object->add_rewrite_rules();
144
		$post_type_object->add_hooks();
145
		$post_type_object->register_taxonomies();
146
		return (object) $post_type_object;
147
	}
148
149
	/**
150
	 * Returns site's post_type_features.
151
	 *
152
	 * @return array
153
	 */
154
	public static function get_post_type_features() {
155
		global $_wp_post_type_features;
156
157
		return $_wp_post_type_features;
158
	}
159
160
	/**
161
	 * Return hosting provider.
162
	 *
163
	 * Uses a set of known constants, classes, or functions to help determine the hosting platform.
164
	 *
165
	 * @return string Hosting provider.
166
	 */
167
	public static function get_hosting_provider() {
168
		$hosting_provider_detection_methods = array(
169
			'get_hosting_provider_by_known_constant',
170
			'get_hosting_provider_by_known_class',
171
			'get_hosting_provider_by_known_function',
172
		);
173
174
		$functions = new Functions();
175
		foreach ( $hosting_provider_detection_methods as $method ) {
176
			$hosting_provider = call_user_func( array( $functions, $method ) );
177
			if ( false !== $hosting_provider ) {
178
				return $hosting_provider;
179
			}
180
		}
181
182
		return 'unknown';
183
	}
184
185
	/**
186
	 * Return a hosting provider using a set of known constants.
187
	 *
188
	 * @return mixed A host identifier string or false.
189
	 */
190
	public function get_hosting_provider_by_known_constant() {
191
		$hosting_provider_constants = array(
192
			'GD_SYSTEM_PLUGIN_DIR' => 'gd-managed-wp',
193
			'MM_BASE_DIR'          => 'bh',
194
			'PAGELYBIN'            => 'pagely',
195
			'KINSTAMU_VERSION'     => 'kinsta',
196
			'FLYWHEEL_CONFIG_DIR'  => 'flywheel',
197
			'IS_PRESSABLE'         => 'pressable',
198
			'VIP_GO_ENV'           => 'vip-go',
199
		);
200
201
		foreach ( $hosting_provider_constants as $constant => $constant_value ) {
202
			if ( Constants::is_defined( $constant ) ) {
203
				if ( 'VIP_GO_ENV' === $constant && false === Constants::get_constant( 'VIP_GO_ENV' ) ) {
204
					continue;
205
				}
206
				return $constant_value;
207
			}
208
		}
209
210
		return false;
211
	}
212
213
	/**
214
	 * Return a hosting provider using a set of known classes.
215
	 *
216
	 * @return mixed A host identifier string or false.
217
	 */
218
	public function get_hosting_provider_by_known_class() {
219
		$hosting_provider = false;
220
221
		switch ( true ) {
222
			case ( class_exists( '\\WPaaS\\Plugin' ) ):
223
				$hosting_provider = 'gd-managed-wp';
224
				break;
225
		}
226
227
		return $hosting_provider;
228
	}
229
230
	/**
231
	 * Return a hosting provider using a set of known functions.
232
	 *
233
	 * @return mixed A host identifier string or false.
234
	 */
235
	public function get_hosting_provider_by_known_function() {
236
		$hosting_provider = false;
237
238
		switch ( true ) {
239
			case ( function_exists( 'is_wpe' ) || function_exists( 'is_wpe_snapshot' ) ):
240
				$hosting_provider = 'wpe';
241
				break;
242
		}
243
244
		return $hosting_provider;
245
	}
246
247
	/**
248
	 * Return array of allowed REST API post types.
249
	 *
250
	 * @return array Array of allowed post types.
251
	 */
252
	public static function rest_api_allowed_post_types() {
253
		/** This filter is already documented in class.json-api-endpoints.php */
254
		return apply_filters( 'rest_api_allowed_post_types', array( 'post', 'page', 'revision' ) );
255
	}
256
257
	/**
258
	 * Return array of allowed REST API public metadata.
259
	 *
260
	 * @return array Array of allowed metadata.
261
	 */
262
	public static function rest_api_allowed_public_metadata() {
263
		/**
264
		 * Filters the meta keys accessible by the REST API.
265
		 *
266
		 * @see https://developer.wordpress.com/2013/04/26/custom-post-type-and-metadata-support-in-the-rest-api/
267
		 *
268
		 * @module json-api
269
		 *
270
		 * @since 2.2.3
271
		 *
272
		 * @param array $whitelisted_meta Array of metadata that is accessible by the REST API.
273
		 */
274
		return apply_filters( 'rest_api_allowed_public_metadata', array() );
275
	}
276
277
	/**
278
	 * Finds out if a site is using a version control system.
279
	 *
280
	 * @return bool
281
	 **/
282
	public static function is_version_controlled() {
283
284
		if ( ! class_exists( 'WP_Automatic_Updater' ) ) {
285
			require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
286
		}
287
		$updater = new \WP_Automatic_Updater();
288
289
		return (bool) (string) $updater->is_vcs_checkout( ABSPATH );
290
	}
291
292
	/**
293
	 * Returns true if the site has file write access false otherwise.
294
	 *
295
	 * @return bool
296
	 **/
297
	public static function file_system_write_access() {
298
		if ( ! function_exists( 'get_filesystem_method' ) ) {
299
			require_once ABSPATH . 'wp-admin/includes/file.php';
300
		}
301
302
		require_once ABSPATH . 'wp-admin/includes/template.php';
303
304
		$filesystem_method = get_filesystem_method();
305
		if ( 'direct' === $filesystem_method ) {
306
			return true;
307
		}
308
309
		ob_start();
310
311
		if ( ! function_exists( 'request_filesystem_credentials' ) ) {
312
			require_once ABSPATH . 'wp-admin/includes/file.php';
313
		}
314
315
		$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
316
		ob_end_clean();
317
		if ( $filesystem_credentials_are_stored ) {
318
			return true;
319
		}
320
321
		return false;
322
	}
323
324
	/**
325
	 * Helper function that is used when getting home or siteurl values. Decides
326
	 * whether to get the raw or filtered value.
327
	 *
328
	 * @param string $url_type URL to get, home or siteurl.
329
	 * @return string
330
	 */
331
	public static function get_raw_or_filtered_url( $url_type ) {
332
		$url_function = ( 'home' === $url_type )
333
			? 'home_url'
334
			: 'site_url';
335
336
		if (
337
			! Constants::is_defined( 'JETPACK_SYNC_USE_RAW_URL' ) ||
338
			Constants::get_constant( 'JETPACK_SYNC_USE_RAW_URL' )
339
		) {
340
			$scheme = is_ssl() ? 'https' : 'http';
341
			$url    = self::get_raw_url( $url_type );
342
			$url    = set_url_scheme( $url, $scheme );
343
		} else {
344
			$url = self::normalize_www_in_url( $url_type, $url_function );
345
		}
346
347
		return self::get_protocol_normalized_url( $url_function, $url );
348
	}
349
350
	/**
351
	 * Return the escaped home_url.
352
	 *
353
	 * @return string
354
	 */
355
	public static function home_url() {
356
		$url = self::get_raw_or_filtered_url( 'home' );
357
358
		/**
359
		 * Allows overriding of the home_url value that is synced back to WordPress.com.
360
		 *
361
		 * @since 5.2.0
362
		 *
363
		 * @param string $home_url
364
		 */
365
		return esc_url_raw( apply_filters( 'jetpack_sync_home_url', $url ) );
366
	}
367
368
	/**
369
	 * Return the escaped siteurl.
370
	 *
371
	 * @return string
372
	 */
373
	public static function site_url() {
374
		$url = self::get_raw_or_filtered_url( 'siteurl' );
375
376
		/**
377
		 * Allows overriding of the site_url value that is synced back to WordPress.com.
378
		 *
379
		 * @since 5.2.0
380
		 *
381
		 * @param string $site_url
382
		 */
383
		return esc_url_raw( apply_filters( 'jetpack_sync_site_url', $url ) );
384
	}
385
386
	/**
387
	 * Return main site URL with a normalized protocol.
388
	 *
389
	 * @return string
390
	 */
391
	public static function main_network_site_url() {
392
		return self::get_protocol_normalized_url( 'main_network_site_url', network_site_url() );
393
	}
394
395
	/**
396
	 * Return main site WordPress.com site ID.
397
	 *
398
	 * @return string
399
	 */
400
	public static function main_network_site_wpcom_id() {
401
		/**
402
		 * Return the current site WPCOM ID for single site installs
403
		 */
404
		if ( ! is_multisite() ) {
405
			return \Jetpack_Options::get_option( 'id' );
406
		}
407
408
		/**
409
		 * Return the main network site WPCOM ID for multi-site installs
410
		 */
411
		$current_network = get_network();
412
		switch_to_blog( $current_network->blog_id );
413
		$wpcom_blog_id = \Jetpack_Options::get_option( 'id' );
414
		restore_current_blog();
415
		return $wpcom_blog_id;
416
	}
417
418
	/**
419
	 * Return URL with a normalized protocol.
420
	 *
421
	 * @param callable $callable Function to retrieve URL option.
422
	 * @param string   $new_value URL Protocol to set URLs to.
423
	 * @return string Normalized URL.
424
	 */
425
	public static function get_protocol_normalized_url( $callable, $new_value ) {
426
		$option_key = self::HTTPS_CHECK_OPTION_PREFIX . $callable;
427
428
		$parsed_url = wp_parse_url( $new_value );
429
		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...
430
			return $new_value;
431
		}
432
		if ( array_key_exists( 'scheme', $parsed_url ) ) {
433
			$scheme = $parsed_url['scheme'];
434
		} else {
435
			$scheme = '';
436
		}
437
		$scheme_history   = get_option( $option_key, array() );
438
		$scheme_history[] = $scheme;
439
440
		// Limit length to self::HTTPS_CHECK_HISTORY.
441
		$scheme_history = array_slice( $scheme_history, ( self::HTTPS_CHECK_HISTORY * -1 ) );
442
443
		update_option( $option_key, $scheme_history );
444
445
		$forced_scheme = in_array( 'https', $scheme_history, true ) ? 'https' : 'http';
446
447
		return set_url_scheme( $new_value, $forced_scheme );
448
	}
449
450
	/**
451
	 * Return URL from option or PHP constant.
452
	 *
453
	 * @param string $option_name (e.g. 'home').
454
	 *
455
	 * @return mixed|null URL.
456
	 */
457
	public static function get_raw_url( $option_name ) {
458
		$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...
459
		$constant = ( 'home' === $option_name )
460
			? 'WP_HOME'
461
			: 'WP_SITEURL';
462
463
		// Since we disregard the constant for multisites in ms-default-filters.php,
464
		// let's also use the db value if this is a multisite.
465
		if ( ! is_multisite() && Constants::is_defined( $constant ) ) {
466
			$value = Constants::get_constant( $constant );
467
		} else {
468
			// Let's get the option from the database so that we can bypass filters. This will help
469
			// ensure that we get more uniform values.
470
			$value = \Jetpack_Options::get_raw_option( $option_name );
471
		}
472
473
		return $value;
474
	}
475
476
	/**
477
	 * Normalize domains by removing www unless declared in the site's option.
478
	 *
479
	 * @param string   $option Option value from the site.
480
	 * @param callable $url_function Function retrieving the URL to normalize.
481
	 * @return mixed|string URL.
482
	 */
483
	public static function normalize_www_in_url( $option, $url_function ) {
484
		$url        = wp_parse_url( call_user_func( $url_function ) );
485
		$option_url = wp_parse_url( get_option( $option ) );
486
487
		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...
488
			return $url;
489
		}
490
491 View Code Duplication
		if ( "www.{$option_url[ 'host' ]}" === $url['host'] ) {
492
			// remove www if not present in option URL.
493
			$url['host'] = $option_url['host'];
494
		}
495 View Code Duplication
		if ( "www.{$url[ 'host' ]}" === $option_url['host'] ) {
496
			// add www if present in option URL.
497
			$url['host'] = $option_url['host'];
498
		}
499
500
		$normalized_url = "{$url['scheme']}://{$url['host']}";
501
		if ( isset( $url['path'] ) ) {
502
			$normalized_url .= "{$url['path']}";
503
		}
504
505
		if ( isset( $url['query'] ) ) {
506
			$normalized_url .= "?{$url['query']}";
507
		}
508
509
		return $normalized_url;
510
	}
511
512
	/**
513
	 * Return filtered value of get_plugins.
514
	 *
515
	 * @return mixed|void
516
	 */
517
	public static function get_plugins() {
518
		if ( ! function_exists( 'get_plugins' ) ) {
519
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
520
		}
521
522
		/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
523
		return apply_filters( 'all_plugins', get_plugins() );
524
	}
525
526
	/**
527
	 * Get custom action link tags that the plugin is using
528
	 * Ref: https://codex.wordpress.org/Plugin_API/Filter_Reference/plugin_action_links_(plugin_file_name)
529
	 *
530
	 * @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...
531
	 * @return array of plugin action links (key: link name value: url)
532
	 */
533
	public static function get_plugins_action_links( $plugin_file_singular = null ) {
534
		// Some sites may have DOM disabled in PHP fail early.
535
		if ( ! class_exists( 'DOMDocument' ) ) {
536
			return array();
537
		}
538
		$plugins_action_links = get_option( 'jetpack_plugin_api_action_links', array() );
539
		if ( ! empty( $plugins_action_links ) ) {
540
			if ( is_null( $plugin_file_singular ) ) {
541
				return $plugins_action_links;
542
			}
543
			return ( isset( $plugins_action_links[ $plugin_file_singular ] ) ? $plugins_action_links[ $plugin_file_singular ] : null );
544
		}
545
		return array();
546
	}
547
548
	/**
549
	 * Return the WP version as defined in the $wp_version global.
550
	 *
551
	 * @return string
552
	 */
553
	public static function wp_version() {
554
		global $wp_version;
555
		return $wp_version;
556
	}
557
558
	/**
559
	 * Return site icon url used on the site.
560
	 *
561
	 * @param int $size Size of requested icon in pixels.
562
	 * @return mixed|string|void
563
	 */
564
	public static function site_icon_url( $size = 512 ) {
565
		$site_icon = get_site_icon_url( $size );
566
		return $site_icon ? $site_icon : get_option( 'jetpack_site_icon_url' );
567
	}
568
569
	/**
570
	 * Return roles registered on the site.
571
	 *
572
	 * @return array
573
	 */
574
	public static function roles() {
575
		$wp_roles = wp_roles();
576
		return $wp_roles->roles;
577
	}
578
579
	/**
580
	 * Determine time zone from WordPress' options "timezone_string"
581
	 * and "gmt_offset".
582
	 *
583
	 * 1. Check if `timezone_string` is set and return it.
584
	 * 2. Check if `gmt_offset` is set, formats UTC-offset from it and return it.
585
	 * 3. Default to "UTC+0" if nothing is set.
586
	 *
587
	 * Note: This function is specifically not using wp_timezone() to keep consistency with
588
	 * the existing formatting of the timezone string.
589
	 *
590
	 * @return string
591
	 */
592
	public static function get_timezone() {
593
		$timezone_string = get_option( 'timezone_string' );
594
595
		if ( ! empty( $timezone_string ) ) {
596
			return str_replace( '_', ' ', $timezone_string );
597
		}
598
599
		$gmt_offset = get_option( 'gmt_offset', 0 );
600
601
		$formatted_gmt_offset = sprintf( '%+g', (float) $gmt_offset );
602
603
		$formatted_gmt_offset = str_replace(
604
			array( '.25', '.5', '.75' ),
605
			array( ':15', ':30', ':45' ),
606
			(string) $formatted_gmt_offset
607
		);
608
609
		/* translators: %s is UTC offset, e.g. "+1" */
610
		return sprintf( __( 'UTC%s', 'jetpack' ), $formatted_gmt_offset );
611
	}
612
613
	/**
614
	 * Return list of paused themes.
615
	 *
616
	 * @return array|bool Array of paused themes or false if unsupported.
617
	 */
618
	public static function get_paused_themes() {
619
		$paused_themes = wp_paused_themes();
620
		return $paused_themes->get_all();
621
	}
622
623
	/**
624
	 * Return list of paused plugins.
625
	 *
626
	 * @return array|bool Array of paused plugins or false if unsupported.
627
	 */
628
	public static function get_paused_plugins() {
629
		$paused_plugins = wp_paused_plugins();
630
		return $paused_plugins->get_all();
631
	}
632
633
	/**
634
	 * Return the theme's supported features.
635
	 * Used for syncing the supported feature that we care about.
636
	 *
637
	 * @return array List of features that the theme supports.
638
	 */
639
	public static function get_theme_support() {
640
		global $_wp_theme_features;
641
642
		$theme_support = array();
643
		foreach ( Defaults::$default_theme_support_whitelist as $theme_feature ) {
644
			$has_support = current_theme_supports( $theme_feature );
645
			if ( $has_support ) {
646
				$theme_support[ $theme_feature ] = $_wp_theme_features[ $theme_feature ];
647
			}
648
		}
649
650
		return $theme_support;
651
	}
652
653
	/**
654
	 * Wraps data in a way so that we can distinguish between objects and array and also prevent object recursion.
655
	 *
656
	 * @since 9.5.0
657
	 *
658
	 * @param array|obj $any        Source data to be cleaned up.
659
	 * @param array     $seen_nodes Built array of nodes.
660
	 *
661
	 * @return array
662
	 */
663
	public static function json_wrap( &$any, $seen_nodes = array() ) {
664
		if ( is_object( $any ) ) {
665
			$input        = get_object_vars( $any );
666
			$input['__o'] = 1;
667
		} else {
668
			$input = &$any;
669
		}
670
671
		if ( is_array( $input ) ) {
672
			$seen_nodes[] = &$any;
673
674
			$return = array();
675
676
			foreach ( $input as $k => &$v ) {
677
				if ( ( is_array( $v ) || is_object( $v ) ) ) {
678
					if ( in_array( $v, $seen_nodes, true ) ) {
679
						continue;
680
					}
681
					$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...
682
				} else {
683
					$return[ $k ] = $v;
684
				}
685
			}
686
687
			return $return;
688
		}
689
690
		return $any;
691
692
	}
693
}
694