Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
1.48% covered (danger)
1.48%
3 / 203
2.86% covered (danger)
2.86%
1 / 35
CRAP
0.00% covered (danger)
0.00%
0 / 1
Functions
1.48% covered (danger)
1.48%
3 / 203
2.86% covered (danger)
2.86%
1 / 35
8725.76
0.00% covered (danger)
0.00%
0 / 1
 get_modules
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 get_taxonomies
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 get_shortcodes
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 sanitize_taxonomy
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
72
 get_post_types
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 sanitize_post_type
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 expand_synced_post_type
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 get_post_type_features
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_hosting_provider
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 get_hosting_provider_by_known_constant
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
30
 get_hosting_provider_by_known_class
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 get_hosting_provider_by_known_function
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 rest_api_allowed_post_types
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 rest_api_allowed_public_metadata
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_version_controlled
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 file_system_write_access
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
30
 get_raw_or_filtered_url
n/a
0 / 0
n/a
0 / 0
1
 home_url
n/a
0 / 0
n/a
0 / 0
1
 site_url
n/a
0 / 0
n/a
0 / 0
1
 main_network_site_url
n/a
0 / 0
n/a
0 / 0
1
 main_network_site_wpcom_id
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 get_protocol_normalized_url
n/a
0 / 0
n/a
0 / 0
1
 get_raw_url
n/a
0 / 0
n/a
0 / 0
1
 normalize_www_in_url
