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