changeset 21 | 48c4eec2b7e6 |
parent 19 | 3d72ae0968f4 |
child 22 | 8c2e4d02f4ef |
20:7b1b88e27a20 | 21:48c4eec2b7e6 |
---|---|
5 * @package WordPress |
5 * @package WordPress |
6 * @subpackage Site_Health |
6 * @subpackage Site_Health |
7 * @since 5.2.0 |
7 * @since 5.2.0 |
8 */ |
8 */ |
9 |
9 |
10 #[AllowDynamicProperties] |
|
10 class WP_Site_Health { |
11 class WP_Site_Health { |
11 private static $instance = null; |
12 private static $instance = null; |
12 |
13 |
13 private $mysql_min_version_check; |
14 private $is_acceptable_mysql_version; |
14 private $mysql_rec_version_check; |
15 private $is_recommended_mysql_version; |
15 |
16 |
16 public $is_mariadb = false; |
17 public $is_mariadb = false; |
17 private $mysql_server_version = ''; |
18 private $mysql_server_version = ''; |
18 private $health_check_mysql_required_version = '5.5'; |
19 private $mysql_required_version = '5.5'; |
19 private $health_check_mysql_rec_version = ''; |
20 private $mysql_recommended_version = '8.0'; |
21 private $mariadb_recommended_version = '10.5'; |
|
20 |
22 |
21 public $php_memory_limit; |
23 public $php_memory_limit; |
22 |
24 |
23 public $schedules; |
25 public $schedules; |
24 public $crons; |
26 public $crons; |
53 |
55 |
54 add_action( 'site_health_tab_content', array( $this, 'show_site_health_tab' ) ); |
56 add_action( 'site_health_tab_content', array( $this, 'show_site_health_tab' ) ); |
55 } |
57 } |
56 |
58 |
57 /** |
59 /** |
58 * Output the content of a tab in the Site Health screen. |
60 * Outputs the content of a tab in the Site Health screen. |
59 * |
61 * |
60 * @since 5.8.0 |
62 * @since 5.8.0 |
61 * |
63 * |
62 * @param string $tab Slug of the current tab being displayed. |
64 * @param string $tab Slug of the current tab being displayed. |
63 */ |
65 */ |
64 public function show_site_health_tab( $tab ) { |
66 public function show_site_health_tab( $tab ) { |
65 if ( 'debug' === $tab ) { |
67 if ( 'debug' === $tab ) { |
66 require_once ABSPATH . '/wp-admin/site-health-info.php'; |
68 require_once ABSPATH . 'wp-admin/site-health-info.php'; |
67 } |
69 } |
68 } |
70 } |
69 |
71 |
70 /** |
72 /** |
71 * Return an instance of the WP_Site_Health class, or create one if none exist yet. |
73 * Returns an instance of the WP_Site_Health class, or create one if none exist yet. |
72 * |
74 * |
73 * @since 5.4.0 |
75 * @since 5.4.0 |
74 * |
76 * |
75 * @return WP_Site_Health|null |
77 * @return WP_Site_Health|null |
76 */ |
78 */ |
158 |
160 |
159 wp_localize_script( 'site-health', 'SiteHealth', $health_check_js_variables ); |
161 wp_localize_script( 'site-health', 'SiteHealth', $health_check_js_variables ); |
160 } |
162 } |
161 |
163 |
162 /** |
164 /** |
163 * Run a Site Health test directly. |
165 * Runs a Site Health test directly. |
164 * |
166 * |
165 * @since 5.4.0 |
167 * @since 5.4.0 |
166 * |
168 * |
167 * @param callable $callback |
169 * @param callable $callback |
168 * @return mixed|void |
170 * @return mixed|void |
191 */ |
193 */ |
192 return apply_filters( 'site_status_test_result', call_user_func( $callback ) ); |
194 return apply_filters( 'site_status_test_result', call_user_func( $callback ) ); |
193 } |
195 } |
194 |
196 |
195 /** |
197 /** |
196 * Run the SQL version checks. |
198 * Runs the SQL version checks. |
197 * |
199 * |
198 * These values are used in later tests, but the part of preparing them is more easily managed |
200 * These values are used in later tests, but the part of preparing them is more easily managed |
199 * early in the class for ease of access and discovery. |
201 * early in the class for ease of access and discovery. |
200 * |
202 * |
201 * @since 5.2.0 |
203 * @since 5.2.0 |
203 * @global wpdb $wpdb WordPress database abstraction object. |
205 * @global wpdb $wpdb WordPress database abstraction object. |
204 */ |
206 */ |
205 private function prepare_sql_data() { |
207 private function prepare_sql_data() { |
206 global $wpdb; |
208 global $wpdb; |
207 |
209 |
208 if ( $wpdb->use_mysqli ) { |
210 $mysql_server_type = $wpdb->db_server_info(); |
209 // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysqli_get_server_info |
|
210 $mysql_server_type = mysqli_get_server_info( $wpdb->dbh ); |
|
211 } else { |
|
212 // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysql_get_server_info,PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved |
|
213 $mysql_server_type = mysql_get_server_info( $wpdb->dbh ); |
|
214 } |
|
215 |
211 |
216 $this->mysql_server_version = $wpdb->get_var( 'SELECT VERSION()' ); |
212 $this->mysql_server_version = $wpdb->get_var( 'SELECT VERSION()' ); |
217 |
213 |
218 $this->health_check_mysql_rec_version = '5.6'; |
|
219 |
|
220 if ( stristr( $mysql_server_type, 'mariadb' ) ) { |
214 if ( stristr( $mysql_server_type, 'mariadb' ) ) { |
221 $this->is_mariadb = true; |
215 $this->is_mariadb = true; |
222 $this->health_check_mysql_rec_version = '10.0'; |
216 $this->mysql_recommended_version = $this->mariadb_recommended_version; |
223 } |
217 } |
224 |
218 |
225 $this->mysql_min_version_check = version_compare( '5.5', $this->mysql_server_version, '<=' ); |
219 $this->is_acceptable_mysql_version = version_compare( $this->mysql_required_version, $this->mysql_server_version, '<=' ); |
226 $this->mysql_rec_version_check = version_compare( $this->health_check_mysql_rec_version, $this->mysql_server_version, '<=' ); |
220 $this->is_recommended_mysql_version = version_compare( $this->mysql_recommended_version, $this->mysql_server_version, '<=' ); |
227 } |
221 } |
228 |
222 |
229 /** |
223 /** |
230 * Test if `wp_version_check` is blocked. |
224 * Tests whether `wp_version_check` is blocked. |
231 * |
225 * |
232 * It's possible to block updates with the `wp_version_check` filter, but this can't be checked |
226 * It's possible to block updates with the `wp_version_check` filter, but this can't be checked |
233 * during an Ajax call, as the filter is never introduced then. |
227 * during an Ajax call, as the filter is never introduced then. |
234 * |
228 * |
235 * This filter overrides a standard page request if it's made by an admin through the Ajax call |
229 * This filter overrides a standard page request if it's made by an admin through the Ajax call |
347 |
341 |
348 return $result; |
342 return $result; |
349 } |
343 } |
350 |
344 |
351 /** |
345 /** |
352 * Test if plugins are outdated, or unnecessary. |
346 * Tests if plugins are outdated, or unnecessary. |
353 * |
347 * |
354 * The tests checks if your plugins are up to date, and encourages you to remove any |
348 * The test checks if your plugins are up to date, and encourages you to remove any |
355 * that are not in use. |
349 * that are not in use. |
356 * |
350 * |
357 * @since 5.2.0 |
351 * @since 5.2.0 |
358 * |
352 * |
359 * @return array The test result. |
353 * @return array The test result. |
379 ); |
373 ); |
380 |
374 |
381 $plugins = get_plugins(); |
375 $plugins = get_plugins(); |
382 $plugin_updates = get_plugin_updates(); |
376 $plugin_updates = get_plugin_updates(); |
383 |
377 |
384 $plugins_have_updates = false; |
378 $plugins_active = 0; |
385 $plugins_active = 0; |
379 $plugins_total = 0; |
386 $plugins_total = 0; |
380 $plugins_need_update = 0; |
387 $plugins_need_update = 0; |
|
388 |
381 |
389 // Loop over the available plugins and check their versions and active state. |
382 // Loop over the available plugins and check their versions and active state. |
390 foreach ( $plugins as $plugin_path => $plugin ) { |
383 foreach ( $plugins as $plugin_path => $plugin ) { |
391 $plugins_total++; |
384 ++$plugins_total; |
392 |
385 |
393 if ( is_plugin_active( $plugin_path ) ) { |
386 if ( is_plugin_active( $plugin_path ) ) { |
394 $plugins_active++; |
387 ++$plugins_active; |
395 } |
388 } |
396 |
|
397 $plugin_version = $plugin['Version']; |
|
398 |
389 |
399 if ( array_key_exists( $plugin_path, $plugin_updates ) ) { |
390 if ( array_key_exists( $plugin_path, $plugin_updates ) ) { |
400 $plugins_need_update++; |
391 ++$plugins_need_update; |
401 $plugins_have_updates = true; |
|
402 } |
392 } |
403 } |
393 } |
404 |
394 |
405 // Add a notice if there are outdated plugins. |
395 // Add a notice if there are outdated plugins. |
406 if ( $plugins_need_update > 0 ) { |
396 if ( $plugins_need_update > 0 ) { |
430 if ( 1 === $plugins_active ) { |
420 if ( 1 === $plugins_active ) { |
431 $result['description'] .= sprintf( |
421 $result['description'] .= sprintf( |
432 '<p>%s</p>', |
422 '<p>%s</p>', |
433 __( 'Your site has 1 active plugin, and it is up to date.' ) |
423 __( 'Your site has 1 active plugin, and it is up to date.' ) |
434 ); |
424 ); |
435 } else { |
425 } elseif ( $plugins_active > 0 ) { |
436 $result['description'] .= sprintf( |
426 $result['description'] .= sprintf( |
437 '<p>%s</p>', |
427 '<p>%s</p>', |
438 sprintf( |
428 sprintf( |
439 /* translators: %d: The number of active plugins. */ |
429 /* translators: %d: The number of active plugins. */ |
440 _n( |
430 _n( |
443 $plugins_active |
433 $plugins_active |
444 ), |
434 ), |
445 $plugins_active |
435 $plugins_active |
446 ) |
436 ) |
447 ); |
437 ); |
438 } else { |
|
439 $result['description'] .= sprintf( |
|
440 '<p>%s</p>', |
|
441 __( 'Your site does not have any active plugins.' ) |
|
442 ); |
|
448 } |
443 } |
449 } |
444 } |
450 |
445 |
451 // Check if there are inactive plugins. |
446 // Check if there are inactive plugins. |
452 if ( $plugins_total > $plugins_active && ! is_multisite() ) { |
447 if ( $plugins_total > $plugins_active && ! is_multisite() ) { |
479 |
474 |
480 return $result; |
475 return $result; |
481 } |
476 } |
482 |
477 |
483 /** |
478 /** |
484 * Test if themes are outdated, or unnecessary. |
479 * Tests if themes are outdated, or unnecessary. |
485 * |
480 * |
486 * Сhecks if your site has a default theme (to fall back on if there is a need), |
481 * Checks if your site has a default theme (to fall back on if there is a need), |
487 * if your themes are up to date and, finally, encourages you to remove any themes |
482 * if your themes are up to date and, finally, encourages you to remove any themes |
488 * that are not needed. |
483 * that are not needed. |
489 * |
484 * |
490 * @since 5.2.0 |
485 * @since 5.2.0 |
491 * |
486 * |
546 $using_default_theme = true; |
541 $using_default_theme = true; |
547 } |
542 } |
548 } |
543 } |
549 |
544 |
550 foreach ( $all_themes as $theme_slug => $theme ) { |
545 foreach ( $all_themes as $theme_slug => $theme ) { |
551 $themes_total++; |
546 ++$themes_total; |
552 |
547 |
553 if ( array_key_exists( $theme_slug, $theme_updates ) ) { |
548 if ( array_key_exists( $theme_slug, $theme_updates ) ) { |
554 $themes_need_updates++; |
549 ++$themes_need_updates; |
555 } |
550 } |
556 } |
551 } |
557 |
552 |
558 // If this is a child theme, increase the allowed theme count by one, to account for the parent. |
553 // If this is a child theme, increase the allowed theme count by one, to account for the parent. |
559 if ( is_child_theme() ) { |
554 if ( is_child_theme() ) { |
560 $allowed_theme_count++; |
555 ++$allowed_theme_count; |
561 } |
556 } |
562 |
557 |
563 // If there's a default theme installed and not in use, we count that as allowed as well. |
558 // If there's a default theme installed and not in use, we count that as allowed as well. |
564 if ( $has_default_theme && ! $using_default_theme ) { |
559 if ( $has_default_theme && ! $using_default_theme ) { |
565 $allowed_theme_count++; |
560 ++$allowed_theme_count; |
566 } |
561 } |
567 |
562 |
568 if ( $themes_total > $allowed_theme_count ) { |
563 if ( $themes_total > $allowed_theme_count ) { |
569 $has_unused_themes = true; |
564 $has_unused_themes = true; |
570 $themes_inactive = ( $themes_total - $allowed_theme_count ); |
565 $themes_inactive = ( $themes_total - $allowed_theme_count ); |
593 if ( 1 === $themes_total ) { |
588 if ( 1 === $themes_total ) { |
594 $result['description'] .= sprintf( |
589 $result['description'] .= sprintf( |
595 '<p>%s</p>', |
590 '<p>%s</p>', |
596 __( 'Your site has 1 installed theme, and it is up to date.' ) |
591 __( 'Your site has 1 installed theme, and it is up to date.' ) |
597 ); |
592 ); |
598 } else { |
593 } elseif ( $themes_total > 0 ) { |
599 $result['description'] .= sprintf( |
594 $result['description'] .= sprintf( |
600 '<p>%s</p>', |
595 '<p>%s</p>', |
601 sprintf( |
596 sprintf( |
602 /* translators: %d: The number of themes. */ |
597 /* translators: %d: The number of themes. */ |
603 _n( |
598 _n( |
605 'Your site has %d installed themes, and they are all up to date.', |
600 'Your site has %d installed themes, and they are all up to date.', |
606 $themes_total |
601 $themes_total |
607 ), |
602 ), |
608 $themes_total |
603 $themes_total |
609 ) |
604 ) |
605 ); |
|
606 } else { |
|
607 $result['description'] .= sprintf( |
|
608 '<p>%s</p>', |
|
609 __( 'Your site does not have any installed themes.' ) |
|
610 ); |
610 ); |
611 } |
611 } |
612 } |
612 } |
613 |
613 |
614 if ( $has_unused_themes && $show_unused_themes && ! is_multisite() ) { |
614 if ( $has_unused_themes && $show_unused_themes && ! is_multisite() ) { |
715 |
715 |
716 return $result; |
716 return $result; |
717 } |
717 } |
718 |
718 |
719 /** |
719 /** |
720 * Test if the supplied PHP version is supported. |
720 * Tests if the supplied PHP version is supported. |
721 * |
721 * |
722 * @since 5.2.0 |
722 * @since 5.2.0 |
723 * |
723 * |
724 * @return array The test results. |
724 * @return array The test results. |
725 */ |
725 */ |
739 ), |
739 ), |
740 'description' => sprintf( |
740 'description' => sprintf( |
741 '<p>%s</p>', |
741 '<p>%s</p>', |
742 sprintf( |
742 sprintf( |
743 /* translators: %s: The minimum recommended PHP version. */ |
743 /* translators: %s: The minimum recommended PHP version. */ |
744 __( 'PHP is the programming language used to build and maintain WordPress. Newer versions of PHP are created with increased performance in mind, so you may see a positive effect on your site’s performance. The minimum recommended version of PHP is %s.' ), |
744 __( 'PHP is one of the programming languages used to build WordPress. Newer versions of PHP receive regular security updates and may increase your site’s performance. The minimum recommended version of PHP is %s.' ), |
745 $response ? $response['recommended_version'] : '' |
745 $response ? $response['recommended_version'] : '' |
746 ) |
746 ) |
747 ), |
747 ), |
748 'actions' => sprintf( |
748 'actions' => sprintf( |
749 '<p><a href="%s" target="_blank" rel="noopener">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
749 '<p><a href="%s" target="_blank" rel="noopener">%s<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
750 esc_url( wp_get_update_php_url() ), |
750 esc_url( wp_get_update_php_url() ), |
751 __( 'Learn more about updating PHP' ), |
751 __( 'Learn more about updating PHP' ), |
752 /* translators: Accessibility text. */ |
752 /* translators: Hidden accessibility text. */ |
753 __( '(opens in a new tab)' ) |
753 __( '(opens in a new tab)' ) |
754 ), |
754 ), |
755 'test' => 'php_version', |
755 'test' => 'php_version', |
756 ); |
756 ); |
757 |
757 |
762 |
762 |
763 // The PHP version is older than the recommended version, but still receiving active support. |
763 // The PHP version is older than the recommended version, but still receiving active support. |
764 if ( $response['is_supported'] ) { |
764 if ( $response['is_supported'] ) { |
765 $result['label'] = sprintf( |
765 $result['label'] = sprintf( |
766 /* translators: %s: The server PHP version. */ |
766 /* translators: %s: The server PHP version. */ |
767 __( 'Your site is running an older version of PHP (%s)' ), |
767 __( 'Your site is running on an older version of PHP (%s)' ), |
768 PHP_VERSION |
768 PHP_VERSION |
769 ); |
769 ); |
770 $result['status'] = 'recommended'; |
770 $result['status'] = 'recommended'; |
771 |
|
772 return $result; |
|
773 } |
|
774 |
|
775 /* |
|
776 * The PHP version is still receiving security fixes, but is lower than |
|
777 * the expected minimum version that will be required by WordPress in the near future. |
|
778 */ |
|
779 if ( $response['is_secure'] && $response['is_lower_than_future_minimum'] ) { |
|
780 // The `is_secure` array key name doesn't actually imply this is a secure version of PHP. It only means it receives security updates. |
|
781 |
|
782 $result['label'] = sprintf( |
|
783 /* translators: %s: The server PHP version. */ |
|
784 __( 'Your site is running on an outdated version of PHP (%s), which soon will not be supported by WordPress.' ), |
|
785 PHP_VERSION |
|
786 ); |
|
787 |
|
788 $result['status'] = 'critical'; |
|
789 $result['badge']['label'] = __( 'Requirements' ); |
|
771 |
790 |
772 return $result; |
791 return $result; |
773 } |
792 } |
774 |
793 |
775 // The PHP version is only receiving security fixes. |
794 // The PHP version is only receiving security fixes. |
776 if ( $response['is_secure'] ) { |
795 if ( $response['is_secure'] ) { |
777 $result['label'] = sprintf( |
796 $result['label'] = sprintf( |
778 /* translators: %s: The server PHP version. */ |
797 /* translators: %s: The server PHP version. */ |
779 __( 'Your site is running an older version of PHP (%s), which should be updated' ), |
798 __( 'Your site is running on an older version of PHP (%s), which should be updated' ), |
780 PHP_VERSION |
799 PHP_VERSION |
781 ); |
800 ); |
782 $result['status'] = 'recommended'; |
801 $result['status'] = 'recommended'; |
783 |
802 |
784 return $result; |
803 return $result; |
785 } |
804 } |
786 |
805 |
787 // Anything no longer secure must be updated. |
806 // No more security updates for the PHP version, and lower than the expected minimum version required by WordPress. |
788 $result['label'] = sprintf( |
807 if ( $response['is_lower_than_future_minimum'] ) { |
789 /* translators: %s: The server PHP version. */ |
808 $message = sprintf( |
790 __( 'Your site is running an outdated version of PHP (%s), which requires an update' ), |
809 /* translators: %s: The server PHP version. */ |
791 PHP_VERSION |
810 __( 'Your site is running on an outdated version of PHP (%s), which does not receive security updates and soon will not be supported by WordPress.' ), |
792 ); |
811 PHP_VERSION |
793 $result['status'] = 'critical'; |
812 ); |
813 } else { |
|
814 // No more security updates for the PHP version, must be updated. |
|
815 $message = sprintf( |
|
816 /* translators: %s: The server PHP version. */ |
|
817 __( 'Your site is running on an outdated version of PHP (%s), which does not receive security updates. It should be updated.' ), |
|
818 PHP_VERSION |
|
819 ); |
|
820 } |
|
821 |
|
822 $result['label'] = $message; |
|
823 $result['status'] = 'critical'; |
|
824 |
|
794 $result['badge']['label'] = __( 'Security' ); |
825 $result['badge']['label'] = __( 'Security' ); |
795 |
826 |
796 return $result; |
827 return $result; |
797 } |
828 } |
798 |
829 |
799 /** |
830 /** |
800 * Check if the passed extension or function are available. |
831 * Checks if the passed extension or function are available. |
801 * |
832 * |
802 * Make the check for available PHP modules into a simple boolean operator for a cleaner test runner. |
833 * Make the check for available PHP modules into a simple boolean operator for a cleaner test runner. |
803 * |
834 * |
804 * @since 5.2.0 |
835 * @since 5.2.0 |
805 * @since 5.3.0 The `$constant_name` and `$class_name` parameters were added. |
836 * @since 5.3.0 The `$constant_name` and `$class_name` parameters were added. |
834 |
865 |
835 return true; |
866 return true; |
836 } |
867 } |
837 |
868 |
838 /** |
869 /** |
839 * Test if required PHP modules are installed on the host. |
870 * Tests if required PHP modules are installed on the host. |
840 * |
871 * |
841 * This test builds on the recommendations made by the WordPress Hosting Team |
872 * This test builds on the recommendations made by the WordPress Hosting Team |
842 * as seen at https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions |
873 * as seen at https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions |
843 * |
874 * |
844 * @since 5.2.0 |
875 * @since 5.2.0 |
861 __( 'The WordPress Hosting Team maintains a list of those modules, both recommended and required, in <a href="%1$s" %2$s>the team handbook%3$s</a>.' ), |
892 __( 'The WordPress Hosting Team maintains a list of those modules, both recommended and required, in <a href="%1$s" %2$s>the team handbook%3$s</a>.' ), |
862 /* translators: Localized team handbook, if one exists. */ |
893 /* translators: Localized team handbook, if one exists. */ |
863 esc_url( __( 'https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions' ) ), |
894 esc_url( __( 'https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions' ) ), |
864 'target="_blank" rel="noopener"', |
895 'target="_blank" rel="noopener"', |
865 sprintf( |
896 sprintf( |
866 ' <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span>', |
897 '<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span>', |
867 /* translators: Accessibility text. */ |
898 /* translators: Hidden accessibility text. */ |
868 __( '(opens in a new tab)' ) |
899 __( '(opens in a new tab)' ) |
869 ) |
900 ) |
870 ) |
901 ) |
871 ), |
902 ), |
872 'actions' => '', |
903 'actions' => '', |
969 'fallback_for' => 'zip', |
1000 'fallback_for' => 'zip', |
970 ), |
1001 ), |
971 ); |
1002 ); |
972 |
1003 |
973 /** |
1004 /** |
974 * An array representing all the modules we wish to test for. |
1005 * Filters the array representing all the modules we wish to test for. |
975 * |
1006 * |
976 * @since 5.2.0 |
1007 * @since 5.2.0 |
977 * @since 5.3.0 The `$constant` and `$class` parameters were added. |
1008 * @since 5.3.0 The `$constant` and `$class` parameters were added. |
978 * |
1009 * |
979 * @param array $modules { |
1010 * @param array $modules { |
1020 || version_compare( PHP_VERSION, $module['php_bundled_version'], '<' ) ) |
1051 || version_compare( PHP_VERSION, $module['php_bundled_version'], '<' ) ) |
1021 ) { |
1052 ) { |
1022 if ( $module['required'] ) { |
1053 if ( $module['required'] ) { |
1023 $result['status'] = 'critical'; |
1054 $result['status'] = 'critical'; |
1024 |
1055 |
1025 $class = 'error'; |
1056 $class = 'error'; |
1057 /* translators: Hidden accessibility text. */ |
|
1026 $screen_reader = __( 'Error' ); |
1058 $screen_reader = __( 'Error' ); |
1027 $message = sprintf( |
1059 $message = sprintf( |
1028 /* translators: %s: The module name. */ |
1060 /* translators: %s: The module name. */ |
1029 __( 'The required module, %s, is not installed, or has been disabled.' ), |
1061 __( 'The required module, %s, is not installed, or has been disabled.' ), |
1030 $library |
1062 $library |
1031 ); |
1063 ); |
1032 } else { |
1064 } else { |
1033 $class = 'warning'; |
1065 $class = 'warning'; |
1066 /* translators: Hidden accessibility text. */ |
|
1034 $screen_reader = __( 'Warning' ); |
1067 $screen_reader = __( 'Warning' ); |
1035 $message = sprintf( |
1068 $message = sprintf( |
1036 /* translators: %s: The module name. */ |
1069 /* translators: %s: The module name. */ |
1037 __( 'The optional module, %s, is not installed, or has been disabled.' ), |
1070 __( 'The optional module, %s, is not installed, or has been disabled.' ), |
1038 $library |
1071 $library |
1073 |
1106 |
1074 return $result; |
1107 return $result; |
1075 } |
1108 } |
1076 |
1109 |
1077 /** |
1110 /** |
1078 * Test if the PHP default timezone is set to UTC. |
1111 * Tests if the PHP default timezone is set to UTC. |
1079 * |
1112 * |
1080 * @since 5.3.1 |
1113 * @since 5.3.1 |
1081 * |
1114 * |
1082 * @return array The test results. |
1115 * @return array The test results. |
1083 */ |
1116 */ |
1114 |
1147 |
1115 return $result; |
1148 return $result; |
1116 } |
1149 } |
1117 |
1150 |
1118 /** |
1151 /** |
1119 * Test if there's an active PHP session that can affect loopback requests. |
1152 * Tests if there's an active PHP session that can affect loopback requests. |
1120 * |
1153 * |
1121 * @since 5.5.0 |
1154 * @since 5.5.0 |
1122 * |
1155 * |
1123 * @return array The test results. |
1156 * @return array The test results. |
1124 */ |
1157 */ |
1160 |
1193 |
1161 return $result; |
1194 return $result; |
1162 } |
1195 } |
1163 |
1196 |
1164 /** |
1197 /** |
1165 * Test if the SQL server is up to date. |
1198 * Tests if the SQL server is up to date. |
1166 * |
1199 * |
1167 * @since 5.2.0 |
1200 * @since 5.2.0 |
1168 * |
1201 * |
1169 * @return array The test results. |
1202 * @return array The test results. |
1170 */ |
1203 */ |
1183 'description' => sprintf( |
1216 'description' => sprintf( |
1184 '<p>%s</p>', |
1217 '<p>%s</p>', |
1185 __( 'The SQL server is a required piece of software for the database WordPress uses to store all your site’s content and settings.' ) |
1218 __( 'The SQL server is a required piece of software for the database WordPress uses to store all your site’s content and settings.' ) |
1186 ), |
1219 ), |
1187 'actions' => sprintf( |
1220 'actions' => sprintf( |
1188 '<p><a href="%s" target="_blank" rel="noopener">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1221 '<p><a href="%s" target="_blank" rel="noopener">%s<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1189 /* translators: Localized version of WordPress requirements if one exists. */ |
1222 /* translators: Localized version of WordPress requirements if one exists. */ |
1190 esc_url( __( 'https://wordpress.org/about/requirements/' ) ), |
1223 esc_url( __( 'https://wordpress.org/about/requirements/' ) ), |
1191 __( 'Learn more about what WordPress requires to run.' ), |
1224 __( 'Learn more about what WordPress requires to run.' ), |
1192 /* translators: Accessibility text. */ |
1225 /* translators: Hidden accessibility text. */ |
1193 __( '(opens in a new tab)' ) |
1226 __( '(opens in a new tab)' ) |
1194 ), |
1227 ), |
1195 'test' => 'sql_server', |
1228 'test' => 'sql_server', |
1196 ); |
1229 ); |
1197 |
1230 |
1198 $db_dropin = file_exists( WP_CONTENT_DIR . '/db.php' ); |
1231 $db_dropin = file_exists( WP_CONTENT_DIR . '/db.php' ); |
1199 |
1232 |
1200 if ( ! $this->mysql_rec_version_check ) { |
1233 if ( ! $this->is_recommended_mysql_version ) { |
1201 $result['status'] = 'recommended'; |
1234 $result['status'] = 'recommended'; |
1202 |
1235 |
1203 $result['label'] = __( 'Outdated SQL server' ); |
1236 $result['label'] = __( 'Outdated SQL server' ); |
1204 |
1237 |
1205 $result['description'] .= sprintf( |
1238 $result['description'] .= sprintf( |
1206 '<p>%s</p>', |
1239 '<p>%s</p>', |
1207 sprintf( |
1240 sprintf( |
1208 /* translators: 1: The database engine in use (MySQL or MariaDB). 2: Database server recommended version number. */ |
1241 /* translators: 1: The database engine in use (MySQL or MariaDB). 2: Database server recommended version number. */ |
1209 __( 'For optimal performance and security reasons, you should consider running %1$s version %2$s or higher. Contact your web hosting company to correct this.' ), |
1242 __( 'For optimal performance and security reasons, you should consider running %1$s version %2$s or higher. Contact your web hosting company to correct this.' ), |
1210 ( $this->is_mariadb ? 'MariaDB' : 'MySQL' ), |
1243 ( $this->is_mariadb ? 'MariaDB' : 'MySQL' ), |
1211 $this->health_check_mysql_rec_version |
1244 $this->mysql_recommended_version |
1212 ) |
1245 ) |
1213 ); |
1246 ); |
1214 } |
1247 } |
1215 |
1248 |
1216 if ( ! $this->mysql_min_version_check ) { |
1249 if ( ! $this->is_acceptable_mysql_version ) { |
1217 $result['status'] = 'critical'; |
1250 $result['status'] = 'critical'; |
1218 |
1251 |
1219 $result['label'] = __( 'Severely outdated SQL server' ); |
1252 $result['label'] = __( 'Severely outdated SQL server' ); |
1220 $result['badge']['label'] = __( 'Security' ); |
1253 $result['badge']['label'] = __( 'Security' ); |
1221 |
1254 |
1223 '<p>%s</p>', |
1256 '<p>%s</p>', |
1224 sprintf( |
1257 sprintf( |
1225 /* translators: 1: The database engine in use (MySQL or MariaDB). 2: Database server minimum version number. */ |
1258 /* translators: 1: The database engine in use (MySQL or MariaDB). 2: Database server minimum version number. */ |
1226 __( 'WordPress requires %1$s version %2$s or higher. Contact your web hosting company to correct this.' ), |
1259 __( 'WordPress requires %1$s version %2$s or higher. Contact your web hosting company to correct this.' ), |
1227 ( $this->is_mariadb ? 'MariaDB' : 'MySQL' ), |
1260 ( $this->is_mariadb ? 'MariaDB' : 'MySQL' ), |
1228 $this->health_check_mysql_required_version |
1261 $this->mysql_required_version |
1229 ) |
1262 ) |
1230 ); |
1263 ); |
1231 } |
1264 } |
1232 |
1265 |
1233 if ( $db_dropin ) { |
1266 if ( $db_dropin ) { |
1249 |
1282 |
1250 return $result; |
1283 return $result; |
1251 } |
1284 } |
1252 |
1285 |
1253 /** |
1286 /** |
1254 * Test if the database server is capable of using utf8mb4. |
1287 * Tests if the site can communicate with WordPress.org. |
1255 * |
|
1256 * @since 5.2.0 |
|
1257 * |
|
1258 * @return array The test results. |
|
1259 */ |
|
1260 public function get_test_utf8mb4_support() { |
|
1261 global $wpdb; |
|
1262 |
|
1263 if ( ! $this->mysql_server_version ) { |
|
1264 $this->prepare_sql_data(); |
|
1265 } |
|
1266 |
|
1267 $result = array( |
|
1268 'label' => __( 'UTF8MB4 is supported' ), |
|
1269 'status' => 'good', |
|
1270 'badge' => array( |
|
1271 'label' => __( 'Performance' ), |
|
1272 'color' => 'blue', |
|
1273 ), |
|
1274 'description' => sprintf( |
|
1275 '<p>%s</p>', |
|
1276 __( 'UTF8MB4 is the character set WordPress prefers for database storage because it safely supports the widest set of characters and encodings, including Emoji, enabling better support for non-English languages.' ) |
|
1277 ), |
|
1278 'actions' => '', |
|
1279 'test' => 'utf8mb4_support', |
|
1280 ); |
|
1281 |
|
1282 if ( ! $this->is_mariadb ) { |
|
1283 if ( version_compare( $this->mysql_server_version, '5.5.3', '<' ) ) { |
|
1284 $result['status'] = 'recommended'; |
|
1285 |
|
1286 $result['label'] = __( 'utf8mb4 requires a MySQL update' ); |
|
1287 |
|
1288 $result['description'] .= sprintf( |
|
1289 '<p>%s</p>', |
|
1290 sprintf( |
|
1291 /* translators: %s: Version number. */ |
|
1292 __( 'WordPress’ utf8mb4 support requires MySQL version %s or greater. Please contact your server administrator.' ), |
|
1293 '5.5.3' |
|
1294 ) |
|
1295 ); |
|
1296 } else { |
|
1297 $result['description'] .= sprintf( |
|
1298 '<p>%s</p>', |
|
1299 __( 'Your MySQL version supports utf8mb4.' ) |
|
1300 ); |
|
1301 } |
|
1302 } else { // MariaDB introduced utf8mb4 support in 5.5.0. |
|
1303 if ( version_compare( $this->mysql_server_version, '5.5.0', '<' ) ) { |
|
1304 $result['status'] = 'recommended'; |
|
1305 |
|
1306 $result['label'] = __( 'utf8mb4 requires a MariaDB update' ); |
|
1307 |
|
1308 $result['description'] .= sprintf( |
|
1309 '<p>%s</p>', |
|
1310 sprintf( |
|
1311 /* translators: %s: Version number. */ |
|
1312 __( 'WordPress’ utf8mb4 support requires MariaDB version %s or greater. Please contact your server administrator.' ), |
|
1313 '5.5.0' |
|
1314 ) |
|
1315 ); |
|
1316 } else { |
|
1317 $result['description'] .= sprintf( |
|
1318 '<p>%s</p>', |
|
1319 __( 'Your MariaDB version supports utf8mb4.' ) |
|
1320 ); |
|
1321 } |
|
1322 } |
|
1323 |
|
1324 if ( $wpdb->use_mysqli ) { |
|
1325 // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysqli_get_client_info |
|
1326 $mysql_client_version = mysqli_get_client_info(); |
|
1327 } else { |
|
1328 // phpcs:ignore WordPress.DB.RestrictedFunctions.mysql_mysql_get_client_info,PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved |
|
1329 $mysql_client_version = mysql_get_client_info(); |
|
1330 } |
|
1331 |
|
1332 /* |
|
1333 * libmysql has supported utf8mb4 since 5.5.3, same as the MySQL server. |
|
1334 * mysqlnd has supported utf8mb4 since 5.0.9. |
|
1335 */ |
|
1336 if ( false !== strpos( $mysql_client_version, 'mysqlnd' ) ) { |
|
1337 $mysql_client_version = preg_replace( '/^\D+([\d.]+).*/', '$1', $mysql_client_version ); |
|
1338 if ( version_compare( $mysql_client_version, '5.0.9', '<' ) ) { |
|
1339 $result['status'] = 'recommended'; |
|
1340 |
|
1341 $result['label'] = __( 'utf8mb4 requires a newer client library' ); |
|
1342 |
|
1343 $result['description'] .= sprintf( |
|
1344 '<p>%s</p>', |
|
1345 sprintf( |
|
1346 /* translators: 1: Name of the library, 2: Number of version. */ |
|
1347 __( 'WordPress’ utf8mb4 support requires MySQL client library (%1$s) version %2$s or newer. Please contact your server administrator.' ), |
|
1348 'mysqlnd', |
|
1349 '5.0.9' |
|
1350 ) |
|
1351 ); |
|
1352 } |
|
1353 } else { |
|
1354 if ( version_compare( $mysql_client_version, '5.5.3', '<' ) ) { |
|
1355 $result['status'] = 'recommended'; |
|
1356 |
|
1357 $result['label'] = __( 'utf8mb4 requires a newer client library' ); |
|
1358 |
|
1359 $result['description'] .= sprintf( |
|
1360 '<p>%s</p>', |
|
1361 sprintf( |
|
1362 /* translators: 1: Name of the library, 2: Number of version. */ |
|
1363 __( 'WordPress’ utf8mb4 support requires MySQL client library (%1$s) version %2$s or newer. Please contact your server administrator.' ), |
|
1364 'libmysql', |
|
1365 '5.5.3' |
|
1366 ) |
|
1367 ); |
|
1368 } |
|
1369 } |
|
1370 |
|
1371 return $result; |
|
1372 } |
|
1373 |
|
1374 /** |
|
1375 * Test if the site can communicate with WordPress.org. |
|
1376 * |
1288 * |
1377 * @since 5.2.0 |
1289 * @since 5.2.0 |
1378 * |
1290 * |
1379 * @return array The test results. |
1291 * @return array The test results. |
1380 */ |
1292 */ |
1409 |
1321 |
1410 $result['description'] .= sprintf( |
1322 $result['description'] .= sprintf( |
1411 '<p>%s</p>', |
1323 '<p>%s</p>', |
1412 sprintf( |
1324 sprintf( |
1413 '<span class="error"><span class="screen-reader-text">%s</span></span> %s', |
1325 '<span class="error"><span class="screen-reader-text">%s</span></span> %s', |
1326 /* translators: Hidden accessibility text. */ |
|
1414 __( 'Error' ), |
1327 __( 'Error' ), |
1415 sprintf( |
1328 sprintf( |
1416 /* translators: 1: The IP address WordPress.org resolves to. 2: The error returned by the lookup. */ |
1329 /* translators: 1: The IP address WordPress.org resolves to. 2: The error returned by the lookup. */ |
1417 __( 'Your site is unable to reach WordPress.org at %1$s, and returned the error: %2$s' ), |
1330 __( 'Your site is unable to reach WordPress.org at %1$s, and returned the error: %2$s' ), |
1418 gethostbyname( 'api.wordpress.org' ), |
1331 gethostbyname( 'api.wordpress.org' ), |
1420 ) |
1333 ) |
1421 ) |
1334 ) |
1422 ); |
1335 ); |
1423 |
1336 |
1424 $result['actions'] = sprintf( |
1337 $result['actions'] = sprintf( |
1425 '<p><a href="%s" target="_blank" rel="noopener">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1338 '<p><a href="%s" target="_blank" rel="noopener">%s<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1426 /* translators: Localized Support reference. */ |
1339 /* translators: Localized Support reference. */ |
1427 esc_url( __( 'https://wordpress.org/support' ) ), |
1340 esc_url( __( 'https://wordpress.org/support/forums/' ) ), |
1428 __( 'Get help resolving this issue.' ), |
1341 __( 'Get help resolving this issue.' ), |
1429 /* translators: Accessibility text. */ |
1342 /* translators: Hidden accessibility text. */ |
1430 __( '(opens in a new tab)' ) |
1343 __( '(opens in a new tab)' ) |
1431 ); |
1344 ); |
1432 } |
1345 } |
1433 |
1346 |
1434 return $result; |
1347 return $result; |
1435 } |
1348 } |
1436 |
1349 |
1437 /** |
1350 /** |
1438 * Test if debug information is enabled. |
1351 * Tests if debug information is enabled. |
1439 * |
1352 * |
1440 * When WP_DEBUG is enabled, errors and information may be disclosed to site visitors, |
1353 * When WP_DEBUG is enabled, errors and information may be disclosed to site visitors, |
1441 * or logged to a publicly accessible file. |
1354 * or logged to a publicly accessible file. |
1442 * |
1355 * |
1443 * Debugging is also frequently left enabled after looking for errors on a site, |
1356 * Debugging is also frequently left enabled after looking for errors on a site, |
1458 'description' => sprintf( |
1371 'description' => sprintf( |
1459 '<p>%s</p>', |
1372 '<p>%s</p>', |
1460 __( 'Debug mode is often enabled to gather more details about an error or site failure, but may contain sensitive information which should not be available on a publicly available website.' ) |
1373 __( 'Debug mode is often enabled to gather more details about an error or site failure, but may contain sensitive information which should not be available on a publicly available website.' ) |
1461 ), |
1374 ), |
1462 'actions' => sprintf( |
1375 'actions' => sprintf( |
1463 '<p><a href="%s" target="_blank" rel="noopener">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1376 '<p><a href="%s" target="_blank" rel="noopener">%s<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1464 /* translators: Documentation explaining debugging in WordPress. */ |
1377 /* translators: Documentation explaining debugging in WordPress. */ |
1465 esc_url( __( 'https://wordpress.org/support/article/debugging-in-wordpress/' ) ), |
1378 esc_url( __( 'https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/' ) ), |
1466 __( 'Learn more about debugging in WordPress.' ), |
1379 __( 'Learn more about debugging in WordPress.' ), |
1467 /* translators: Accessibility text. */ |
1380 /* translators: Hidden accessibility text. */ |
1468 __( '(opens in a new tab)' ) |
1381 __( '(opens in a new tab)' ) |
1469 ), |
1382 ), |
1470 'test' => 'is_in_debug_mode', |
1383 'test' => 'is_in_debug_mode', |
1471 ); |
1384 ); |
1472 |
1385 |
1473 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { |
1386 if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { |
1474 if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) { |
1387 if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) { |
1475 $result['label'] = __( 'Your site is set to log errors to a potentially public file' ); |
1388 $result['label'] = __( 'Your site is set to log errors to a potentially public file' ); |
1476 |
1389 |
1477 $result['status'] = ( 0 === strpos( ini_get( 'error_log' ), ABSPATH ) ) ? 'critical' : 'recommended'; |
1390 $result['status'] = str_starts_with( ini_get( 'error_log' ), ABSPATH ) ? 'critical' : 'recommended'; |
1478 |
1391 |
1479 $result['description'] .= sprintf( |
1392 $result['description'] .= sprintf( |
1480 '<p>%s</p>', |
1393 '<p>%s</p>', |
1481 sprintf( |
1394 sprintf( |
1482 /* translators: %s: WP_DEBUG_LOG */ |
1395 /* translators: %s: WP_DEBUG_LOG */ |
1510 |
1423 |
1511 return $result; |
1424 return $result; |
1512 } |
1425 } |
1513 |
1426 |
1514 /** |
1427 /** |
1515 * Test if your site is serving content over HTTPS. |
1428 * Tests if the site is serving content over HTTPS. |
1516 * |
1429 * |
1517 * Many sites have varying degrees of HTTPS support, the most common of which is sites that have it |
1430 * Many sites have varying degrees of HTTPS support, the most common of which is sites that have it |
1518 * enabled, but only if you visit the right site address. |
1431 * enabled, but only if you visit the right site address. |
1519 * |
1432 * |
1520 * @since 5.2.0 |
1433 * @since 5.2.0 |
1521 * @since 5.7.0 Updated to rely on {@see wp_is_using_https()} and {@see wp_is_https_supported()}. |
1434 * @since 5.7.0 Updated to rely on {@see wp_is_using_https()} and {@see wp_is_https_supported()}. |
1522 * |
1435 * |
1523 * @return array The test results. |
1436 * @return array The test results. |
1524 */ |
1437 */ |
1525 public function get_test_https_status() { |
1438 public function get_test_https_status() { |
1526 // Enforce fresh HTTPS detection results. This is normally invoked by using cron, |
1439 /* |
1527 // but for Site Health it should always rely on the latest results. |
1440 * Check HTTPS detection results. |
1528 wp_update_https_detection_errors(); |
1441 */ |
1442 $errors = wp_get_https_detection_errors(); |
|
1529 |
1443 |
1530 $default_update_url = wp_get_default_update_https_url(); |
1444 $default_update_url = wp_get_default_update_https_url(); |
1531 |
1445 |
1532 $result = array( |
1446 $result = array( |
1533 'label' => __( 'Your website is using an active HTTPS connection' ), |
1447 'label' => __( 'Your website is using an active HTTPS connection' ), |
1542 ), |
1456 ), |
1543 'actions' => sprintf( |
1457 'actions' => sprintf( |
1544 '<p><a href="%s" target="_blank" rel="noopener">%s<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1458 '<p><a href="%s" target="_blank" rel="noopener">%s<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1545 esc_url( $default_update_url ), |
1459 esc_url( $default_update_url ), |
1546 __( 'Learn more about why you should use HTTPS' ), |
1460 __( 'Learn more about why you should use HTTPS' ), |
1547 /* translators: Accessibility text. */ |
1461 /* translators: Hidden accessibility text. */ |
1548 __( '(opens in a new tab)' ) |
1462 __( '(opens in a new tab)' ) |
1549 ), |
1463 ), |
1550 'test' => 'https_status', |
1464 'test' => 'https_status', |
1551 ); |
1465 ); |
1552 |
1466 |
1553 if ( ! wp_is_using_https() ) { |
1467 if ( ! wp_is_using_https() ) { |
1554 // If the website is not using HTTPS, provide more information |
1468 /* |
1555 // about whether it is supported and how it can be enabled. |
1469 * If the website is not using HTTPS, provide more information |
1470 * about whether it is supported and how it can be enabled. |
|
1471 */ |
|
1556 $result['status'] = 'recommended'; |
1472 $result['status'] = 'recommended'; |
1557 $result['label'] = __( 'Your website does not use HTTPS' ); |
1473 $result['label'] = __( 'Your website does not use HTTPS' ); |
1558 |
1474 |
1559 if ( wp_is_site_url_using_https() ) { |
1475 if ( wp_is_site_url_using_https() ) { |
1560 if ( is_ssl() ) { |
1476 if ( is_ssl() ) { |
1624 if ( ! empty( $direct_update_url ) ) { |
1540 if ( ! empty( $direct_update_url ) ) { |
1625 $result['actions'] = sprintf( |
1541 $result['actions'] = sprintf( |
1626 '<p class="button-container"><a class="button button-primary" href="%1$s" target="_blank" rel="noopener">%2$s<span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1542 '<p class="button-container"><a class="button button-primary" href="%1$s" target="_blank" rel="noopener">%2$s<span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1627 esc_url( $direct_update_url ), |
1543 esc_url( $direct_update_url ), |
1628 __( 'Update your site to use HTTPS' ), |
1544 __( 'Update your site to use HTTPS' ), |
1629 /* translators: Accessibility text. */ |
1545 /* translators: Hidden accessibility text. */ |
1630 __( '(opens in a new tab)' ) |
1546 __( '(opens in a new tab)' ) |
1631 ); |
1547 ); |
1632 } else { |
1548 } else { |
1633 $result['actions'] = sprintf( |
1549 $result['actions'] = sprintf( |
1634 '<p class="button-container"><a class="button button-primary" href="%1$s">%2$s</a></p>', |
1550 '<p class="button-container"><a class="button button-primary" href="%1$s">%2$s</a></p>', |
1643 if ( $update_url !== $default_update_url ) { |
1559 if ( $update_url !== $default_update_url ) { |
1644 $result['description'] .= sprintf( |
1560 $result['description'] .= sprintf( |
1645 '<p><a href="%s" target="_blank" rel="noopener">%s<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1561 '<p><a href="%s" target="_blank" rel="noopener">%s<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
1646 esc_url( $update_url ), |
1562 esc_url( $update_url ), |
1647 __( 'Talk to your web host about supporting HTTPS for your website.' ), |
1563 __( 'Talk to your web host about supporting HTTPS for your website.' ), |
1648 /* translators: Accessibility text. */ |
1564 /* translators: Hidden accessibility text. */ |
1649 __( '(opens in a new tab)' ) |
1565 __( '(opens in a new tab)' ) |
1650 ); |
1566 ); |
1651 } else { |
1567 } else { |
1652 $result['description'] .= sprintf( |
1568 $result['description'] .= sprintf( |
1653 '<p>%s</p>', |
1569 '<p>%s</p>', |
1659 |
1575 |
1660 return $result; |
1576 return $result; |
1661 } |
1577 } |
1662 |
1578 |
1663 /** |
1579 /** |
1664 * Check if the HTTP API can handle SSL/TLS requests. |
1580 * Checks if the HTTP API can handle SSL/TLS requests. |
1665 * |
1581 * |
1666 * @since 5.2.0 |
1582 * @since 5.2.0 |
1667 * |
1583 * |
1668 * @return array The test results. |
1584 * @return array The test result. |
1669 */ |
1585 */ |
1670 public function get_test_ssl_support() { |
1586 public function get_test_ssl_support() { |
1671 $result = array( |
1587 $result = array( |
1672 'label' => '', |
1588 'label' => '', |
1673 'status' => '', |
1589 'status' => '', |
1702 |
1618 |
1703 return $result; |
1619 return $result; |
1704 } |
1620 } |
1705 |
1621 |
1706 /** |
1622 /** |
1707 * Test if scheduled events run as intended. |
1623 * Tests if scheduled events run as intended. |
1708 * |
1624 * |
1709 * If scheduled events are not running, this may indicate something with WP_Cron is not working |
1625 * If scheduled events are not running, this may indicate something with WP_Cron is not working |
1710 * as intended, or that there are orphaned events hanging around from older code. |
1626 * as intended, or that there are orphaned events hanging around from older code. |
1711 * |
1627 * |
1712 * @since 5.2.0 |
1628 * @since 5.2.0 |
1774 |
1690 |
1775 return $result; |
1691 return $result; |
1776 } |
1692 } |
1777 |
1693 |
1778 /** |
1694 /** |
1779 * Test if WordPress can run automated background updates. |
1695 * Tests if WordPress can run automated background updates. |
1780 * |
1696 * |
1781 * Background updates in WordPress are primarily used for minor releases and security updates. |
1697 * Background updates in WordPress are primarily used for minor releases and security updates. |
1782 * It's important to either have these working, or be aware that they are intentionally disabled |
1698 * It's important to either have these working, or be aware that they are intentionally disabled |
1783 * for whatever reason. |
1699 * for whatever reason. |
1784 * |
1700 * |
1804 |
1720 |
1805 if ( ! class_exists( 'WP_Site_Health_Auto_Updates' ) ) { |
1721 if ( ! class_exists( 'WP_Site_Health_Auto_Updates' ) ) { |
1806 require_once ABSPATH . 'wp-admin/includes/class-wp-site-health-auto-updates.php'; |
1722 require_once ABSPATH . 'wp-admin/includes/class-wp-site-health-auto-updates.php'; |
1807 } |
1723 } |
1808 |
1724 |
1809 // Run the auto-update tests in a separate class, |
1725 /* |
1810 // as there are many considerations to be made. |
1726 * Run the auto-update tests in a separate class, |
1727 * as there are many considerations to be made. |
|
1728 */ |
|
1811 $automatic_updates = new WP_Site_Health_Auto_Updates(); |
1729 $automatic_updates = new WP_Site_Health_Auto_Updates(); |
1812 $tests = $automatic_updates->run_tests(); |
1730 $tests = $automatic_updates->run_tests(); |
1813 |
1731 |
1814 $output = '<ul>'; |
1732 $output = '<ul>'; |
1815 |
1733 |
1816 foreach ( $tests as $test ) { |
1734 foreach ( $tests as $test ) { |
1735 /* translators: Hidden accessibility text. */ |
|
1817 $severity_string = __( 'Passed' ); |
1736 $severity_string = __( 'Passed' ); |
1818 |
1737 |
1819 if ( 'fail' === $test->severity ) { |
1738 if ( 'fail' === $test->severity ) { |
1820 $result['label'] = __( 'Background updates are not working as expected' ); |
1739 $result['label'] = __( 'Background updates are not working as expected' ); |
1821 |
1740 |
1822 $result['status'] = 'critical'; |
1741 $result['status'] = 'critical'; |
1823 |
1742 |
1743 /* translators: Hidden accessibility text. */ |
|
1824 $severity_string = __( 'Error' ); |
1744 $severity_string = __( 'Error' ); |
1825 } |
1745 } |
1826 |
1746 |
1827 if ( 'warning' === $test->severity && 'good' === $result['status'] ) { |
1747 if ( 'warning' === $test->severity && 'good' === $result['status'] ) { |
1828 $result['label'] = __( 'Background updates may not be working properly' ); |
1748 $result['label'] = __( 'Background updates may not be working properly' ); |
1829 |
1749 |
1830 $result['status'] = 'recommended'; |
1750 $result['status'] = 'recommended'; |
1831 |
1751 |
1752 /* translators: Hidden accessibility text. */ |
|
1832 $severity_string = __( 'Warning' ); |
1753 $severity_string = __( 'Warning' ); |
1833 } |
1754 } |
1834 |
1755 |
1835 $output .= sprintf( |
1756 $output .= sprintf( |
1836 '<li><span class="dashicons %s"><span class="screen-reader-text">%s</span></span> %s</li>', |
1757 '<li><span class="dashicons %s"><span class="screen-reader-text">%s</span></span> %s</li>', |
1848 |
1769 |
1849 return $result; |
1770 return $result; |
1850 } |
1771 } |
1851 |
1772 |
1852 /** |
1773 /** |
1853 * Test if plugin and theme auto-updates appear to be configured correctly. |
1774 * Tests if plugin and theme auto-updates appear to be configured correctly. |
1854 * |
1775 * |
1855 * @since 5.5.0 |
1776 * @since 5.5.0 |
1856 * |
1777 * |
1857 * @return array The test results. |
1778 * @return array The test results. |
1858 */ |
1779 */ |
1887 |
1808 |
1888 return $result; |
1809 return $result; |
1889 } |
1810 } |
1890 |
1811 |
1891 /** |
1812 /** |
1892 * Test if loopbacks work as expected. |
1813 * Tests available disk space for updates. |
1814 * |
|
1815 * @since 6.3.0 |
|
1816 * |
|
1817 * @return array The test results. |
|
1818 */ |
|
1819 public function get_test_available_updates_disk_space() { |
|
1820 $available_space = function_exists( 'disk_free_space' ) ? @disk_free_space( WP_CONTENT_DIR . '/upgrade/' ) : false; |
|
1821 |
|
1822 $result = array( |
|
1823 'label' => __( 'Disk space available to safely perform updates' ), |
|
1824 'status' => 'good', |
|
1825 'badge' => array( |
|
1826 'label' => __( 'Security' ), |
|
1827 'color' => 'blue', |
|
1828 ), |
|
1829 'description' => sprintf( |
|
1830 /* translators: %s: Available disk space in MB or GB. */ |
|
1831 '<p>' . __( '%s available disk space was detected, update routines can be performed safely.' ) . '</p>', |
|
1832 size_format( $available_space ) |
|
1833 ), |
|
1834 'actions' => '', |
|
1835 'test' => 'available_updates_disk_space', |
|
1836 ); |
|
1837 |
|
1838 if ( false === $available_space ) { |
|
1839 $result['description'] = __( 'Could not determine available disk space for updates.' ); |
|
1840 $result['status'] = 'recommended'; |
|
1841 } elseif ( $available_space < 20 * MB_IN_BYTES ) { |
|
1842 $result['description'] = sprintf( |
|
1843 /* translators: %s: Available disk space in MB or GB. */ |
|
1844 __( 'Available disk space is critically low, less than %s available. Proceed with caution, updates may fail.' ), |
|
1845 size_format( 20 * MB_IN_BYTES ) |
|
1846 ); |
|
1847 $result['status'] = 'critical'; |
|
1848 } elseif ( $available_space < 100 * MB_IN_BYTES ) { |
|
1849 $result['description'] = sprintf( |
|
1850 /* translators: %s: Available disk space in MB or GB. */ |
|
1851 __( 'Available disk space is low, less than %s available.' ), |
|
1852 size_format( 100 * MB_IN_BYTES ) |
|
1853 ); |
|
1854 $result['status'] = 'recommended'; |
|
1855 } |
|
1856 |
|
1857 return $result; |
|
1858 } |
|
1859 |
|
1860 /** |
|
1861 * Tests if plugin and theme temporary backup directories are writable or can be created. |
|
1862 * |
|
1863 * @since 6.3.0 |
|
1864 * |
|
1865 * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. |
|
1866 * |
|
1867 * @return array The test results. |
|
1868 */ |
|
1869 public function get_test_update_temp_backup_writable() { |
|
1870 global $wp_filesystem; |
|
1871 |
|
1872 $result = array( |
|
1873 'label' => __( 'Plugin and theme temporary backup directory is writable' ), |
|
1874 'status' => 'good', |
|
1875 'badge' => array( |
|
1876 'label' => __( 'Security' ), |
|
1877 'color' => 'blue', |
|
1878 ), |
|
1879 'description' => sprintf( |
|
1880 /* translators: %s: wp-content/upgrade-temp-backup */ |
|
1881 '<p>' . __( 'The %s directory used to improve the stability of plugin and theme updates is writable.' ) . '</p>', |
|
1882 '<code>wp-content/upgrade-temp-backup</code>' |
|
1883 ), |
|
1884 'actions' => '', |
|
1885 'test' => 'update_temp_backup_writable', |
|
1886 ); |
|
1887 |
|
1888 if ( ! function_exists( 'WP_Filesystem' ) ) { |
|
1889 require_once ABSPATH . '/wp-admin/includes/file.php'; |
|
1890 } |
|
1891 |
|
1892 ob_start(); |
|
1893 $credentials = request_filesystem_credentials( '' ); |
|
1894 ob_end_clean(); |
|
1895 |
|
1896 if ( false === $credentials || ! WP_Filesystem( $credentials ) ) { |
|
1897 $result['status'] = 'recommended'; |
|
1898 $result['label'] = __( 'Could not access filesystem' ); |
|
1899 $result['description'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' ); |
|
1900 return $result; |
|
1901 } |
|
1902 |
|
1903 $wp_content = $wp_filesystem->wp_content_dir(); |
|
1904 |
|
1905 if ( ! $wp_content ) { |
|
1906 $result['status'] = 'critical'; |
|
1907 $result['label'] = __( 'Unable to locate WordPress content directory' ); |
|
1908 $result['description'] = sprintf( |
|
1909 /* translators: %s: wp-content */ |
|
1910 '<p>' . __( 'The %s directory cannot be located.' ) . '</p>', |
|
1911 '<code>wp-content</code>' |
|
1912 ); |
|
1913 return $result; |
|
1914 } |
|
1915 |
|
1916 $upgrade_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade" ); |
|
1917 $upgrade_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade" ); |
|
1918 $backup_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade-temp-backup" ); |
|
1919 $backup_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade-temp-backup" ); |
|
1920 |
|
1921 $plugins_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade-temp-backup/plugins" ); |
|
1922 $plugins_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade-temp-backup/plugins" ); |
|
1923 $themes_dir_exists = $wp_filesystem->is_dir( "$wp_content/upgrade-temp-backup/themes" ); |
|
1924 $themes_dir_is_writable = $wp_filesystem->is_writable( "$wp_content/upgrade-temp-backup/themes" ); |
|
1925 |
|
1926 if ( $plugins_dir_exists && ! $plugins_dir_is_writable && $themes_dir_exists && ! $themes_dir_is_writable ) { |
|
1927 $result['status'] = 'critical'; |
|
1928 $result['label'] = __( 'Plugin and theme temporary backup directories exist but are not writable' ); |
|
1929 $result['description'] = sprintf( |
|
1930 /* translators: 1: wp-content/upgrade-temp-backup/plugins, 2: wp-content/upgrade-temp-backup/themes. */ |
|
1931 '<p>' . __( 'The %1$s and %2$s directories exist but are not writable. These directories are used to improve the stability of plugin updates. Please make sure the server has write permissions to these directories.' ) . '</p>', |
|
1932 '<code>wp-content/upgrade-temp-backup/plugins</code>', |
|
1933 '<code>wp-content/upgrade-temp-backup/themes</code>' |
|
1934 ); |
|
1935 return $result; |
|
1936 } |
|
1937 |
|
1938 if ( $plugins_dir_exists && ! $plugins_dir_is_writable ) { |
|
1939 $result['status'] = 'critical'; |
|
1940 $result['label'] = __( 'Plugin temporary backup directory exists but is not writable' ); |
|
1941 $result['description'] = sprintf( |
|
1942 /* translators: %s: wp-content/upgrade-temp-backup/plugins */ |
|
1943 '<p>' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of plugin updates. Please make sure the server has write permissions to this directory.' ) . '</p>', |
|
1944 '<code>wp-content/upgrade-temp-backup/plugins</code>' |
|
1945 ); |
|
1946 return $result; |
|
1947 } |
|
1948 |
|
1949 if ( $themes_dir_exists && ! $themes_dir_is_writable ) { |
|
1950 $result['status'] = 'critical'; |
|
1951 $result['label'] = __( 'Theme temporary backup directory exists but is not writable' ); |
|
1952 $result['description'] = sprintf( |
|
1953 /* translators: %s: wp-content/upgrade-temp-backup/themes */ |
|
1954 '<p>' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of theme updates. Please make sure the server has write permissions to this directory.' ) . '</p>', |
|
1955 '<code>wp-content/upgrade-temp-backup/themes</code>' |
|
1956 ); |
|
1957 return $result; |
|
1958 } |
|
1959 |
|
1960 if ( ( ! $plugins_dir_exists || ! $themes_dir_exists ) && $backup_dir_exists && ! $backup_dir_is_writable ) { |
|
1961 $result['status'] = 'critical'; |
|
1962 $result['label'] = __( 'The temporary backup directory exists but is not writable' ); |
|
1963 $result['description'] = sprintf( |
|
1964 /* translators: %s: wp-content/upgrade-temp-backup */ |
|
1965 '<p>' . __( 'The %s directory exists but is not writable. This directory is used to improve the stability of plugin and theme updates. Please make sure the server has write permissions to this directory.' ) . '</p>', |
|
1966 '<code>wp-content/upgrade-temp-backup</code>' |
|
1967 ); |
|
1968 return $result; |
|
1969 } |
|
1970 |
|
1971 if ( ! $backup_dir_exists && $upgrade_dir_exists && ! $upgrade_dir_is_writable ) { |
|
1972 $result['status'] = 'critical'; |
|
1973 $result['label'] = __( 'The upgrade directory exists but is not writable' ); |
|
1974 $result['description'] = sprintf( |
|
1975 /* translators: %s: wp-content/upgrade */ |
|
1976 '<p>' . __( 'The %s directory exists but is not writable. This directory is used for plugin and theme updates. Please make sure the server has write permissions to this directory.' ) . '</p>', |
|
1977 '<code>wp-content/upgrade</code>' |
|
1978 ); |
|
1979 return $result; |
|
1980 } |
|
1981 |
|
1982 if ( ! $upgrade_dir_exists && ! $wp_filesystem->is_writable( $wp_content ) ) { |
|
1983 $result['status'] = 'critical'; |
|
1984 $result['label'] = __( 'The upgrade directory cannot be created' ); |
|
1985 $result['description'] = sprintf( |
|
1986 /* translators: 1: wp-content/upgrade, 2: wp-content. */ |
|
1987 '<p>' . __( 'The %1$s directory does not exist, and the server does not have write permissions in %2$s to create it. This directory is used for plugin and theme updates. Please make sure the server has write permissions in %2$s.' ) . '</p>', |
|
1988 '<code>wp-content/upgrade</code>', |
|
1989 '<code>wp-content</code>' |
|
1990 ); |
|
1991 return $result; |
|
1992 } |
|
1993 |
|
1994 return $result; |
|
1995 } |
|
1996 |
|
1997 /** |
|
1998 * Tests if loopbacks work as expected. |
|
1893 * |
1999 * |
1894 * A loopback is when WordPress queries itself, for example to start a new WP_Cron instance, |
2000 * A loopback is when WordPress queries itself, for example to start a new WP_Cron instance, |
1895 * or when editing a plugin or theme. This has shown itself to be a recurring issue, |
2001 * or when editing a plugin or theme. This has shown itself to be a recurring issue, |
1896 * as code can very easily break this interaction. |
2002 * as code can very easily break this interaction. |
1897 * |
2003 * |
1930 |
2036 |
1931 return $result; |
2037 return $result; |
1932 } |
2038 } |
1933 |
2039 |
1934 /** |
2040 /** |
1935 * Test if HTTP requests are blocked. |
2041 * Tests if HTTP requests are blocked. |
1936 * |
2042 * |
1937 * It's possible to block all outgoing communication (with the possibility of allowing certain |
2043 * It's possible to block all outgoing communication (with the possibility of allowing certain |
1938 * hosts) via the HTTP API. This may create problems for users as many features are running as |
2044 * hosts) via the HTTP API. This may create problems for users as many features are running as |
1939 * services these days. |
2045 * services these days. |
1940 * |
2046 * |
2002 |
2108 |
2003 return $result; |
2109 return $result; |
2004 } |
2110 } |
2005 |
2111 |
2006 /** |
2112 /** |
2007 * Test if the REST API is accessible. |
2113 * Tests if the REST API is accessible. |
2008 * |
2114 * |
2009 * Various security measures may block the REST API from working, or it may have been disabled in general. |
2115 * Various security measures may block the REST API from working, or it may have been disabled in general. |
2010 * This is required for the new block editor to work, so we explicitly test for this. |
2116 * This is required for the new block editor to work, so we explicitly test for this. |
2011 * |
2117 * |
2012 * @since 5.2.0 |
2118 * @since 5.2.0 |
2021 'label' => __( 'Performance' ), |
2127 'label' => __( 'Performance' ), |
2022 'color' => 'blue', |
2128 'color' => 'blue', |
2023 ), |
2129 ), |
2024 'description' => sprintf( |
2130 'description' => sprintf( |
2025 '<p>%s</p>', |
2131 '<p>%s</p>', |
2026 __( 'The REST API is one way WordPress, and other applications, communicate with the server. One example is the block editor screen, which relies on this to display, and save, your posts and pages.' ) |
2132 __( 'The REST API is one way that WordPress and other applications communicate with the server. For example, the block editor screen relies on the REST API to display and save your posts and pages.' ) |
2027 ), |
2133 ), |
2028 'actions' => '', |
2134 'actions' => '', |
2029 'test' => 'rest_availability', |
2135 'test' => 'rest_availability', |
2030 ); |
2136 ); |
2031 |
2137 |
2032 $cookies = wp_unslash( $_COOKIE ); |
2138 $cookies = wp_unslash( $_COOKIE ); |
2033 $timeout = 10; |
2139 $timeout = 10; // 10 seconds. |
2034 $headers = array( |
2140 $headers = array( |
2035 'Cache-Control' => 'no-cache', |
2141 'Cache-Control' => 'no-cache', |
2036 'X-WP-Nonce' => wp_create_nonce( 'wp_rest' ), |
2142 'X-WP-Nonce' => wp_create_nonce( 'wp_rest' ), |
2037 ); |
2143 ); |
2038 /** This filter is documented in wp-includes/class-wp-http-streams.php */ |
2144 /** This filter is documented in wp-includes/class-wp-http-streams.php */ |
2059 $result['status'] = 'critical'; |
2165 $result['status'] = 'critical'; |
2060 |
2166 |
2061 $result['label'] = __( 'The REST API encountered an error' ); |
2167 $result['label'] = __( 'The REST API encountered an error' ); |
2062 |
2168 |
2063 $result['description'] .= sprintf( |
2169 $result['description'] .= sprintf( |
2064 '<p>%s</p>', |
2170 '<p>%s</p><p>%s<br>%s</p>', |
2171 __( 'When testing the REST API, an error was encountered:' ), |
|
2065 sprintf( |
2172 sprintf( |
2066 '%s<br>%s', |
2173 // translators: %s: The REST API URL. |
2067 __( 'The REST API request failed due to an error.' ), |
2174 __( 'REST API Endpoint: %s' ), |
2068 sprintf( |
2175 $url |
2069 /* translators: 1: The WordPress error message. 2: The WordPress error code. */ |
2176 ), |
2070 __( 'Error: %1$s (%2$s)' ), |
2177 sprintf( |
2071 $r->get_error_message(), |
2178 // translators: 1: The WordPress error code. 2: The WordPress error message. |
2072 $r->get_error_code() |
2179 __( 'REST API Response: (%1$s) %2$s' ), |
2073 ) |
2180 $r->get_error_code(), |
2181 $r->get_error_message() |
|
2074 ) |
2182 ) |
2075 ); |
2183 ); |
2076 } elseif ( 200 !== wp_remote_retrieve_response_code( $r ) ) { |
2184 } elseif ( 200 !== wp_remote_retrieve_response_code( $r ) ) { |
2077 $result['status'] = 'recommended'; |
2185 $result['status'] = 'recommended'; |
2078 |
2186 |
2079 $result['label'] = __( 'The REST API encountered an unexpected result' ); |
2187 $result['label'] = __( 'The REST API encountered an unexpected result' ); |
2080 |
2188 |
2081 $result['description'] .= sprintf( |
2189 $result['description'] .= sprintf( |
2082 '<p>%s</p>', |
2190 '<p>%s</p><p>%s<br>%s</p>', |
2191 __( 'When testing the REST API, an unexpected result was returned:' ), |
|
2083 sprintf( |
2192 sprintf( |
2084 /* translators: 1: The HTTP error code. 2: The HTTP error message. */ |
2193 // translators: %s: The REST API URL. |
2085 __( 'The REST API call gave the following unexpected result: (%1$d) %2$s.' ), |
2194 __( 'REST API Endpoint: %s' ), |
2195 $url |
|
2196 ), |
|
2197 sprintf( |
|
2198 // translators: 1: The WordPress error code. 2: The HTTP status code error message. |
|
2199 __( 'REST API Response: (%1$s) %2$s' ), |
|
2086 wp_remote_retrieve_response_code( $r ), |
2200 wp_remote_retrieve_response_code( $r ), |
2087 esc_html( wp_remote_retrieve_body( $r ) ) |
2201 wp_remote_retrieve_response_message( $r ) |
2088 ) |
2202 ) |
2089 ); |
2203 ); |
2090 } else { |
2204 } else { |
2091 $json = json_decode( wp_remote_retrieve_body( $r ), true ); |
2205 $json = json_decode( wp_remote_retrieve_body( $r ), true ); |
2092 |
2206 |
2108 |
2222 |
2109 return $result; |
2223 return $result; |
2110 } |
2224 } |
2111 |
2225 |
2112 /** |
2226 /** |
2113 * Test if 'file_uploads' directive in PHP.ini is turned off. |
2227 * Tests if 'file_uploads' directive in PHP.ini is turned off. |
2114 * |
2228 * |
2115 * @since 5.5.0 |
2229 * @since 5.5.0 |
2116 * |
2230 * |
2117 * @return array The test results. |
2231 * @return array The test results. |
2118 */ |
2232 */ |
2216 'label' => __( 'Security' ), |
2330 'label' => __( 'Security' ), |
2217 'color' => 'blue', |
2331 'color' => 'blue', |
2218 ), |
2332 ), |
2219 'description' => sprintf( |
2333 'description' => sprintf( |
2220 '<p>%s</p>', |
2334 '<p>%s</p>', |
2221 __( 'The Authorization header comes from the third-party applications you approve. Without it, those apps cannot connect to your site.' ) |
2335 __( 'The Authorization header is used by third-party applications you have approved for this site. Without this header, those apps cannot connect to your site.' ) |
2222 ), |
2336 ), |
2223 'actions' => '', |
2337 'actions' => '', |
2224 'test' => 'authorization_header', |
2338 'test' => 'authorization_header', |
2225 ); |
2339 ); |
2226 |
2340 |
2230 $result['label'] = __( 'The authorization header is invalid' ); |
2344 $result['label'] = __( 'The authorization header is invalid' ); |
2231 } else { |
2345 } else { |
2232 return $result; |
2346 return $result; |
2233 } |
2347 } |
2234 |
2348 |
2235 $result['status'] = 'recommended'; |
2349 $result['status'] = 'recommended'; |
2350 $result['description'] .= sprintf( |
|
2351 '<p>%s</p>', |
|
2352 __( 'If you are still seeing this warning after having tried the actions below, you may need to contact your hosting provider for further assistance.' ) |
|
2353 ); |
|
2236 |
2354 |
2237 if ( ! function_exists( 'got_mod_rewrite' ) ) { |
2355 if ( ! function_exists( 'got_mod_rewrite' ) ) { |
2238 require_once ABSPATH . 'wp-admin/includes/misc.php'; |
2356 require_once ABSPATH . 'wp-admin/includes/misc.php'; |
2239 } |
2357 } |
2240 |
2358 |
2244 esc_url( admin_url( 'options-permalink.php' ) ), |
2362 esc_url( admin_url( 'options-permalink.php' ) ), |
2245 __( 'Flush permalinks' ) |
2363 __( 'Flush permalinks' ) |
2246 ); |
2364 ); |
2247 } else { |
2365 } else { |
2248 $result['actions'] .= sprintf( |
2366 $result['actions'] .= sprintf( |
2249 '<p><a href="%s" target="_blank" rel="noopener">%s <span class="screen-reader-text">%s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
2367 '<p><a href="%s" target="_blank" rel="noopener">%s<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
2250 __( 'https://developer.wordpress.org/rest-api/frequently-asked-questions/#why-is-authentication-not-working' ), |
2368 __( 'https://developer.wordpress.org/rest-api/frequently-asked-questions/#why-is-authentication-not-working' ), |
2251 __( 'Learn how to configure the Authorization header.' ), |
2369 __( 'Learn how to configure the Authorization header.' ), |
2252 /* translators: Accessibility text. */ |
2370 /* translators: Hidden accessibility text. */ |
2253 __( '(opens in a new tab)' ) |
2371 __( '(opens in a new tab)' ) |
2254 ); |
2372 ); |
2255 } |
2373 } |
2256 |
2374 |
2257 return $result; |
2375 return $result; |
2258 } |
2376 } |
2259 |
2377 |
2260 /** |
2378 /** |
2261 * Return a set of tests that belong to the site status page. |
2379 * Tests if a full page cache is available. |
2380 * |
|
2381 * @since 6.1.0 |
|
2382 * |
|
2383 * @return array The test result. |
|
2384 */ |
|
2385 public function get_test_page_cache() { |
|
2386 $description = '<p>' . __( 'Page cache enhances the speed and performance of your site by saving and serving static pages instead of calling for a page every time a user visits.' ) . '</p>'; |
|
2387 $description .= '<p>' . __( 'Page cache is detected by looking for an active page cache plugin as well as making three requests to the homepage and looking for one or more of the following HTTP client caching response headers:' ) . '</p>'; |
|
2388 $description .= '<code>' . implode( '</code>, <code>', array_keys( $this->get_page_cache_headers() ) ) . '.</code>'; |
|
2389 |
|
2390 $result = array( |
|
2391 'badge' => array( |
|
2392 'label' => __( 'Performance' ), |
|
2393 'color' => 'blue', |
|
2394 ), |
|
2395 'description' => wp_kses_post( $description ), |
|
2396 'test' => 'page_cache', |
|
2397 'status' => 'good', |
|
2398 'label' => '', |
|
2399 'actions' => sprintf( |
|
2400 '<p><a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s<span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
|
2401 __( 'https://developer.wordpress.org/advanced-administration/performance/optimization/#caching' ), |
|
2402 __( 'Learn more about page cache' ), |
|
2403 /* translators: Hidden accessibility text. */ |
|
2404 __( '(opens in a new tab)' ) |
|
2405 ), |
|
2406 ); |
|
2407 |
|
2408 $page_cache_detail = $this->get_page_cache_detail(); |
|
2409 |
|
2410 if ( is_wp_error( $page_cache_detail ) ) { |
|
2411 $result['label'] = __( 'Unable to detect the presence of page cache' ); |
|
2412 $result['status'] = 'recommended'; |
|
2413 $error_info = sprintf( |
|
2414 /* translators: 1: Error message, 2: Error code. */ |
|
2415 __( 'Unable to detect page cache due to possible loopback request problem. Please verify that the loopback request test is passing. Error: %1$s (Code: %2$s)' ), |
|
2416 $page_cache_detail->get_error_message(), |
|
2417 $page_cache_detail->get_error_code() |
|
2418 ); |
|
2419 $result['description'] = wp_kses_post( "<p>$error_info</p>" ) . $result['description']; |
|
2420 return $result; |
|
2421 } |
|
2422 |
|
2423 $result['status'] = $page_cache_detail['status']; |
|
2424 |
|
2425 switch ( $page_cache_detail['status'] ) { |
|
2426 case 'recommended': |
|
2427 $result['label'] = __( 'Page cache is not detected but the server response time is OK' ); |
|
2428 break; |
|
2429 case 'good': |
|
2430 $result['label'] = __( 'Page cache is detected and the server response time is good' ); |
|
2431 break; |
|
2432 default: |
|
2433 if ( empty( $page_cache_detail['headers'] ) && ! $page_cache_detail['advanced_cache_present'] ) { |
|
2434 $result['label'] = __( 'Page cache is not detected and the server response time is slow' ); |
|
2435 } else { |
|
2436 $result['label'] = __( 'Page cache is detected but the server response time is still slow' ); |
|
2437 } |
|
2438 } |
|
2439 |
|
2440 $page_cache_test_summary = array(); |
|
2441 |
|
2442 if ( empty( $page_cache_detail['response_time'] ) ) { |
|
2443 $page_cache_test_summary[] = '<span class="dashicons dashicons-dismiss"></span> ' . __( 'Server response time could not be determined. Verify that loopback requests are working.' ); |
|
2444 } else { |
|
2445 |
|
2446 $threshold = $this->get_good_response_time_threshold(); |
|
2447 if ( $page_cache_detail['response_time'] < $threshold ) { |
|
2448 $page_cache_test_summary[] = '<span class="dashicons dashicons-yes-alt"></span> ' . sprintf( |
|
2449 /* translators: 1: The response time in milliseconds, 2: The recommended threshold in milliseconds. */ |
|
2450 __( 'Median server response time was %1$s milliseconds. This is less than the recommended %2$s milliseconds threshold.' ), |
|
2451 number_format_i18n( $page_cache_detail['response_time'] ), |
|
2452 number_format_i18n( $threshold ) |
|
2453 ); |
|
2454 } else { |
|
2455 $page_cache_test_summary[] = '<span class="dashicons dashicons-warning"></span> ' . sprintf( |
|
2456 /* translators: 1: The response time in milliseconds, 2: The recommended threshold in milliseconds. */ |
|
2457 __( 'Median server response time was %1$s milliseconds. It should be less than the recommended %2$s milliseconds threshold.' ), |
|
2458 number_format_i18n( $page_cache_detail['response_time'] ), |
|
2459 number_format_i18n( $threshold ) |
|
2460 ); |
|
2461 } |
|
2462 |
|
2463 if ( empty( $page_cache_detail['headers'] ) ) { |
|
2464 $page_cache_test_summary[] = '<span class="dashicons dashicons-warning"></span> ' . __( 'No client caching response headers were detected.' ); |
|
2465 } else { |
|
2466 $headers_summary = '<span class="dashicons dashicons-yes-alt"></span>'; |
|
2467 $headers_summary .= ' ' . sprintf( |
|
2468 /* translators: %d: Number of caching headers. */ |
|
2469 _n( |
|
2470 'There was %d client caching response header detected:', |
|
2471 'There were %d client caching response headers detected:', |
|
2472 count( $page_cache_detail['headers'] ) |
|
2473 ), |
|
2474 count( $page_cache_detail['headers'] ) |
|
2475 ); |
|
2476 $headers_summary .= ' <code>' . implode( '</code>, <code>', $page_cache_detail['headers'] ) . '</code>.'; |
|
2477 $page_cache_test_summary[] = $headers_summary; |
|
2478 } |
|
2479 } |
|
2480 |
|
2481 if ( $page_cache_detail['advanced_cache_present'] ) { |
|
2482 $page_cache_test_summary[] = '<span class="dashicons dashicons-yes-alt"></span> ' . __( 'A page cache plugin was detected.' ); |
|
2483 } elseif ( ! ( is_array( $page_cache_detail ) && ! empty( $page_cache_detail['headers'] ) ) ) { |
|
2484 // Note: This message is not shown if client caching response headers were present since an external caching layer may be employed. |
|
2485 $page_cache_test_summary[] = '<span class="dashicons dashicons-warning"></span> ' . __( 'A page cache plugin was not detected.' ); |
|
2486 } |
|
2487 |
|
2488 $result['description'] .= '<ul><li>' . implode( '</li><li>', $page_cache_test_summary ) . '</li></ul>'; |
|
2489 return $result; |
|
2490 } |
|
2491 |
|
2492 /** |
|
2493 * Tests if the site uses persistent object cache and recommends to use it if not. |
|
2494 * |
|
2495 * @since 6.1.0 |
|
2496 * |
|
2497 * @return array The test result. |
|
2498 */ |
|
2499 public function get_test_persistent_object_cache() { |
|
2500 /** |
|
2501 * Filters the action URL for the persistent object cache health check. |
|
2502 * |
|
2503 * @since 6.1.0 |
|
2504 * |
|
2505 * @param string $action_url Learn more link for persistent object cache health check. |
|
2506 */ |
|
2507 $action_url = apply_filters( |
|
2508 'site_status_persistent_object_cache_url', |
|
2509 /* translators: Localized Support reference. */ |
|
2510 __( 'https://developer.wordpress.org/advanced-administration/performance/optimization/#persistent-object-cache' ) |
|
2511 ); |
|
2512 |
|
2513 $result = array( |
|
2514 'test' => 'persistent_object_cache', |
|
2515 'status' => 'good', |
|
2516 'badge' => array( |
|
2517 'label' => __( 'Performance' ), |
|
2518 'color' => 'blue', |
|
2519 ), |
|
2520 'label' => __( 'A persistent object cache is being used' ), |
|
2521 'description' => sprintf( |
|
2522 '<p>%s</p>', |
|
2523 __( 'A persistent object cache makes your site’s database more efficient, resulting in faster load times because WordPress can retrieve your site’s content and settings much more quickly.' ) |
|
2524 ), |
|
2525 'actions' => sprintf( |
|
2526 '<p><a href="%s" target="_blank" rel="noopener">%s<span class="screen-reader-text"> %s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>', |
|
2527 esc_url( $action_url ), |
|
2528 __( 'Learn more about persistent object caching.' ), |
|
2529 /* translators: Hidden accessibility text. */ |
|
2530 __( '(opens in a new tab)' ) |
|
2531 ), |
|
2532 ); |
|
2533 |
|
2534 if ( wp_using_ext_object_cache() ) { |
|
2535 return $result; |
|
2536 } |
|
2537 |
|
2538 if ( ! $this->should_suggest_persistent_object_cache() ) { |
|
2539 $result['label'] = __( 'A persistent object cache is not required' ); |
|
2540 |
|
2541 return $result; |
|
2542 } |
|
2543 |
|
2544 $available_services = $this->available_object_cache_services(); |
|
2545 |
|
2546 $notes = __( 'Your hosting provider can tell you if a persistent object cache can be enabled on your site.' ); |
|
2547 |
|
2548 if ( ! empty( $available_services ) ) { |
|
2549 $notes .= ' ' . sprintf( |
|
2550 /* translators: Available object caching services. */ |
|
2551 __( 'Your host appears to support the following object caching services: %s.' ), |
|
2552 implode( ', ', $available_services ) |
|
2553 ); |
|
2554 } |
|
2555 |
|
2556 /** |
|
2557 * Filters the second paragraph of the health check's description |
|
2558 * when suggesting the use of a persistent object cache. |
|
2559 * |
|
2560 * Hosts may want to replace the notes to recommend their preferred object caching solution. |
|
2561 * |
|
2562 * Plugin authors may want to append notes (not replace) on why object caching is recommended for their plugin. |
|
2563 * |
|
2564 * @since 6.1.0 |
|
2565 * |
|
2566 * @param string $notes The notes appended to the health check description. |
|
2567 * @param string[] $available_services The list of available persistent object cache services. |
|
2568 */ |
|
2569 $notes = apply_filters( 'site_status_persistent_object_cache_notes', $notes, $available_services ); |
|
2570 |
|
2571 $result['status'] = 'recommended'; |
|
2572 $result['label'] = __( 'You should use a persistent object cache' ); |
|
2573 $result['description'] .= sprintf( |
|
2574 '<p>%s</p>', |
|
2575 wp_kses( |
|
2576 $notes, |
|
2577 array( |
|
2578 'a' => array( 'href' => true ), |
|
2579 'code' => true, |
|
2580 'em' => true, |
|
2581 'strong' => true, |
|
2582 ) |
|
2583 ) |
|
2584 ); |
|
2585 |
|
2586 return $result; |
|
2587 } |
|
2588 |
|
2589 /** |
|
2590 * Calculates total amount of autoloaded data. |
|
2591 * |
|
2592 * @since 6.6.0 |
|
2593 * |
|
2594 * @return int Autoloaded data in bytes. |
|
2595 */ |
|
2596 public function get_autoloaded_options_size() { |
|
2597 $alloptions = wp_load_alloptions(); |
|
2598 |
|
2599 $total_length = 0; |
|
2600 |
|
2601 foreach ( $alloptions as $option_value ) { |
|
2602 if ( is_array( $option_value ) || is_object( $option_value ) ) { |
|
2603 $option_value = maybe_serialize( $option_value ); |
|
2604 } |
|
2605 $total_length += strlen( (string) $option_value ); |
|
2606 } |
|
2607 |
|
2608 return $total_length; |
|
2609 } |
|
2610 |
|
2611 /** |
|
2612 * Tests the number of autoloaded options. |
|
2613 * |
|
2614 * @since 6.6.0 |
|
2615 * |
|
2616 * @return array The test results. |
|
2617 */ |
|
2618 public function get_test_autoloaded_options() { |
|
2619 $autoloaded_options_size = $this->get_autoloaded_options_size(); |
|
2620 $autoloaded_options_count = count( wp_load_alloptions() ); |
|
2621 |
|
2622 $base_description = __( 'Autoloaded options are configuration settings for plugins and themes that are automatically loaded with every page load in WordPress. Having too many autoloaded options can slow down your site.' ); |
|
2623 |
|
2624 $result = array( |
|
2625 'label' => __( 'Autoloaded options are acceptable' ), |
|
2626 'status' => 'good', |
|
2627 'badge' => array( |
|
2628 'label' => __( 'Performance' ), |
|
2629 'color' => 'blue', |
|
2630 ), |
|
2631 'description' => sprintf( |
|
2632 /* translators: 1: Number of autoloaded options, 2: Autoloaded options size. */ |
|
2633 '<p>' . esc_html( $base_description ) . ' ' . __( 'Your site has %1$s autoloaded options (size: %2$s) in the options table, which is acceptable.' ) . '</p>', |
|
2634 $autoloaded_options_count, |
|
2635 size_format( $autoloaded_options_size ) |
|
2636 ), |
|
2637 'actions' => '', |
|
2638 'test' => 'autoloaded_options', |
|
2639 ); |
|
2640 |
|
2641 /** |
|
2642 * Filters max bytes threshold to trigger warning in Site Health. |
|
2643 * |
|
2644 * @since 6.6.0 |
|
2645 * |
|
2646 * @param int $limit Autoloaded options threshold size. Default 800000. |
|
2647 */ |
|
2648 $limit = apply_filters( 'site_status_autoloaded_options_size_limit', 800000 ); |
|
2649 |
|
2650 if ( $autoloaded_options_size < $limit ) { |
|
2651 return $result; |
|
2652 } |
|
2653 |
|
2654 $result['status'] = 'critical'; |
|
2655 $result['label'] = __( 'Autoloaded options could affect performance' ); |
|
2656 $result['description'] = sprintf( |
|
2657 /* translators: 1: Number of autoloaded options, 2: Autoloaded options size. */ |
|
2658 '<p>' . esc_html( $base_description ) . ' ' . __( 'Your site has %1$s autoloaded options (size: %2$s) in the options table, which could cause your site to be slow. You can review the options being autoloaded in your database and remove any options that are no longer needed by your site.' ) . '</p>', |
|
2659 $autoloaded_options_count, |
|
2660 size_format( $autoloaded_options_size ) |
|
2661 ); |
|
2662 |
|
2663 /** |
|
2664 * Filters description to be shown on Site Health warning when threshold is met. |
|
2665 * |
|
2666 * @since 6.6.0 |
|
2667 * |
|
2668 * @param string $description Description message when autoloaded options bigger than threshold. |
|
2669 */ |
|
2670 $result['description'] = apply_filters( 'site_status_autoloaded_options_limit_description', $result['description'] ); |
|
2671 |
|
2672 $result['actions'] = sprintf( |
|
2673 /* translators: 1: HelpHub URL, 2: Link description. */ |
|
2674 '<p><a target="_blank" rel="noopener" href="%1$s">%2$s</a></p>', |
|
2675 esc_url( __( 'https://developer.wordpress.org/advanced-administration/performance/optimization/#autoloaded-options' ) ), |
|
2676 __( 'More info about optimizing autoloaded options' ) |
|
2677 ); |
|
2678 |
|
2679 /** |
|
2680 * Filters actionable information to tackle the problem. It can be a link to an external guide. |
|
2681 * |
|
2682 * @since 6.6.0 |
|
2683 * |
|
2684 * @param string $actions Call to Action to be used to point to the right direction to solve the issue. |
|
2685 */ |
|
2686 $result['actions'] = apply_filters( 'site_status_autoloaded_options_action_to_perform', $result['actions'] ); |
|
2687 return $result; |
|
2688 } |
|
2689 |
|
2690 /** |
|
2691 * Returns a set of tests that belong to the site status page. |
|
2262 * |
2692 * |
2263 * Each site status test is defined here, they may be `direct` tests, that run on page load, or `async` tests |
2693 * Each site status test is defined here, they may be `direct` tests, that run on page load, or `async` tests |
2264 * which will run later down the line via JavaScript calls to improve page performance and hopefully also user |
2694 * which will run later down the line via JavaScript calls to improve page performance and hopefully also user |
2265 * experiences. |
2695 * experiences. |
2266 * |
2696 * |
2270 * @return array The list of tests to run. |
2700 * @return array The list of tests to run. |
2271 */ |
2701 */ |
2272 public static function get_tests() { |
2702 public static function get_tests() { |
2273 $tests = array( |
2703 $tests = array( |
2274 'direct' => array( |
2704 'direct' => array( |
2275 'wordpress_version' => array( |
2705 'wordpress_version' => array( |
2276 'label' => __( 'WordPress Version' ), |
2706 'label' => __( 'WordPress Version' ), |
2277 'test' => 'wordpress_version', |
2707 'test' => 'wordpress_version', |
2278 ), |
2708 ), |
2279 'plugin_version' => array( |
2709 'plugin_version' => array( |
2280 'label' => __( 'Plugin Versions' ), |
2710 'label' => __( 'Plugin Versions' ), |
2281 'test' => 'plugin_version', |
2711 'test' => 'plugin_version', |
2282 ), |
2712 ), |
2283 'theme_version' => array( |
2713 'theme_version' => array( |
2284 'label' => __( 'Theme Versions' ), |
2714 'label' => __( 'Theme Versions' ), |
2285 'test' => 'theme_version', |
2715 'test' => 'theme_version', |
2286 ), |
2716 ), |
2287 'php_version' => array( |
2717 'php_version' => array( |
2288 'label' => __( 'PHP Version' ), |
2718 'label' => __( 'PHP Version' ), |
2289 'test' => 'php_version', |
2719 'test' => 'php_version', |
2290 ), |
2720 ), |
2291 'php_extensions' => array( |
2721 'php_extensions' => array( |
2292 'label' => __( 'PHP Extensions' ), |
2722 'label' => __( 'PHP Extensions' ), |
2293 'test' => 'php_extensions', |
2723 'test' => 'php_extensions', |
2294 ), |
2724 ), |
2295 'php_default_timezone' => array( |
2725 'php_default_timezone' => array( |
2296 'label' => __( 'PHP Default Timezone' ), |
2726 'label' => __( 'PHP Default Timezone' ), |
2297 'test' => 'php_default_timezone', |
2727 'test' => 'php_default_timezone', |
2298 ), |
2728 ), |
2299 'php_sessions' => array( |
2729 'php_sessions' => array( |
2300 'label' => __( 'PHP Sessions' ), |
2730 'label' => __( 'PHP Sessions' ), |
2301 'test' => 'php_sessions', |
2731 'test' => 'php_sessions', |
2302 ), |
2732 ), |
2303 'sql_server' => array( |
2733 'sql_server' => array( |
2304 'label' => __( 'Database Server version' ), |
2734 'label' => __( 'Database Server version' ), |
2305 'test' => 'sql_server', |
2735 'test' => 'sql_server', |
2306 ), |
2736 ), |
2307 'utf8mb4_support' => array( |
2737 'ssl_support' => array( |
2308 'label' => __( 'MySQL utf8mb4 support' ), |
|
2309 'test' => 'utf8mb4_support', |
|
2310 ), |
|
2311 'ssl_support' => array( |
|
2312 'label' => __( 'Secure communication' ), |
2738 'label' => __( 'Secure communication' ), |
2313 'test' => 'ssl_support', |
2739 'test' => 'ssl_support', |
2314 ), |
2740 ), |
2315 'scheduled_events' => array( |
2741 'scheduled_events' => array( |
2316 'label' => __( 'Scheduled events' ), |
2742 'label' => __( 'Scheduled events' ), |
2317 'test' => 'scheduled_events', |
2743 'test' => 'scheduled_events', |
2318 ), |
2744 ), |
2319 'http_requests' => array( |
2745 'http_requests' => array( |
2320 'label' => __( 'HTTP Requests' ), |
2746 'label' => __( 'HTTP Requests' ), |
2321 'test' => 'http_requests', |
2747 'test' => 'http_requests', |
2322 ), |
2748 ), |
2323 'rest_availability' => array( |
2749 'rest_availability' => array( |
2324 'label' => __( 'REST API availability' ), |
2750 'label' => __( 'REST API availability' ), |
2325 'test' => 'rest_availability', |
2751 'test' => 'rest_availability', |
2326 'skip_cron' => true, |
2752 'skip_cron' => true, |
2327 ), |
2753 ), |
2328 'debug_enabled' => array( |
2754 'debug_enabled' => array( |
2329 'label' => __( 'Debugging enabled' ), |
2755 'label' => __( 'Debugging enabled' ), |
2330 'test' => 'is_in_debug_mode', |
2756 'test' => 'is_in_debug_mode', |
2331 ), |
2757 ), |
2332 'file_uploads' => array( |
2758 'file_uploads' => array( |
2333 'label' => __( 'File uploads' ), |
2759 'label' => __( 'File uploads' ), |
2334 'test' => 'file_uploads', |
2760 'test' => 'file_uploads', |
2335 ), |
2761 ), |
2336 'plugin_theme_auto_updates' => array( |
2762 'plugin_theme_auto_updates' => array( |
2337 'label' => __( 'Plugin and theme auto-updates' ), |
2763 'label' => __( 'Plugin and theme auto-updates' ), |
2338 'test' => 'plugin_theme_auto_updates', |
2764 'test' => 'plugin_theme_auto_updates', |
2765 ), |
|
2766 'update_temp_backup_writable' => array( |
|
2767 'label' => __( 'Plugin and theme temporary backup directory access' ), |
|
2768 'test' => 'update_temp_backup_writable', |
|
2769 ), |
|
2770 'available_updates_disk_space' => array( |
|
2771 'label' => __( 'Available disk space' ), |
|
2772 'test' => 'available_updates_disk_space', |
|
2773 ), |
|
2774 'autoloaded_options' => array( |
|
2775 'label' => __( 'Autoloaded options' ), |
|
2776 'test' => 'autoloaded_options', |
|
2339 ), |
2777 ), |
2340 ), |
2778 ), |
2341 'async' => array( |
2779 'async' => array( |
2342 'dotorg_communication' => array( |
2780 'dotorg_communication' => array( |
2343 'label' => __( 'Communication with WordPress.org' ), |
2781 'label' => __( 'Communication with WordPress.org' ), |
2375 'headers' => array( 'Authorization' => 'Basic ' . base64_encode( 'user:pwd' ) ), |
2813 'headers' => array( 'Authorization' => 'Basic ' . base64_encode( 'user:pwd' ) ), |
2376 'skip_cron' => true, |
2814 'skip_cron' => true, |
2377 ); |
2815 ); |
2378 } |
2816 } |
2379 |
2817 |
2818 // Only check for caches in production environments. |
|
2819 if ( 'production' === wp_get_environment_type() ) { |
|
2820 $tests['async']['page_cache'] = array( |
|
2821 'label' => __( 'Page cache' ), |
|
2822 'test' => rest_url( 'wp-site-health/v1/tests/page-cache' ), |
|
2823 'has_rest' => true, |
|
2824 'async_direct_test' => array( WP_Site_Health::get_instance(), 'get_test_page_cache' ), |
|
2825 ); |
|
2826 |
|
2827 $tests['direct']['persistent_object_cache'] = array( |
|
2828 'label' => __( 'Persistent object cache' ), |
|
2829 'test' => 'persistent_object_cache', |
|
2830 ); |
|
2831 } |
|
2832 |
|
2380 /** |
2833 /** |
2381 * Add or modify which site status tests are run on a site. |
2834 * Filters which site status tests are run on a site. |
2382 * |
2835 * |
2383 * The site health is determined by a set of tests based on best practices from |
2836 * The site health is determined by a set of tests based on best practices from |
2384 * both the WordPress Hosting Team and web standards in general. |
2837 * both the WordPress Hosting Team and web standards in general. |
2385 * |
2838 * |
2386 * Some sites may not have the same requirements, for example the automatic update |
2839 * Some sites may not have the same requirements, for example the automatic update |
2442 |
2895 |
2443 return $tests; |
2896 return $tests; |
2444 } |
2897 } |
2445 |
2898 |
2446 /** |
2899 /** |
2447 * Add a class to the body HTML tag. |
2900 * Adds a class to the body HTML tag. |
2448 * |
2901 * |
2449 * Filters the body class string for admin pages and adds our own class for easier styling. |
2902 * Filters the body class string for admin pages and adds our own class for easier styling. |
2450 * |
2903 * |
2451 * @since 5.2.0 |
2904 * @since 5.2.0 |
2452 * |
2905 * |
2463 |
2916 |
2464 return $body_class; |
2917 return $body_class; |
2465 } |
2918 } |
2466 |
2919 |
2467 /** |
2920 /** |
2468 * Initiate the WP_Cron schedule test cases. |
2921 * Initiates the WP_Cron schedule test cases. |
2469 * |
2922 * |
2470 * @since 5.2.0 |
2923 * @since 5.2.0 |
2471 */ |
2924 */ |
2472 private function wp_schedule_test_init() { |
2925 private function wp_schedule_test_init() { |
2473 $this->schedules = wp_get_schedules(); |
2926 $this->schedules = wp_get_schedules(); |
2474 $this->get_cron_tasks(); |
2927 $this->get_cron_tasks(); |
2475 } |
2928 } |
2476 |
2929 |
2477 /** |
2930 /** |
2478 * Populate our list of cron events and store them to a class-wide variable. |
2931 * Populates the list of cron events and store them to a class-wide variable. |
2479 * |
2932 * |
2480 * @since 5.2.0 |
2933 * @since 5.2.0 |
2481 */ |
2934 */ |
2482 private function get_cron_tasks() { |
2935 private function get_cron_tasks() { |
2483 $cron_tasks = _get_cron_array(); |
2936 $cron_tasks = _get_cron_array(); |
2506 } |
2959 } |
2507 } |
2960 } |
2508 } |
2961 } |
2509 |
2962 |
2510 /** |
2963 /** |
2511 * Check if any scheduled tasks have been missed. |
2964 * Checks if any scheduled tasks have been missed. |
2512 * |
2965 * |
2513 * Returns a boolean value of `true` if a scheduled task has been missed and ends processing. |
2966 * Returns a boolean value of `true` if a scheduled task has been missed and ends processing. |
2514 * |
2967 * |
2515 * If the list of crons is an instance of WP_Error, returns the instance instead of a boolean value. |
2968 * If the list of crons is an instance of WP_Error, returns the instance instead of a boolean value. |
2516 * |
2969 * |
2532 |
2985 |
2533 return false; |
2986 return false; |
2534 } |
2987 } |
2535 |
2988 |
2536 /** |
2989 /** |
2537 * Check if any scheduled tasks are late. |
2990 * Checks if any scheduled tasks are late. |
2538 * |
2991 * |
2539 * Returns a boolean value of `true` if a scheduled task is late and ends processing. |
2992 * Returns a boolean value of `true` if a scheduled task is late and ends processing. |
2540 * |
2993 * |
2541 * If the list of crons is an instance of WP_Error, returns the instance instead of a boolean value. |
2994 * If the list of crons is an instance of WP_Error, returns the instance instead of a boolean value. |
2542 * |
2995 * |
2562 |
3015 |
2563 return false; |
3016 return false; |
2564 } |
3017 } |
2565 |
3018 |
2566 /** |
3019 /** |
2567 * Check for potential issues with plugin and theme auto-updates. |
3020 * Checks for potential issues with plugin and theme auto-updates. |
2568 * |
3021 * |
2569 * Though there is no way to 100% determine if plugin and theme auto-updates are configured |
3022 * Though there is no way to 100% determine if plugin and theme auto-updates are configured |
2570 * correctly, a few educated guesses could be made to flag any conditions that would |
3023 * correctly, a few educated guesses could be made to flag any conditions that would |
2571 * potentially cause unexpected behaviors. |
3024 * potentially cause unexpected behaviors. |
2572 * |
3025 * |
2646 'message' => __( 'There appear to be no issues with plugin and theme auto-updates.' ), |
3099 'message' => __( 'There appear to be no issues with plugin and theme auto-updates.' ), |
2647 ); |
3100 ); |
2648 } |
3101 } |
2649 |
3102 |
2650 /** |
3103 /** |
2651 * Run a loopback test on our site. |
3104 * Runs a loopback test on the site. |
2652 * |
3105 * |
2653 * Loopbacks are what WordPress uses to communicate with itself to start up WP_Cron, scheduled posts, |
3106 * Loopbacks are what WordPress uses to communicate with itself to start up WP_Cron, scheduled posts, |
2654 * make sure plugin or theme edits don't cause site failures and similar. |
3107 * make sure plugin or theme edits don't cause site failures and similar. |
2655 * |
3108 * |
2656 * @since 5.2.0 |
3109 * @since 5.2.0 |
2658 * @return object The test results. |
3111 * @return object The test results. |
2659 */ |
3112 */ |
2660 public function can_perform_loopback() { |
3113 public function can_perform_loopback() { |
2661 $body = array( 'site-health' => 'loopback-test' ); |
3114 $body = array( 'site-health' => 'loopback-test' ); |
2662 $cookies = wp_unslash( $_COOKIE ); |
3115 $cookies = wp_unslash( $_COOKIE ); |
2663 $timeout = 10; |
3116 $timeout = 10; // 10 seconds. |
2664 $headers = array( |
3117 $headers = array( |
2665 'Cache-Control' => 'no-cache', |
3118 'Cache-Control' => 'no-cache', |
2666 ); |
3119 ); |
2667 /** This filter is documented in wp-includes/class-wp-http-streams.php */ |
3120 /** This filter is documented in wp-includes/class-wp-http-streams.php */ |
2668 $sslverify = apply_filters( 'https_local_ssl_verify', false ); |
3121 $sslverify = apply_filters( 'https_local_ssl_verify', false ); |
2718 'message' => __( 'The loopback request to your site completed successfully.' ), |
3171 'message' => __( 'The loopback request to your site completed successfully.' ), |
2719 ); |
3172 ); |
2720 } |
3173 } |
2721 |
3174 |
2722 /** |
3175 /** |
2723 * Create a weekly cron event, if one does not already exist. |
3176 * Creates a weekly cron event, if one does not already exist. |
2724 * |
3177 * |
2725 * @since 5.4.0 |
3178 * @since 5.4.0 |
2726 */ |
3179 */ |
2727 public function maybe_create_scheduled_event() { |
3180 public function maybe_create_scheduled_event() { |
2728 if ( ! wp_next_scheduled( 'wp_site_health_scheduled_check' ) && ! wp_installing() ) { |
3181 if ( ! wp_next_scheduled( 'wp_site_health_scheduled_check' ) && ! wp_installing() ) { |
2729 wp_schedule_event( time() + DAY_IN_SECONDS, 'weekly', 'wp_site_health_scheduled_check' ); |
3182 wp_schedule_event( time() + DAY_IN_SECONDS, 'weekly', 'wp_site_health_scheduled_check' ); |
2730 } |
3183 } |
2731 } |
3184 } |
2732 |
3185 |
2733 /** |
3186 /** |
2734 * Run our scheduled event to check and update the latest site health status for the website. |
3187 * Runs the scheduled event to check and update the latest site health status for the website. |
2735 * |
3188 * |
2736 * @since 5.4.0 |
3189 * @since 5.4.0 |
2737 */ |
3190 */ |
2738 public function wp_cron_scheduled_check() { |
3191 public function wp_cron_scheduled_check() { |
2739 // Bootstrap wp-admin, as WP_Cron doesn't do this for us. |
3192 // Bootstrap wp-admin, as WP_Cron doesn't do this for us. |
2828 } |
3281 } |
2829 } |
3282 } |
2830 |
3283 |
2831 foreach ( $results as $result ) { |
3284 foreach ( $results as $result ) { |
2832 if ( 'critical' === $result['status'] ) { |
3285 if ( 'critical' === $result['status'] ) { |
2833 $site_status['critical']++; |
3286 ++$site_status['critical']; |
2834 } elseif ( 'recommended' === $result['status'] ) { |
3287 } elseif ( 'recommended' === $result['status'] ) { |
2835 $site_status['recommended']++; |
3288 ++$site_status['recommended']; |
2836 } else { |
3289 } else { |
2837 $site_status['good']++; |
3290 ++$site_status['good']; |
2838 } |
3291 } |
2839 } |
3292 } |
2840 |
3293 |
2841 set_transient( 'health-check-site-status-result', wp_json_encode( $site_status ) ); |
3294 set_transient( 'health-check-site-status-result', wp_json_encode( $site_status ) ); |
2842 } |
3295 } |
2850 */ |
3303 */ |
2851 public function is_development_environment() { |
3304 public function is_development_environment() { |
2852 return in_array( wp_get_environment_type(), array( 'development', 'local' ), true ); |
3305 return in_array( wp_get_environment_type(), array( 'development', 'local' ), true ); |
2853 } |
3306 } |
2854 |
3307 |
3308 /** |
|
3309 * Returns a list of headers and its verification callback to verify if page cache is enabled or not. |
|
3310 * |
|
3311 * Note: key is header name and value could be callable function to verify header value. |
|
3312 * Empty value mean existence of header detect page cache is enabled. |
|
3313 * |
|
3314 * @since 6.1.0 |
|
3315 * |
|
3316 * @return array List of client caching headers and their (optional) verification callbacks. |
|
3317 */ |
|
3318 public function get_page_cache_headers() { |
|
3319 |
|
3320 $cache_hit_callback = static function ( $header_value ) { |
|
3321 return str_contains( strtolower( $header_value ), 'hit' ); |
|
3322 }; |
|
3323 |
|
3324 $cache_headers = array( |
|
3325 'cache-control' => static function ( $header_value ) { |
|
3326 return (bool) preg_match( '/max-age=[1-9]/', $header_value ); |
|
3327 }, |
|
3328 'expires' => static function ( $header_value ) { |
|
3329 return strtotime( $header_value ) > time(); |
|
3330 }, |
|
3331 'age' => static function ( $header_value ) { |
|
3332 return is_numeric( $header_value ) && $header_value > 0; |
|
3333 }, |
|
3334 'last-modified' => '', |
|
3335 'etag' => '', |
|
3336 'x-cache-enabled' => static function ( $header_value ) { |
|
3337 return 'true' === strtolower( $header_value ); |
|
3338 }, |
|
3339 'x-cache-disabled' => static function ( $header_value ) { |
|
3340 return ( 'on' !== strtolower( $header_value ) ); |
|
3341 }, |
|
3342 'x-srcache-store-status' => $cache_hit_callback, |
|
3343 'x-srcache-fetch-status' => $cache_hit_callback, |
|
3344 ); |
|
3345 |
|
3346 /** |
|
3347 * Filters the list of cache headers supported by core. |
|
3348 * |
|
3349 * @since 6.1.0 |
|
3350 * |
|
3351 * @param array $cache_headers Array of supported cache headers. |
|
3352 */ |
|
3353 return apply_filters( 'site_status_page_cache_supported_cache_headers', $cache_headers ); |
|
3354 } |
|
3355 |
|
3356 /** |
|
3357 * Checks if site has page cache enabled or not. |
|
3358 * |
|
3359 * @since 6.1.0 |
|
3360 * |
|
3361 * @return WP_Error|array { |
|
3362 * Page cache detection details or else error information. |
|
3363 * |
|
3364 * @type bool $advanced_cache_present Whether a page cache plugin is present. |
|
3365 * @type array[] $page_caching_response_headers Sets of client caching headers for the responses. |
|
3366 * @type float[] $response_timing Response timings. |
|
3367 * } |
|
3368 */ |
|
3369 private function check_for_page_caching() { |
|
3370 |
|
3371 /** This filter is documented in wp-includes/class-wp-http-streams.php */ |
|
3372 $sslverify = apply_filters( 'https_local_ssl_verify', false ); |
|
3373 |
|
3374 $headers = array(); |
|
3375 |
|
3376 /* |
|
3377 * Include basic auth in loopback requests. Note that this will only pass along basic auth when user is |
|
3378 * initiating the test. If a site requires basic auth, the test will fail when it runs in WP Cron as part of |
|
3379 * wp_site_health_scheduled_check. This logic is copied from WP_Site_Health::can_perform_loopback(). |
|
3380 */ |
|
3381 if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { |
|
3382 $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); |
|
3383 } |
|
3384 |
|
3385 $caching_headers = $this->get_page_cache_headers(); |
|
3386 $page_caching_response_headers = array(); |
|
3387 $response_timing = array(); |
|
3388 |
|
3389 for ( $i = 1; $i <= 3; $i++ ) { |
|
3390 $start_time = microtime( true ); |
|
3391 $http_response = wp_remote_get( home_url( '/' ), compact( 'sslverify', 'headers' ) ); |
|
3392 $end_time = microtime( true ); |
|
3393 |
|
3394 if ( is_wp_error( $http_response ) ) { |
|
3395 return $http_response; |
|
3396 } |
|
3397 if ( wp_remote_retrieve_response_code( $http_response ) !== 200 ) { |
|
3398 return new WP_Error( |
|
3399 'http_' . wp_remote_retrieve_response_code( $http_response ), |
|
3400 wp_remote_retrieve_response_message( $http_response ) |
|
3401 ); |
|
3402 } |
|
3403 |
|
3404 $response_headers = array(); |
|
3405 |
|
3406 foreach ( $caching_headers as $header => $callback ) { |
|
3407 $header_values = wp_remote_retrieve_header( $http_response, $header ); |
|
3408 if ( empty( $header_values ) ) { |
|
3409 continue; |
|
3410 } |
|
3411 $header_values = (array) $header_values; |
|
3412 if ( empty( $callback ) || ( is_callable( $callback ) && count( array_filter( $header_values, $callback ) ) > 0 ) ) { |
|
3413 $response_headers[ $header ] = $header_values; |
|
3414 } |
|
3415 } |
|
3416 |
|
3417 $page_caching_response_headers[] = $response_headers; |
|
3418 $response_timing[] = ( $end_time - $start_time ) * 1000; |
|
3419 } |
|
3420 |
|
3421 return array( |
|
3422 'advanced_cache_present' => ( |
|
3423 file_exists( WP_CONTENT_DIR . '/advanced-cache.php' ) |
|
3424 && |
|
3425 ( defined( 'WP_CACHE' ) && WP_CACHE ) |
|
3426 && |
|
3427 /** This filter is documented in wp-settings.php */ |
|
3428 apply_filters( 'enable_loading_advanced_cache_dropin', true ) |
|
3429 ), |
|
3430 'page_caching_response_headers' => $page_caching_response_headers, |
|
3431 'response_timing' => $response_timing, |
|
3432 ); |
|
3433 } |
|
3434 |
|
3435 /** |
|
3436 * Gets page cache details. |
|
3437 * |
|
3438 * @since 6.1.0 |
|
3439 * |
|
3440 * @return WP_Error|array { |
|
3441 * Page cache detail or else a WP_Error if unable to determine. |
|
3442 * |
|
3443 * @type string $status Page cache status. Good, Recommended or Critical. |
|
3444 * @type bool $advanced_cache_present Whether page cache plugin is available or not. |
|
3445 * @type string[] $headers Client caching response headers detected. |
|
3446 * @type float $response_time Response time of site. |
|
3447 * } |
|
3448 */ |
|
3449 private function get_page_cache_detail() { |
|
3450 $page_cache_detail = $this->check_for_page_caching(); |
|
3451 if ( is_wp_error( $page_cache_detail ) ) { |
|
3452 return $page_cache_detail; |
|
3453 } |
|
3454 |
|
3455 // Use the median server response time. |
|
3456 $response_timings = $page_cache_detail['response_timing']; |
|
3457 rsort( $response_timings ); |
|
3458 $page_speed = $response_timings[ floor( count( $response_timings ) / 2 ) ]; |
|
3459 |
|
3460 // Obtain unique set of all client caching response headers. |
|
3461 $headers = array(); |
|
3462 foreach ( $page_cache_detail['page_caching_response_headers'] as $page_caching_response_headers ) { |
|
3463 $headers = array_merge( $headers, array_keys( $page_caching_response_headers ) ); |
|
3464 } |
|
3465 $headers = array_unique( $headers ); |
|
3466 |
|
3467 // Page cache is detected if there are response headers or a page cache plugin is present. |
|
3468 $has_page_caching = ( count( $headers ) > 0 || $page_cache_detail['advanced_cache_present'] ); |
|
3469 |
|
3470 if ( $page_speed && $page_speed < $this->get_good_response_time_threshold() ) { |
|
3471 $result = $has_page_caching ? 'good' : 'recommended'; |
|
3472 } else { |
|
3473 $result = 'critical'; |
|
3474 } |
|
3475 |
|
3476 return array( |
|
3477 'status' => $result, |
|
3478 'advanced_cache_present' => $page_cache_detail['advanced_cache_present'], |
|
3479 'headers' => $headers, |
|
3480 'response_time' => $page_speed, |
|
3481 ); |
|
3482 } |
|
3483 |
|
3484 /** |
|
3485 * Gets the threshold below which a response time is considered good. |
|
3486 * |
|
3487 * @since 6.1.0 |
|
3488 * |
|
3489 * @return int Threshold in milliseconds. |
|
3490 */ |
|
3491 private function get_good_response_time_threshold() { |
|
3492 /** |
|
3493 * Filters the threshold below which a response time is considered good. |
|
3494 * |
|
3495 * The default is based on https://web.dev/time-to-first-byte/. |
|
3496 * |
|
3497 * @param int $threshold Threshold in milliseconds. Default 600. |
|
3498 * |
|
3499 * @since 6.1.0 |
|
3500 */ |
|
3501 return (int) apply_filters( 'site_status_good_response_time_threshold', 600 ); |
|
3502 } |
|
3503 |
|
3504 /** |
|
3505 * Determines whether to suggest using a persistent object cache. |
|
3506 * |
|
3507 * @since 6.1.0 |
|
3508 * |
|
3509 * @global wpdb $wpdb WordPress database abstraction object. |
|
3510 * |
|
3511 * @return bool Whether to suggest using a persistent object cache. |
|
3512 */ |
|
3513 public function should_suggest_persistent_object_cache() { |
|
3514 global $wpdb; |
|
3515 |
|
3516 /** |
|
3517 * Filters whether to suggest use of a persistent object cache and bypass default threshold checks. |
|
3518 * |
|
3519 * Using this filter allows to override the default logic, effectively short-circuiting the method. |
|
3520 * |
|
3521 * @since 6.1.0 |
|
3522 * |
|
3523 * @param bool|null $suggest Boolean to short-circuit, for whether to suggest using a persistent object cache. |
|
3524 * Default null. |
|
3525 */ |
|
3526 $short_circuit = apply_filters( 'site_status_should_suggest_persistent_object_cache', null ); |
|
3527 if ( is_bool( $short_circuit ) ) { |
|
3528 return $short_circuit; |
|
3529 } |
|
3530 |
|
3531 if ( is_multisite() ) { |
|
3532 return true; |
|
3533 } |
|
3534 |
|
3535 /** |
|
3536 * Filters the thresholds used to determine whether to suggest the use of a persistent object cache. |
|
3537 * |
|
3538 * @since 6.1.0 |
|
3539 * |
|
3540 * @param int[] $thresholds The list of threshold numbers keyed by threshold name. |
|
3541 */ |
|
3542 $thresholds = apply_filters( |
|
3543 'site_status_persistent_object_cache_thresholds', |
|
3544 array( |
|
3545 'alloptions_count' => 500, |
|
3546 'alloptions_bytes' => 100000, |
|
3547 'comments_count' => 1000, |
|
3548 'options_count' => 1000, |
|
3549 'posts_count' => 1000, |
|
3550 'terms_count' => 1000, |
|
3551 'users_count' => 1000, |
|
3552 ) |
|
3553 ); |
|
3554 |
|
3555 $alloptions = wp_load_alloptions(); |
|
3556 |
|
3557 if ( $thresholds['alloptions_count'] < count( $alloptions ) ) { |
|
3558 return true; |
|
3559 } |
|
3560 |
|
3561 if ( $thresholds['alloptions_bytes'] < strlen( serialize( $alloptions ) ) ) { |
|
3562 return true; |
|
3563 } |
|
3564 |
|
3565 $table_names = implode( "','", array( $wpdb->comments, $wpdb->options, $wpdb->posts, $wpdb->terms, $wpdb->users ) ); |
|
3566 |
|
3567 // With InnoDB the `TABLE_ROWS` are estimates, which are accurate enough and faster to retrieve than individual `COUNT()` queries. |
|
3568 $results = $wpdb->get_results( |
|
3569 $wpdb->prepare( |
|
3570 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- This query cannot use interpolation. |
|
3571 "SELECT TABLE_NAME AS 'table', TABLE_ROWS AS 'rows', SUM(data_length + index_length) as 'bytes' FROM information_schema.TABLES WHERE TABLE_SCHEMA = %s AND TABLE_NAME IN ('$table_names') GROUP BY TABLE_NAME;", |
|
3572 DB_NAME |
|
3573 ), |
|
3574 OBJECT_K |
|
3575 ); |
|
3576 |
|
3577 $threshold_map = array( |
|
3578 'comments_count' => $wpdb->comments, |
|
3579 'options_count' => $wpdb->options, |
|
3580 'posts_count' => $wpdb->posts, |
|
3581 'terms_count' => $wpdb->terms, |
|
3582 'users_count' => $wpdb->users, |
|
3583 ); |
|
3584 |
|
3585 foreach ( $threshold_map as $threshold => $table ) { |
|
3586 if ( $thresholds[ $threshold ] <= $results[ $table ]->rows ) { |
|
3587 return true; |
|
3588 } |
|
3589 } |
|
3590 |
|
3591 return false; |
|
3592 } |
|
3593 |
|
3594 /** |
|
3595 * Returns a list of available persistent object cache services. |
|
3596 * |
|
3597 * @since 6.1.0 |
|
3598 * |
|
3599 * @return string[] The list of available persistent object cache services. |
|
3600 */ |
|
3601 private function available_object_cache_services() { |
|
3602 $extensions = array_map( |
|
3603 'extension_loaded', |
|
3604 array( |
|
3605 'APCu' => 'apcu', |
|
3606 'Redis' => 'redis', |
|
3607 'Relay' => 'relay', |
|
3608 'Memcache' => 'memcache', |
|
3609 'Memcached' => 'memcached', |
|
3610 ) |
|
3611 ); |
|
3612 |
|
3613 $services = array_keys( array_filter( $extensions ) ); |
|
3614 |
|
3615 /** |
|
3616 * Filters the persistent object cache services available to the user. |
|
3617 * |
|
3618 * This can be useful to hide or add services not included in the defaults. |
|
3619 * |
|
3620 * @since 6.1.0 |
|
3621 * |
|
3622 * @param string[] $services The list of available persistent object cache services. |
|
3623 */ |
|
3624 return apply_filters( 'site_status_available_object_cache_services', $services ); |
|
3625 } |
|
2855 } |
3626 } |