n/a
0 / 0
n/a
0 / 0
1
 get_plugins
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 get_plugins_action_links
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 wp_version
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 site_icon_url
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 roles
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 get_timezone
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 get_paused_themes
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 get_paused_plugins
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 get_theme_support
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 get_is_fse_theme
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_site_visibility
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 json_wrap
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
72
 get_themes
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 get_active_modules
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_loaded_extensions
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
42
 get_jetpack_connection_active_plugins
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_jetpack_sync_active_modules
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 get_jetpack_package_versions
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Utility functions to generate data synced to wpcom
4 *
5 * @package automattic/jetpack-sync
6 */
7
8namespace Automattic\Jetpack\Sync;
9
10use Automattic\Jetpack\Connection\Manager;
11use Automattic\Jetpack\Connection\Urls;
12use Automattic\Jetpack\Constants;
13use Automattic\Jetpack\Modules as Jetpack_Modules;
14use Automattic\Jetpack\Status;
15
16/**
17 * Utility functions to generate data synced to wpcom
18 */
19class Functions {
20    const HTTPS_CHECK_OPTION_PREFIX = 'jetpack_sync_https_history_';
21    const HTTPS_CHECK_HISTORY       = 5;
22
23    /**
24     * Return array of Jetpack modules.
25     *
26     * @return array
27     */
28    public static function get_modules() {
29        if ( defined( 'JETPACK__PLUGIN_DIR' ) ) {
30            require_once JETPACK__PLUGIN_DIR . 'class.jetpack-admin.php';
31
32            return \Jetpack_Admin::init()->get_modules();
33        }
34
35        return array();
36    }
37
38    /**
39     * Return array of taxonomies registered on the site.
40     *
41     * @return array
42     */
43    public static function get_taxonomies() {
44        global $wp_taxonomies;
45        $wp_taxonomies_without_callbacks = array();
46        foreach ( $wp_taxonomies as $taxonomy_name => $taxonomy ) {
47            $sanitized_taxonomy = self::sanitize_taxonomy( $taxonomy );
48            if ( ! empty( $sanitized_taxonomy ) ) {
49                $wp_taxonomies_without_callbacks[ $taxonomy_name ] = $sanitized_taxonomy;
50            }
51        }
52        return $wp_taxonomies_without_callbacks;
53    }
54
55    /**
56     * Return array of registered shortcodes.
57     *
58     * @return array
59     */
60    public static function get_shortcodes() {
61        global $shortcode_tags;
62        return array_keys( $shortcode_tags );
63    }
64
65    /**
66     * Removes any callback data since we will not be able to process it on our side anyways.
67     *
68     * @param \WP_Taxonomy $taxonomy \WP_Taxonomy item.
69     *
70     * @return mixed|null
71     */
72    public static function sanitize_taxonomy( $taxonomy ) {
73
74        // Lets clone the taxonomy object instead of modifing the global one.
75        $cloned_taxonomy = json_decode( wp_json_encode( $taxonomy, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) );
76
77        // recursive taxonomies are no fun.
78        if ( $cloned_taxonomy === null ) {
79            return null;
80        }
81        // Remove any meta_box_cb if they are not the default wp ones.
82        if ( isset( $cloned_taxonomy->meta_box_cb ) &&
83            ! in_array( $cloned_taxonomy->meta_box_cb, array( 'post_tags_meta_box', 'post_categories_meta_box' ), true ) ) {
84            $cloned_taxonomy->meta_box_cb = null;
85        }
86        // Remove update call back.
87        if ( isset( $cloned_taxonomy->update_count_callback ) &&
88            $cloned_taxonomy->update_count_callback !== null ) {
89            $cloned_taxonomy->update_count_callback = null;
90        }
91        // Remove rest_controller_class if it something other then the default.
92        if ( isset( $cloned_taxonomy->rest_controller_class ) &&
93            'WP_REST_Terms_Controller' !== $cloned_taxonomy->rest_controller_class ) {
94            $cloned_taxonomy->rest_controller_class = null;
95        }
96        return $cloned_taxonomy;
97    }
98
99    /**
100     * Return array of registered post types.
101     *
102     * @return array
103     */
104    public static function get_post_types() {
105        global $wp_post_types;
106
107        $post_types_without_callbacks = array();
108        foreach ( $wp_post_types as $post_type_name => $post_type ) {
109            $sanitized_post_type = self::sanitize_post_type( $post_type );
110            if ( ! empty( $sanitized_post_type ) ) {
111                $post_types_without_callbacks[ $post_type_name ] = $sanitized_post_type;
112            }
113        }
114        return $post_types_without_callbacks;
115    }
116
117    /**
118     * Sanitizes by cloning post type object.
119     *
120     * @param object $post_type \WP_Post_Type.
121     *
122     * @return object
123     */
124    public static function sanitize_post_type( $post_type ) {
125        // Lets clone the post type object instead of modifing the global one.
126        $sanitized_post_type = array();
127        foreach ( Defaults::$default_post_type_attributes as $attribute_key => $default_value ) {
128            if ( isset( $post_type->{ $attribute_key } ) ) {
129                $sanitized_post_type[ $attribute_key ] = $post_type->{ $attribute_key };
130            }
131        }
132        return (object) $sanitized_post_type;
133    }
134
135    /**
136     * Return information about a synced post type.
137     *
138     * @param array  $sanitized_post_type Array of args used in constructing \WP_Post_Type.
139     * @param string $post_type Post type name.
140     *
141     * @return object \WP_Post_Type
142     */
143    public static function expand_synced_post_type( $sanitized_post_type, $post_type ) {
144        $post_type        = sanitize_key( $post_type );
145        $post_type_object = new \WP_Post_Type( $post_type, $sanitized_post_type );
146        $post_type_object->add_supports();
147        $post_type_object->add_rewrite_rules();
148        $post_type_object->add_hooks();
149        $post_type_object->register_taxonomies();
150        return $post_type_object;
151    }
152
153    /**
154     * Returns site's post_type_features.
155     *
156     * @return array
157     */
158    public static function get_post_type_features() {
159        global $_wp_post_type_features;
160
161        return $_wp_post_type_features;
162    }
163
164    /**
165     * Return hosting provider.
166     *
167     * Uses a set of known constants, classes, or functions to help determine the hosting platform.
168     *
169     * @return string Hosting provider.
170     */
171    public static function get_hosting_provider() {
172        $hosting_provider_detection_methods = array(
173            'get_hosting_provider_by_known_constant',
174            'get_hosting_provider_by_known_class',
175            'get_hosting_provider_by_known_function',
176        );
177
178        $functions = new Functions();
179        foreach ( $hosting_provider_detection_methods as $method ) {
180            $hosting_provider = call_user_func( array( $functions, $method ) );
181            if ( false !== $hosting_provider ) {
182                return $hosting_provider;
183            }
184        }
185
186        return 'unknown';
187    }
188
189    /**
190     * Return a hosting provider using a set of known constants.
191     *
192     * @return mixed A host identifier string or false.
193     */
194    public function get_hosting_provider_by_known_constant() {
195        $hosting_provider_constants = array(
196            'GD_SYSTEM_PLUGIN_DIR' => 'gd-managed-wp',
197            'MM_BASE_DIR'          => 'bh',
198            'PAGELYBIN'            => 'pagely',
199            'KINSTAMU_VERSION'     => 'kinsta',
200            'FLYWHEEL_CONFIG_DIR'  => 'flywheel',
201            'IS_PRESSABLE'         => 'pressable',
202            'VIP_GO_ENV'           => 'vip-go',
203        );
204
205        foreach ( $hosting_provider_constants as $constant => $constant_value ) {
206            if ( Constants::is_defined( $constant ) ) {
207                if ( 'VIP_GO_ENV' === $constant && false === Constants::get_constant( 'VIP_GO_ENV' ) ) {
208                    continue;
209                }
210                return $constant_value;
211            }
212        }
213
214        return false;
215    }
216
217    /**
218     * Return a hosting provider using a set of known classes.
219     *
220     * @return mixed A host identifier string or false.
221     */
222    public function get_hosting_provider_by_known_class() {
223        $hosting_provider = false;
224
225        switch ( true ) {
226            case ( class_exists( '\\WPaaS\\Plugin' ) ):
227                $hosting_provider = 'gd-managed-wp';
228                break;
229        }
230
231        return $hosting_provider;
232    }
233
234    /**
235     * Return a hosting provider using a set of known functions.
236     *
237     * @return mixed A host identifier string or false.
238     */
239    public function get_hosting_provider_by_known_function() {
240        $hosting_provider = false;
241
242        switch ( true ) {
243            case ( function_exists( 'is_wpe' ) || function_exists( 'is_wpe_snapshot' ) ):
244                $hosting_provider = 'wpe';
245                break;
246        }
247
248        return $hosting_provider;
249    }
250
251    /**
252     * Return array of allowed REST API post types.
253     *
254     * @return array Array of allowed post types.
255     */
256    public static function rest_api_allowed_post_types() {
257        /** This filter is already documented in class.json-api-endpoints.php */
258        return apply_filters( 'rest_api_allowed_post_types', array( 'post', 'page', 'revision' ) );
259    }
260
261    /**
262     * Return array of allowed REST API public metadata.
263     *
264     * @return array Array of allowed metadata.
265     */
266    public static function rest_api_allowed_public_metadata() {
267        /**
268         * Filters the meta keys accessible by the REST API.
269         *
270         * @see https://developer.wordpress.com/2013/04/26/custom-post-type-and-metadata-support-in-the-rest-api/
271         *
272         * @module json-api
273         *
274         * @since 1.6.3
275         * @since-jetpack 2.2.3
276         *
277         * @param array $whitelisted_meta Array of metadata that is accessible by the REST API.
278         */
279        return apply_filters( 'rest_api_allowed_public_metadata', array() );
280    }
281
282    /**
283     * Finds out if a site is using a version control system.
284     *
285     * @return bool
286     **/
287    public static function is_version_controlled() {
288
289        if ( ! class_exists( 'WP_Automatic_Updater' ) ) {
290            require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
291        }
292        $updater = new \WP_Automatic_Updater();
293
294        return (bool) (string) $updater->is_vcs_checkout( ABSPATH );
295    }
296
297    /**
298     * Returns true if the site has file write access false otherwise.
299     *
300     * @return bool
301     **/
302    public static function file_system_write_access() {
303        if ( ! function_exists( 'get_filesystem_method' ) ) {
304            require_once ABSPATH . 'wp-admin/includes/file.php';
305        }
306
307        require_once ABSPATH . 'wp-admin/includes/template.php';
308
309        $filesystem_method = get_filesystem_method();
310        if ( 'direct' === $filesystem_method ) {
311            return true;
312        }
313
314        ob_start();
315
316        if ( ! function_exists( 'request_filesystem_credentials' ) ) {
317            require_once ABSPATH . 'wp-admin/includes/file.php';
318        }
319
320        $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
321        ob_end_clean();
322        if ( $filesystem_credentials_are_stored ) {
323            return true;
324        }
325
326        return false;
327    }
328
329    /**
330     * Helper function that is used when getting home or siteurl values. Decides
331     * whether to get the raw or filtered value.
332     *
333     * @deprecated 1.23.1
334     *
335     * @param string $url_type URL to get, home or siteurl.
336     * @return string
337     */
338    public static function get_raw_or_filtered_url( $url_type ) {
339        _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::get_raw_or_filtered_url' );
340        return Urls::get_raw_or_filtered_url( $url_type );
341    }
342
343    /**
344     * Return the escaped home_url.
345     *
346     * @deprecated 1.23.1
347     *
348     * @return string
349     */
350    public static function home_url() {
351        _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::home_url' );
352        return Urls::home_url();
353    }
354
355    /**
356     * Return the escaped siteurl.
357     *
358     * @deprecated 1.23.1
359     *
360     * @return string
361     */
362    public static function site_url() {
363        _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::site_url' );
364        return Urls::site_url();
365    }
366
367    /**
368     * Return main site URL with a normalized protocol.
369     *
370     * @deprecated 1.23.1
371     *
372     * @return string
373     */
374    public static function main_network_site_url() {
375        _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::main_network_site_url' );
376        return Urls::main_network_site_url();
377    }
378
379    /**
380     * Return main site WordPress.com site ID.
381     *
382     * @return string
383     */
384    public static function main_network_site_wpcom_id() {
385        /**
386         * Return the current site WPCOM ID for single site installs
387         */
388        if ( ! is_multisite() ) {
389            return \Jetpack_Options::get_option( 'id' );
390        }
391
392        /**
393         * Return the main network site WPCOM ID for multi-site installs
394         */
395        $current_network = get_network();
396        switch_to_blog( (int) $current_network->blog_id );
397        $wpcom_blog_id = \Jetpack_Options::get_option( 'id' );
398        restore_current_blog();
399        return $wpcom_blog_id;
400    }
401
402    /**
403     * Return URL with a normalized protocol.
404     *
405     * @deprecated 1.23.1
406     *
407     * @param string $callable Function name that was used to retrieve URL option.
408     * @param string $new_value URL Protocol to set URLs to.
409     * @return string Normalized URL.
410     */
411    public static function get_protocol_normalized_url( $callable, $new_value ) {
412        _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::get_protocol_normalized_url' );
413        return Urls::get_protocol_normalized_url( $callable, $new_value );
414    }
415
416    /**
417     * Return URL from option or PHP constant.
418     *
419     * @deprecated 1.23.1
420     *
421     * @param string $option_name (e.g. 'home').
422     *
423     * @return mixed|null URL.
424     */
425    public static function get_raw_url( $option_name ) {
426        _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::get_raw_url' );
427        return Urls::get_raw_url( $option_name );
428    }
429
430    /**
431     * Normalize domains by removing www unless declared in the site's option.
432     *
433     * @deprecated 1.23.1
434     *
435     * @param string   $option Option value from the site.
436     * @param callable $url_function Function retrieving the URL to normalize.
437     * @return mixed|string URL.
438     */
439    public static function normalize_www_in_url( $option, $url_function ) {
440        _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::normalize_www_in_url' );
441        return Urls::normalize_www_in_url( $option, $url_function );
442    }
443
444    /**
445     * Return filtered value of get_plugins.
446     *
447     * @return mixed|void
448     */
449    public static function get_plugins() {
450        if ( ! function_exists( 'get_plugins' ) ) {
451            require_once ABSPATH . 'wp-admin/includes/plugin.php';
452        }
453
454        /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
455        return apply_filters( 'all_plugins', get_plugins() );
456    }
457
458    /**
459     * Get custom action link tags that the plugin is using
460     * Ref: https://codex.wordpress.org/Plugin_API/Filter_Reference/plugin_action_links_(plugin_file_name)
461     *
462     * @param string $plugin_file_singular Particular plugin.
463     * @return array of plugin action links (key: link name value: url)
464     */
465    public static function get_plugins_action_links( $plugin_file_singular = null ) {
466        // Some sites may have DOM disabled in PHP fail early.
467        if ( ! class_exists( 'DOMDocument' ) ) {
468            return array();
469        }
470        $plugins_action_links = get_option( 'jetpack_plugin_api_action_links', array() );
471        if ( ! empty( $plugins_action_links ) ) {
472            if ( $plugin_file_singular === null ) {
473                return $plugins_action_links;
474            }
475            return ( $plugins_action_links[ $plugin_file_singular ] ?? null );
476        }
477        return array();
478    }
479
480    /**
481     * Return the WP version as defined in the $wp_version global.
482     *
483     * @return string
484     */
485    public static function wp_version() {
486        global $wp_version;
487        return $wp_version;
488    }
489
490    /**
491     * Return site icon url used on the site.
492     *
493     * @param int $size Size of requested icon in pixels.
494     * @return mixed|string|void
495     */
496    public static function site_icon_url( $size = 512 ) {
497        $site_icon = get_site_icon_url( $size );
498        return $site_icon ? $site_icon : get_option( 'jetpack_site_icon_url' );
499    }
500
501    /**
502     * Return roles registered on the site.
503     *
504     * @return array
505     */
506    public static function roles() {
507        $wp_roles = wp_roles();
508        return $wp_roles->roles;
509    }
510
511    /**
512     * Determine time zone from WordPress' options "timezone_string"
513     * and "gmt_offset".
514     *
515     * 1. Check if `timezone_string` is set and return it.
516     * 2. Check if `gmt_offset` is set, formats UTC-offset from it and return it.
517     * 3. Default to "UTC+0" if nothing is set.
518     *
519     * Note: This function is specifically not using wp_timezone() to keep consistency with
520     * the existing formatting of the timezone string.
521     *
522     * @return string
523     */
524    public static function get_timezone() {
525        $timezone_string = get_option( 'timezone_string' );
526
527        if ( ! empty( $timezone_string ) ) {
528            return str_replace( '_', ' ', $timezone_string );
529        }
530
531        $gmt_offset = get_option( 'gmt_offset', 0 );
532
533        $formatted_gmt_offset = sprintf( '%+g', (float) $gmt_offset );
534
535        $formatted_gmt_offset = str_replace(
536            array( '.25', '.5', '.75' ),
537            array( ':15', ':30', ':45' ),
538            $formatted_gmt_offset
539        );
540
541        /* translators: %s is UTC offset, e.g. "+1" */
542        return sprintf( __( 'UTC%s', 'jetpack-sync' ), $formatted_gmt_offset );
543    }
544
545    /**
546     * Return list of paused themes.
547     *
548     * @return array|bool Array of paused themes or false if unsupported.
549     */
550    public static function get_paused_themes() {
551        $paused_themes = wp_paused_themes();
552        return $paused_themes->get_all();
553    }
554
555    /**
556     * Return list of paused plugins.
557     *
558     * @return array|bool Array of paused plugins or false if unsupported.
559     */
560    public static function get_paused_plugins() {
561        $paused_plugins = wp_paused_plugins();
562        return $paused_plugins->get_all();
563    }
564
565    /**
566     * Return the theme's supported features.
567     * Used for syncing the supported feature that we care about.
568     *
569     * @return array List of features that the theme supports.
570     */
571    public static function get_theme_support() {
572        global $_wp_theme_features;
573
574        $theme_support = array();
575        foreach ( Defaults::$default_theme_support_whitelist as $theme_feature ) {
576            $has_support = current_theme_supports( $theme_feature );
577            if ( $has_support ) {
578                $theme_support[ $theme_feature ] = $_wp_theme_features[ $theme_feature ];
579            }
580        }
581
582        return $theme_support;
583    }
584
585    /**
586     * Returns if the current theme is a Full Site Editing theme.
587     *
588     * @since 1.49.0 Uses wp_is_block_theme() instead of deprecated gutenberg_is_fse_theme().
589     *
590     * @return bool Theme is a Full Site Editing theme.
591     */
592    public static function get_is_fse_theme() {
593        return wp_is_block_theme();
594    }
595
596    /**
597     * Returns the site's visibility as a `blog_public` value, or -1 when Jetpack considers the site private.
598     *
599     * Lets `jetpack_is_private_site` report the site as private to WordPress.com without changing
600     * the `blog_public` option itself, since core and other plugins do not expect a -1 there.
601     *
602     * @since 5.2.0
603     *
604     * @return int -1 when private, otherwise the stored `blog_public` value.
605     */
606    public static function get_site_visibility() {
607        if ( ( new Status() )->is_private_site() ) {
608            return -1;
609        }
610
611        return (int) get_option( 'blog_public', 1 );
612    }
613
614    /**
615     * Wraps data in a way so that we can distinguish between objects and array and also prevent object recursion.
616     *
617     * @since 1.21.0
618     *
619     * @param mixed $any        Source data to be cleaned up.
620     * @param array $seen_nodes Built array of nodes.
621     *
622     * @return array
623     */
624    public static function json_wrap( &$any, $seen_nodes = array() ) {
625        if ( is_object( $any ) ) {
626            $input = get_object_vars( $any );
627
628            // WordPress 6.9 introduced lazy-loading of WP_User `roles`, `caps`, and `allcaps` properties.
629            // It also made said properties protected, so we need to access them and set them as keys manually.
630            if ( $any instanceof \WP_User ) {
631                $roles            = $any->roles;
632                $caps             = $any->caps;
633                $allcaps          = $any->allcaps;
634                $input['roles']   = $roles;
635                $input['caps']    = $caps;
636                $input['allcaps'] = $allcaps;
637            }
638
639            $input['__o'] = 1;
640        } else {
641            $input = &$any;
642        }
643
644        if ( is_array( $input ) ) {
645            $seen_nodes[] = &$any;
646
647            $return = array();
648
649            foreach ( $input as $k => &$v ) {
650                if ( ( is_array( $v ) || is_object( $v ) ) ) {
651                    if ( in_array( $v, $seen_nodes, true ) ) {
652                        continue;
653                    }
654                    $return[ $k ] = self::json_wrap( $v, $seen_nodes );
655                } else {
656                    $return[ $k ] = $v;
657                }
658            }
659
660            return $return;
661        }
662
663        return $any;
664    }
665
666    /**
667     * Return the list of installed themes
668     *
669     * @since 1.31.0
670     *
671     * @return array
672     */
673    public static function get_themes() {
674        $current_stylesheet = get_stylesheet();
675        $installed_themes   = wp_get_themes();
676        $synced_headers     = array( 'Name', 'ThemeURI', 'Author', 'Version', 'Template', 'Status', 'TextDomain', 'RequiresWP', 'RequiresPHP' );
677        $themes             = array();
678        foreach ( $installed_themes as $stylesheet => $theme ) {
679            $themes[ $stylesheet ] = array();
680            foreach ( $synced_headers as $header ) {
681                $themes[ $stylesheet ][ $header ] = $theme->get( $header );
682            }
683            $themes[ $stylesheet ]['active'] = $stylesheet === $current_stylesheet;
684            if ( method_exists( $theme, 'is_block_theme' ) ) {
685                $themes[ $stylesheet ]['is_block_theme'] = $theme->is_block_theme();
686            }
687        }
688        /**
689         * Filters the output of Sync's get_theme callable
690         *
691         * @since 1.31.0
692         *
693         * @param array $themes The list of installed themes formatted in an array with a collection of information extracted from the Theme's headers
694         */
695        return apply_filters( 'jetpack_sync_get_themes_callable', $themes );
696    }
697
698    /**
699     * Return the list of active Jetpack modules.
700     *
701     * @since 1.34.0
702     *
703     * @return array
704     */
705    public static function get_active_modules() {
706        return ( new Jetpack_Modules() )->get_active();
707    }
708
709    /**
710     * Return a list of PHP modules that we want to track.
711     *
712     * @since 1.50.0
713     *
714     * @return array
715     */
716    public static function get_loaded_extensions() {
717        if ( function_exists( 'get_loaded_extensions' ) ) {
718            return get_loaded_extensions();
719        }
720
721        // If a hosting provider has blocked get_loaded_extensions for any reason,
722        // we check extensions manually.
723
724        $extensions_to_check = array(
725            'libxml' => array( 'class' => 'libXMLError' ),
726            'xml'    => array( 'function' => 'xml_parse' ),
727            'dom'    => array( 'class' => 'DOMDocument' ),
728            'xdebug' => array( 'function' => 'xdebug_break' ),
729        );
730
731        $enabled_extensions = array();
732        foreach ( $extensions_to_check as $extension_name => $extension ) {
733            if (
734                ( isset( $extension['function'] )
735                && function_exists( $extension['function'] ) )
736                || class_exists( $extension['class'] )
737            ) {
738                $enabled_extensions[] = $extension_name;
739            }
740        }
741
742        return $enabled_extensions;
743    }
744
745    /**
746     * Return the list of active connected Jetpack plugins.
747     *
748     * @since 3.2.0
749     *
750     * @return array
751     */
752    public static function get_jetpack_connection_active_plugins() {
753        return ( new Manager() )->get_connected_plugins();
754    }
755
756    /**
757     * Return the list of active sync modules.
758     *
759     * @since 3.6.0
760     *
761     * @return array
762     */
763    public static function get_jetpack_sync_active_modules() {
764
765        /** This filter is documented in projects/packages/sync/src/class-modules.php */
766        $modules = apply_filters( 'jetpack_sync_modules', Modules::DEFAULT_SYNC_MODULES );
767        $modules = array_unique( $modules );
768        $modules = array_map( 'wp_normalize_path', $modules );
769        return $modules;
770    }
771
772    /**
773     * Return the list of Jetpack package versions.
774     *
775     * @since 4.11.1
776     *
777     * @return array
778     */
779    public static function get_jetpack_package_versions() {
780        return apply_filters( 'jetpack_package_versions', array() );
781    }
782}