From 0612ea2d381b510dbe3385f1b8e6350911083b17 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Wed, 30 Jan 2019 11:00:30 +0000 Subject: [PATCH] Bootstrap/Load: Revert fatal error recovery mechanism from 5.1 to polish for 5.2. Due to the high number of follow-up tickets and associated security concerns, it was decided to reschedule the fatal error recovery feature for WordPress 5.2, in order to address these issues properly. The feature will continue to be developed, with iterations being merged into trunk early in the 5.2 release cycle. Fixes #46141. See #44458, #45932, #45940, #46038, #46047, #46068. git-svn-id: https://develop.svn.wordpress.org/trunk@44717 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/css/list-tables.css | 25 -- src/wp-admin/includes/admin-filters.php | 2 - .../includes/class-wp-plugins-list-table.php | 81 +------ src/wp-admin/includes/plugin.php | 183 +-------------- src/wp-admin/includes/theme.php | 124 ---------- src/wp-admin/plugins.php | 25 -- src/wp-admin/themes.php | 49 ---- src/wp-includes/capabilities.php | 8 - .../class-wp-fatal-error-handler.php | 200 ---------------- .../class-wp-paused-extensions-storage.php | 221 ------------------ src/wp-includes/class-wp-theme.php | 4 - src/wp-includes/error-protection.php | 199 ---------------- src/wp-includes/load.php | 161 ------------- src/wp-includes/ms-load.php | 8 - src/wp-settings.php | 15 -- tests/phpunit/tests/user/capabilities.php | 3 - 16 files changed, 10 insertions(+), 1298 deletions(-) delete mode 100644 src/wp-includes/class-wp-fatal-error-handler.php delete mode 100644 src/wp-includes/class-wp-paused-extensions-storage.php delete mode 100644 src/wp-includes/error-protection.php diff --git a/src/wp-admin/css/list-tables.css b/src/wp-admin/css/list-tables.css index 60c0da30c5..c22d4cc852 100644 --- a/src/wp-admin/css/list-tables.css +++ b/src/wp-admin/css/list-tables.css @@ -1301,31 +1301,6 @@ ul.cat-checklist { text-decoration: underline; } -.plugins tr.paused th.check-column { - border-left: 4px solid #d54e21; -} - -.plugins tr.paused th, -.plugins tr.paused td { - background-color: #fef7f1; -} - -.plugins tr.paused .plugin-title, -.plugins .paused .dashicons-warning { - color: #dc3232; -} - -.plugins .paused .error-display p, -.plugins .paused .error-display code { - font-size: 90%; - font-style: italic; - color: rgb( 0, 0, 0, 0.7 ); -} - -.plugins .resume-link { - color: #dc3232; -} - .plugin-card .update-now:before { color: #f56e28; content: "\f463"; diff --git a/src/wp-admin/includes/admin-filters.php b/src/wp-admin/includes/admin-filters.php index 3407e93171..4da6469d5f 100644 --- a/src/wp-admin/includes/admin-filters.php +++ b/src/wp-admin/includes/admin-filters.php @@ -117,8 +117,6 @@ add_action( 'load-plugins.php', 'wp_plugin_update_rows', 20 ); // After wp_updat add_action( 'load-themes.php', 'wp_theme_update_rows', 20 ); // After wp_update_themes() is called. add_action( 'admin_notices', 'update_nag', 3 ); -add_action( 'admin_notices', 'paused_plugins_notice', 5 ); -add_action( 'admin_notices', 'paused_themes_notice', 5 ); add_action( 'admin_notices', 'maintenance_nag', 10 ); add_filter( 'update_footer', 'core_update_footer' ); diff --git a/src/wp-admin/includes/class-wp-plugins-list-table.php b/src/wp-admin/includes/class-wp-plugins-list-table.php index a07945a973..fa40ba9a36 100644 --- a/src/wp-admin/includes/class-wp-plugins-list-table.php +++ b/src/wp-admin/includes/class-wp-plugins-list-table.php @@ -40,7 +40,7 @@ class WP_Plugins_List_Table extends WP_List_Table { ); $status = 'all'; - if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search', 'paused' ) ) ) { + if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search' ) ) ) { $status = $_REQUEST['plugin_status']; } @@ -99,7 +99,6 @@ class WP_Plugins_List_Table extends WP_List_Table { 'upgrade' => array(), 'mustuse' => array(), 'dropins' => array(), - 'paused' => array(), ); $screen = $this->screen; @@ -210,9 +209,6 @@ class WP_Plugins_List_Table extends WP_List_Table { if ( $show_network_active ) { // On the non-network screen, show network-active plugins if allowed $plugins['active'][ $plugin_file ] = $plugin_data; - if ( is_plugin_paused( $plugin_file ) ) { - $plugins['paused'][ $plugin_file ] = $plugin_data; - } } else { // On the non-network screen, filter out network-active plugins unset( $plugins['all'][ $plugin_file ] ); @@ -222,9 +218,6 @@ class WP_Plugins_List_Table extends WP_List_Table { // On the non-network screen, populate the active list with plugins that are individually activated // On the network-admin screen, populate the active list with plugins that are network activated $plugins['active'][ $plugin_file ] = $plugin_data; - if ( is_plugin_paused( $plugin_file ) ) { - $plugins['paused'][ $plugin_file ] = $plugin_data; - } } else { if ( isset( $recently_activated[ $plugin_file ] ) ) { // Populate the recently activated list with plugins that have been recently activated @@ -452,10 +445,6 @@ class WP_Plugins_List_Table extends WP_List_Table { /* translators: %s: plugin count */ $text = _n( 'Drop-ins (%s)', 'Drop-ins (%s)', $count ); break; - case 'paused': - /* translators: %s: plugin count */ - $text = _n( 'Paused (%s)', 'Paused (%s)', $count ); - break; case 'upgrade': /* translators: %s: plugin count */ $text = _n( 'Update Available (%s)', 'Update Available (%s)', $count ); @@ -644,19 +633,11 @@ class WP_Plugins_List_Table extends WP_List_Table { /* translators: %s: plugin name */ $actions['deactivate'] = '' . __( 'Network Deactivate' ) . ''; } - if ( current_user_can( 'manage_network_plugins' ) && count_paused_plugin_sites_for_network( $plugin_file ) ) { - /* translators: %s: plugin name */ - $actions['resume'] = '' . __( 'Network Resume' ) . ''; - } } else { if ( current_user_can( 'manage_network_plugins' ) ) { /* translators: %s: plugin name */ $actions['activate'] = '' . __( 'Network Activate' ) . ''; } - if ( current_user_can( 'manage_network_plugins' ) && count_paused_plugin_sites_for_network( $plugin_file ) ) { - /* translators: %s: plugin name */ - $actions['resume'] = '' . __( 'Network Resume' ) . ''; - } if ( current_user_can( 'delete_plugins' ) && ! is_plugin_active( $plugin_file ) ) { /* translators: %s: plugin name */ $actions['delete'] = '' . __( 'Delete' ) . ''; @@ -667,10 +648,6 @@ class WP_Plugins_List_Table extends WP_List_Table { $actions = array( 'network_active' => __( 'Network Active' ), ); - if ( ! $restrict_network_only && current_user_can( 'resume_plugin', $plugin_file ) && is_plugin_paused( $plugin_file ) ) { - /* translators: %s: plugin name */ - $actions['resume'] = '' . __( 'Resume' ) . ''; - } } elseif ( $restrict_network_only ) { $actions = array( 'network_only' => __( 'Network Only' ), @@ -680,10 +657,6 @@ class WP_Plugins_List_Table extends WP_List_Table { /* translators: %s: plugin name */ $actions['deactivate'] = '' . __( 'Deactivate' ) . ''; } - if ( current_user_can( 'resume_plugin', $plugin_file ) && is_plugin_paused( $plugin_file ) ) { - /* translators: %s: plugin name */ - $actions['resume'] = '' . __( 'Resume' ) . ''; - } } else { if ( current_user_can( 'activate_plugin', $plugin_file ) ) { /* translators: %s: plugin name */ @@ -791,12 +764,6 @@ class WP_Plugins_List_Table extends WP_List_Table { $class .= ' update'; } - $paused = is_plugin_paused( $plugin_file ); - $paused_on_network_sites_count = $screen->in_admin( 'network' ) ? count_paused_plugin_sites_for_network( $plugin_file ) : 0; - if ( $paused || $paused_on_network_sites_count ) { - $class .= ' paused'; - } - $plugin_slug = isset( $plugin_data['slug'] ) ? $plugin_data['slug'] : sanitize_title( $plugin_name ); printf( '', @@ -878,53 +845,13 @@ class WP_Plugins_List_Table extends WP_List_Table { * @param array $plugin_data An array of plugin data. * @param string $status Status of the plugin. Defaults are 'All', 'Active', * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', - * 'Drop-ins', 'Search', 'Paused'. + * 'Drop-ins', 'Search'. */ $plugin_meta = apply_filters( 'plugin_row_meta', $plugin_meta, $plugin_file, $plugin_data, $status ); echo implode( ' | ', $plugin_meta ); echo ''; - if ( $paused || $paused_on_network_sites_count ) { - $notice_text = __( 'This plugin failed to load properly and was paused within the admin backend.' ); - if ( $screen->in_admin( 'network' ) && $paused_on_network_sites_count ) { - $notice_text = sprintf( - /* translators: %s: number of sites */ - _n( 'This plugin failed to load properly and was paused within the admin backend for %s site.', 'This plugin failed to load properly and was paused within the admin backend for %s sites.', $paused_on_network_sites_count ), - number_format_i18n( $paused_on_network_sites_count ) - ); - } - - printf( '

%s

', $notice_text ); - - $error = wp_get_plugin_error( $plugin_file ); - - if ( false !== $error ) { - $constants = get_defined_constants( true ); - $constants = isset( $constants['Core'] ) ? $constants['Core'] : $constants['internal']; - - foreach ( $constants as $constant => $value ) { - if ( 0 === strpos( $constant, 'E_' ) ) { - $core_errors[ $value ] = $constant; - } - } - - $error['type'] = $core_errors[ $error['type'] ]; - - printf( - '

%s

', - sprintf( - /* translators: 1: error type, 2: error line number, 3: error file name, 4: error message */ - __( 'The plugin caused an error of type %1$s in line %2$s of the file %3$s. Error message: %4$s' ), - "{$error['type']}", - "{$error['line']}", - "{$error['file']}", - "{$error['message']}" - ) - ); - } - } - echo ''; break; default: @@ -958,7 +885,7 @@ class WP_Plugins_List_Table extends WP_List_Table { * @param array $plugin_data An array of plugin data. * @param string $status Status of the plugin. Defaults are 'All', 'Active', * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', - * 'Drop-ins', 'Search', 'Paused'. + * 'Drop-ins', 'Search'. */ do_action( 'after_plugin_row', $plugin_file, $plugin_data, $status ); @@ -974,7 +901,7 @@ class WP_Plugins_List_Table extends WP_List_Table { * @param array $plugin_data An array of plugin data. * @param string $status Status of the plugin. Defaults are 'All', 'Active', * 'Inactive', 'Recently Activated', 'Upgrade', 'Must-Use', - * 'Drop-ins', 'Search', 'Paused'. + * 'Drop-ins', 'Search'. */ do_action( "after_plugin_row_{$plugin_file}", $plugin_file, $plugin_data, $status ); } diff --git a/src/wp-admin/includes/plugin.php b/src/wp-admin/includes/plugin.php index 05e3861f17..5873a33466 100644 --- a/src/wp-admin/includes/plugin.php +++ b/src/wp-admin/includes/plugin.php @@ -468,14 +468,12 @@ function get_dropins() { */ function _get_dropins() { $dropins = array( - 'advanced-cache.php' => array( __( 'Advanced caching plugin.' ), 'WP_CACHE' ), // WP_CACHE - 'db.php' => array( __( 'Custom database class.' ), true ), // auto on load - 'db-error.php' => array( __( 'Custom database error message.' ), true ), // auto on error - 'install.php' => array( __( 'Custom installation script.' ), true ), // auto on installation - 'maintenance.php' => array( __( 'Custom maintenance message.' ), true ), // auto on maintenance - 'object-cache.php' => array( __( 'External object cache.' ), true ), // auto on load - 'php-error.php' => array( __( 'Custom PHP error message.' ), true ), // auto on error - 'fatal-error-handler.php' => array( __( 'Custom PHP fatal error handler.' ), true ), // auto on error + 'advanced-cache.php' => array( __( 'Advanced caching plugin.' ), 'WP_CACHE' ), // WP_CACHE + 'db.php' => array( __( 'Custom database class.' ), true ), // auto on load + 'db-error.php' => array( __( 'Custom database error message.' ), true ), // auto on error + 'install.php' => array( __( 'Custom installation script.' ), true ), // auto on installation + 'maintenance.php' => array( __( 'Custom maintenance message.' ), true ), // auto on maintenance + 'object-cache.php' => array( __( 'External object cache.' ), true ), // auto on load ); if ( is_multisite() ) { @@ -528,84 +526,6 @@ function is_plugin_inactive( $plugin ) { return ! is_plugin_active( $plugin ); } -/** - * Determines whether a plugin is technically active but was paused while - * loading. - * - * For more information on this and similar theme functions, check out - * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ - * Conditional Tags} article in the Theme Developer Handbook. - * - * @since 5.1.0 - * - * @param string $plugin Path to the plugin file relative to the plugins directory. - * @return bool True, if in the list of paused plugins. False, not in the list. - */ -function is_plugin_paused( $plugin ) { - if ( ! isset( $GLOBALS['_paused_plugins'] ) ) { - return false; - } - - if ( ! is_plugin_active( $plugin ) && ! is_plugin_active_for_network( $plugin ) ) { - return false; - } - - list( $plugin ) = explode( '/', $plugin ); - - return array_key_exists( $plugin, $GLOBALS['_paused_plugins'] ); -} - -/** - * Gets the error that was recorded for a paused plugin. - * - * @since 5.1.0 - * - * @param string $plugin Path to the plugin file relative to the plugins - * directory. - * @return array|false Array of error information as it was returned by - * `error_get_last()`, or false if none was recorded. - */ -function wp_get_plugin_error( $plugin ) { - if ( ! isset( $GLOBALS['_paused_plugins'] ) ) { - return false; - } - - list( $plugin ) = explode( '/', $plugin ); - - if ( ! array_key_exists( $plugin, $GLOBALS['_paused_plugins'] ) ) { - return false; - } - - return $GLOBALS['_paused_plugins'][ $plugin ]; -} - -/** - * Gets the number of sites on which a specific plugin is paused. - * - * @since 5.1.0 - * - * @param string $plugin Path to the plugin file relative to the plugins directory. - * @return int Site count. - */ -function count_paused_plugin_sites_for_network( $plugin ) { - if ( ! is_multisite() ) { - return is_plugin_paused( $plugin ) ? 1 : 0; - } - - list( $plugin ) = explode( '/', $plugin ); - - $query_args = array( - 'count' => true, - 'number' => 0, - 'network_id' => get_current_network_id(), - 'meta_query' => array( - wp_paused_plugins()->get_site_meta_query_clause( $plugin ), - ), - ); - - return get_sites( $query_args ); -} - /** * Determines whether the plugin is active for the entire network. * @@ -803,11 +723,6 @@ function deactivate_plugins( $plugins, $silent = false, $network_wide = null ) { continue; } - // Clean up the database before deactivating the plugin. - if ( is_plugin_paused( $plugin ) ) { - resume_plugin( $plugin ); - } - $network_deactivating = false !== $network_wide && is_plugin_active_for_network( $plugin ); if ( ! $silent ) { @@ -1002,11 +917,6 @@ function delete_plugins( $plugins, $deprecated = '' ) { uninstall_plugin( $plugin_file ); } - // Clean up the database before removing the plugin. - if ( is_plugin_paused( $plugin_file ) ) { - resume_plugin( $plugin_file ); - } - /** * Fires immediately before a plugin deletion attempt. * @@ -1084,57 +994,6 @@ function delete_plugins( $plugins, $deprecated = '' ) { return true; } -/** - * Tries to resume a single plugin. - * - * If a redirect was provided, we first ensure the plugin does not throw fatal - * errors anymore. - * - * The way it works is by setting the redirection to the error before trying to - * include the plugin file. If the plugin fails, then the redirection will not - * be overwritten with the success message and the plugin will not be resumed. - * - * @since 5.1.0 - * - * @param string $plugin Single plugin to resume. - * @param string $redirect Optional. URL to redirect to. Default empty string. - * @param bool $network_wide Optional. Whether to resume the plugin for the entire - * network. Default false. - * @return bool|WP_Error True on success, false if `$plugin` was not paused, - * `WP_Error` on failure. - */ -function resume_plugin( $plugin, $redirect = '', $network_wide = false ) { - /* - * We'll override this later if the plugin could be included without - * creating a fatal error. - */ - if ( ! empty( $redirect ) ) { - wp_redirect( - add_query_arg( - '_error_nonce', - wp_create_nonce( 'plugin-resume-error_' . $plugin ), - $redirect - ) - ); - - // Load the plugin to test whether it throws a fatal error. - ob_start(); - plugin_sandbox_scrape( $plugin ); - ob_clean(); - } - - $result = wp_forget_extension_error( 'plugins', $plugin, $network_wide ); - - if ( ! $result ) { - return new WP_Error( - 'could_not_resume_plugin', - __( 'Could not resume the plugin.' ) - ); - } - - return true; -} - /** * Validate active plugins * @@ -2242,33 +2101,3 @@ function wp_add_privacy_policy_content( $plugin_name, $policy_text ) { WP_Privacy_Policy_Content::add( $plugin_name, $policy_text ); } - -/** - * Renders an admin notice in case some plugins have been paused due to errors. - * - * @since 5.1.0 - */ -function paused_plugins_notice() { - if ( 'plugins.php' === $GLOBALS['pagenow'] ) { - return; - } - - if ( ! current_user_can( 'deactivate_plugins' ) ) { - return; - } - - if ( ! isset( $GLOBALS['_paused_plugins'] ) || empty( $GLOBALS['_paused_plugins'] ) ) { - return; - } - - printf( - '

%s
%s

%s

', - __( 'One or more plugins failed to load properly.' ), - __( 'You can find more details and make changes on the Plugins screen.' ), - sprintf( - '%s', - admin_url( 'plugins.php?plugin_status=paused' ), - 'Go to the Plugins screen' - ) - ); -} diff --git a/src/wp-admin/includes/theme.php b/src/wp-admin/includes/theme.php index 7c307c33eb..91a3577906 100644 --- a/src/wp-admin/includes/theme.php +++ b/src/wp-admin/includes/theme.php @@ -768,127 +768,3 @@ function customize_themes_print_templates() { true, - 'number' => 0, - 'network_id' => get_current_network_id(), - 'meta_query' => array( - wp_paused_themes()->get_site_meta_query_clause( $theme ), - ), - ); - - return get_sites( $query_args ); -} - -/** - * Tries to resume a single theme. - * - * @since 5.1.0 - * - * @param string $theme Single theme to resume. - * @return bool|WP_Error True on success, false if `$theme` was not paused, - * `WP_Error` on failure. - */ -function resume_theme( $theme ) { - $result = wp_forget_extension_error( 'themes', $theme ); - - if ( ! $result ) { - return new WP_Error( - 'could_not_resume_theme', - __( 'Could not resume the theme.' ) - ); - } - - return true; -} - -/** - * Renders an admin notice in case some themes have been paused due to errors. - * - * @since 5.1.0 - */ -function paused_themes_notice() { - if ( 'themes.php' === $GLOBALS['pagenow'] ) { - return; - } - - if ( ! current_user_can( 'switch_themes' ) ) { - return; - } - - if ( ! isset( $GLOBALS['_paused_themes'] ) || empty( $GLOBALS['_paused_themes'] ) ) { - return; - } - - printf( - '

%s
%s

%s

', - __( 'One or more themes failed to load properly.' ), - __( 'You can find more details and make changes on the Themes screen.' ), - sprintf( - '%s', - admin_url( 'themes.php' ), - 'Go to the Themes screen' - ) - ); -} diff --git a/src/wp-admin/plugins.php b/src/wp-admin/plugins.php index 6c41e31b2f..c80e96831f 100644 --- a/src/wp-admin/plugins.php +++ b/src/wp-admin/plugins.php @@ -389,27 +389,6 @@ if ( $action ) { } break; - case 'resume': - if ( ! current_user_can( 'resume_plugin', $plugin ) ) { - wp_die( __( 'Sorry, you are not allowed to resume this plugin.' ) ); - } - - if ( is_multisite() && ! is_network_admin() && is_network_only_plugin( $plugin ) ) { - wp_redirect( self_admin_url( "plugins.php?plugin_status=$status&paged=$page&s=$s" ) ); - exit; - } - - check_admin_referer( 'resume-plugin_' . $plugin ); - - $result = resume_plugin( $plugin, self_admin_url( 'plugins.php?error=resuming' ), is_network_admin() ); - - if ( is_wp_error( $result ) ) { - wp_die( $result ); - } - - wp_redirect( self_admin_url( "plugins.php?resume=true&plugin_status=$status&paged=$page&s=$s" ) ); - exit; - default: if ( isset( $_POST['checked'] ) ) { check_admin_referer( 'bulk-plugins' ); @@ -509,8 +488,6 @@ if ( isset( $_GET['error'] ) ) : $_GET['charsout'] ); $errmsg .= ' ' . __( 'If you notice “headers already sent” messages, problems with syndication feeds or other issues, try deactivating or removing this plugin.' ); - } elseif ( 'resuming' === $_GET['error'] ) { - $errmsg = __( 'Plugin could not be resumed because it triggered a fatal error.' ); } else { $errmsg = __( 'Plugin could not be activated because it triggered a fatal error.' ); } @@ -564,8 +541,6 @@ elseif ( isset( $_GET['deleted'] ) ) :

deactivated.' ); ?>

- -

resumed.' ); ?>

diff --git a/src/wp-admin/themes.php b/src/wp-admin/themes.php index 754b75808d..494e952184 100644 --- a/src/wp-admin/themes.php +++ b/src/wp-admin/themes.php @@ -33,26 +33,6 @@ if ( current_user_can( 'switch_themes' ) && isset( $_GET['action'] ) ) { switch_theme( $theme->get_stylesheet() ); wp_redirect( admin_url( 'themes.php?activated=true' ) ); exit; - } elseif ( 'resume' === $_GET['action'] ) { - check_admin_referer( 'resume-theme_' . $_GET['stylesheet'] ); - $theme = wp_get_theme( $_GET['stylesheet'] ); - - if ( ! current_user_can( 'resume_themes' ) ) { - wp_die( - '

' . __( 'You need a higher level of permission.' ) . '

' . - '

' . __( 'Sorry, you are not allowed to resume this theme.' ) . '

', - 403 - ); - } - - $result = resume_theme( $theme->get_stylesheet() ); - - if ( is_wp_error( $result ) ) { - wp_die( $result ); - } - - wp_redirect( admin_url( 'themes.php?resumed=true' ) ); - exit; } elseif ( 'delete' == $_GET['action'] ) { check_admin_referer( 'delete-theme_' . $_GET['stylesheet'] ); $theme = wp_get_theme( $_GET['stylesheet'] ); @@ -215,10 +195,6 @@ if ( ! validate_current_theme() || isset( $_GET['broken'] ) ) { ?>

-

-

@@ -380,9 +355,6 @@ if ( ! is_multisite() && current_user_can( 'edit_themes' ) && $broken_themes = w - - - @@ -395,27 +367,6 @@ if ( ! is_multisite() && current_user_can( 'edit_themes' ) && $broken_themes = w get( 'Name' ) ? $broken_theme->display( 'Name' ) : $broken_theme->get_stylesheet(); ?> errors()->get_error_message(); ?> errors()->get_error_code() ) { - $stylesheet = $broken_theme->get_stylesheet(); - $resume_url = add_query_arg( - array( - 'action' => 'resume', - 'stylesheet' => urlencode( $stylesheet ), - ), - admin_url( 'themes.php' ) - ); - $resume_url = wp_nonce_url( $resume_url, 'resume-theme_' . $stylesheet ); - ?> - - - - get_stylesheet(); $delete_url = add_query_arg( diff --git a/src/wp-includes/capabilities.php b/src/wp-includes/capabilities.php index afb6f59507..c192639608 100644 --- a/src/wp-includes/capabilities.php +++ b/src/wp-includes/capabilities.php @@ -464,14 +464,6 @@ function map_meta_cap( $cap, $user_id ) { } } break; - case 'resume_plugin': - // Even in a multisite, regular administrators should be able to resume a plugin. - $caps[] = 'activate_plugins'; - break; - case 'resume_themes': - // Even in a multisite, regular administrators should be able to resume a theme. - $caps[] = 'switch_themes'; - break; case 'delete_user': case 'delete_users': // If multisite only super admins can delete users. diff --git a/src/wp-includes/class-wp-fatal-error-handler.php b/src/wp-includes/class-wp-fatal-error-handler.php deleted file mode 100644 index 0299b67a48..0000000000 --- a/src/wp-includes/class-wp-fatal-error-handler.php +++ /dev/null @@ -1,200 +0,0 @@ -detect_error(); - if ( ! $error ) { - return; - } - - // If the error was stored and thus the extension paused, - // redirect the request to catch multiple errors in one go. - if ( $this->store_error( $error ) ) { - $this->redirect_protected(); - } - - // Display the PHP error template. - $this->display_error_template(); - } catch ( Exception $e ) { - // Catch exceptions and remain silent. - } - } - - /** - * Detects the error causing the crash if it should be handled. - * - * @since 5.1.0 - * - * @return array|null Error that was triggered, or null if no error received or if the error should not be handled. - */ - protected function detect_error() { - $error = error_get_last(); - - // No error, just skip the error handling code. - if ( null === $error ) { - return null; - } - - // Bail if this error should not be handled. - if ( ! wp_should_handle_error( $error ) ) { - return null; - } - - return $error; - } - - /** - * Stores the given error so that the extension causing it is paused. - * - * @since 5.1.0 - * - * @param array $error Error that was triggered. - * @return bool True if the error was stored successfully, false otherwise. - */ - protected function store_error( $error ) { - // Do not pause extensions if they only crash on a non-protected endpoint. - if ( ! is_protected_endpoint() ) { - return false; - } - - return wp_record_extension_error( $error ); - } - - /** - * Redirects the current request to allow recovering multiple errors in one go. - * - * The redirection will only happen when on a protected endpoint. - * - * It must be ensured that this method is only called when an error actually occurred and will not occur on the - * next request again. Otherwise it will create a redirect loop. - * - * @since 5.1.0 - */ - protected function redirect_protected() { - // Do not redirect requests on non-protected endpoints. - if ( ! is_protected_endpoint() ) { - return; - } - - // Pluggable is usually loaded after plugins, so we manually include it here for redirection functionality. - if ( ! function_exists( 'wp_redirect' ) ) { - include ABSPATH . WPINC . '/pluggable.php'; - } - - $scheme = is_ssl() ? 'https://' : 'http://'; - - $url = "{$scheme}{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"; - wp_redirect( $url ); - exit; - } - - /** - * Displays the PHP error template and sends the HTTP status code, typically 500. - * - * A drop-in 'php-error.php' can be used as a custom template. This drop-in should control the HTTP status code and - * print the HTML markup indicating that a PHP error occurred. Note that this drop-in may potentially be executed - * very early in the WordPress bootstrap process, so any core functions used that are not part of - * `wp-includes/load.php` should be checked for before being called. - * - * If no such drop-in is available, this will call {@see WP_Fatal_Error_Handler::display_default_error_template()}. - * - * @since 5.1.0 - */ - protected function display_error_template() { - if ( defined( 'WP_CONTENT_DIR' ) ) { - // Load custom PHP error template, if present. - $php_error_pluggable = WP_CONTENT_DIR . '/php-error.php'; - if ( is_readable( $php_error_pluggable ) ) { - require_once $php_error_pluggable; - return; - } - } - - // Otherwise, display the default error template. - $this->display_default_error_template(); - } - - /** - * Displays the default PHP error template. - * - * This method is called conditionally if no 'php-error.php' drop-in is available. - * - * It calls {@see wp_die()} with a message indicating that the site is experiencing technical difficulties and a - * login link to the admin backend. The {@see 'wp_php_error_message'} and {@see 'wp_php_error_args'} filters can - * be used to modify these parameters. - * - * @since 5.1.0 - */ - protected function display_default_error_template() { - if ( ! function_exists( '__' ) ) { - wp_load_translations_early(); - } - - if ( ! function_exists( 'wp_die' ) ) { - require_once ABSPATH . WPINC . '/functions.php'; - } - - $message = __( 'The site is experiencing technical difficulties.' ); - - $args = array( - 'response' => 500, - 'exit' => false, - ); - if ( function_exists( 'admin_url' ) ) { - $args['link_url'] = admin_url(); - $args['link_text'] = __( 'Log into the admin backend to fix this.' ); - } - - /** - * Filters the message that the default PHP error template displays. - * - * @since 5.1.0 - * - * @param string $message HTML error message to display. - */ - $message = apply_filters( 'wp_php_error_message', $message ); - - /** - * Filters the arguments passed to {@see wp_die()} for the default PHP error template. - * - * @since 5.1.0 - * - * @param array $args Associative array of arguments passed to `wp_die()`. By default these contain a - * 'response' key, and optionally 'link_url' and 'link_text' keys. - */ - $args = apply_filters( 'wp_php_error_args', $args ); - - wp_die( $message, '', $args ); - } -} diff --git a/src/wp-includes/class-wp-paused-extensions-storage.php b/src/wp-includes/class-wp-paused-extensions-storage.php deleted file mode 100644 index 37b39d2788..0000000000 --- a/src/wp-includes/class-wp-paused-extensions-storage.php +++ /dev/null @@ -1,221 +0,0 @@ -option_name = $option_name; - $this->meta_prefix = $meta_prefix; - } - - /** - * Records an extension error. - * - * Only one error is stored per extension, with subsequent errors for the same extension overriding the - * previously stored error. - * - * @since 5.1.0 - * - * @param string $extension Plugin or theme directory name. - * @param array $error { - * Error that was triggered. - * - * @type string $type The error type. - * @type string $file The name of the file in which the error occurred. - * @type string $line The line number in which the error occurred. - * @type string $message The error message. - * } - * @return bool True on success, false on failure. - */ - public function record( $extension, $error ) { - if ( ! $this->is_api_loaded() ) { - return false; - } - - if ( is_multisite() && is_site_meta_supported() ) { - // Do not update if the error is already stored. - if ( get_site_meta( get_current_blog_id(), $this->meta_prefix . $extension, true ) === $error ) { - return true; - } - - return (bool) update_site_meta( get_current_blog_id(), $this->meta_prefix . $extension, $error ); - } - - $paused_extensions = $this->get_all(); - - // Do not update if the error is already stored. - if ( isset( $paused_extensions[ $extension ] ) && $paused_extensions[ $extension ] === $error ) { - return true; - } - - $paused_extensions[ $extension ] = $error; - - return update_option( $this->option_name, $paused_extensions ); - } - - /** - * Forgets a previously recorded extension error. - * - * @since 5.1.0 - * - * @param string $extension Plugin or theme directory name. - * @return bool True on success, false on failure. - */ - public function forget( $extension ) { - if ( ! $this->is_api_loaded() ) { - return false; - } - - if ( is_multisite() && is_site_meta_supported() ) { - // Do not delete if no error is stored. - if ( get_site_meta( get_current_blog_id(), $this->meta_prefix . $extension ) === array() ) { - return true; - } - - return (bool) delete_site_meta( get_current_blog_id(), $this->meta_prefix . $extension ); - } - - $paused_extensions = $this->get_all(); - - // Do not delete if no error is stored. - if ( ! isset( $paused_extensions[ $extension ] ) ) { - return true; - } - - // Clean up the entire option if we're removing the only error. - if ( count( $paused_extensions ) === 1 ) { - return delete_option( $this->option_name ); - } - - unset( $paused_extensions[ $extension ] ); - - return update_option( $this->option_name, $paused_extensions ); - } - - /** - * Gets the error for an extension, if paused. - * - * @since 5.1.0 - * - * @param string $extension Plugin or theme directory name. - * @return array|null Error that is stored, or null if the extension is not paused. - */ - public function get( $extension ) { - if ( ! $this->is_api_loaded() ) { - return null; - } - - if ( is_multisite() && is_site_meta_supported() ) { - $error = get_site_meta( get_current_blog_id(), $this->meta_prefix . $extension, true ); - if ( ! $error ) { - return null; - } - - return $error; - } - - $paused_extensions = $this->get_all(); - - if ( ! isset( $paused_extensions[ $extension ] ) ) { - return null; - } - - return $paused_extensions[ $extension ]; - } - - /** - * Gets the paused extensions with their errors. - * - * @since 5.1.0 - * - * @return array Associative array of $extension => $error pairs. - */ - public function get_all() { - if ( ! $this->is_api_loaded() ) { - return array(); - } - - if ( is_multisite() && is_site_meta_supported() ) { - $site_metadata = get_site_meta( get_current_blog_id() ); - - $paused_extensions = array(); - foreach ( $site_metadata as $meta_key => $meta_values ) { - if ( 0 !== strpos( $meta_key, $this->meta_prefix ) ) { - continue; - } - - $error = maybe_unserialize( array_shift( $meta_values ) ); - - $paused_extensions[ substr( $meta_key, strlen( $this->meta_prefix ) ) ] = $error; - } - - return $paused_extensions; - } - - return (array) get_option( $this->option_name, array() ); - } - - /** - * Gets the site meta query clause for querying sites with paused extensions. - * - * @since 5.1.0 - * - * @param string $extension Plugin or theme directory name. - * @return array A single clause to add to a meta query. - */ - public function get_site_meta_query_clause( $extension ) { - return array( - 'key' => $this->meta_prefix . $extension, - 'compare_key' => '=', - ); - } - - /** - * Checks whether the underlying API to store paused extensions is loaded. - * - * @since 5.1.0 - * - * @return bool True if the API is loaded, false otherwise. - */ - protected function is_api_loaded() { - if ( is_multisite() ) { - return function_exists( 'is_site_meta_supported' ) && function_exists( 'get_site_meta' ); - } - - return function_exists( 'get_option' ); - } -} diff --git a/src/wp-includes/class-wp-theme.php b/src/wp-includes/class-wp-theme.php index 0bdd6a43e3..22a5768448 100644 --- a/src/wp-includes/class-wp-theme.php +++ b/src/wp-includes/class-wp-theme.php @@ -371,10 +371,6 @@ final class WP_Theme implements ArrayAccess { $this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this ); } - if ( wp_paused_themes()->get( $this->stylesheet ) && ( ! is_wp_error( $this->errors ) || ! isset( $this->errors->errors['theme_paused'] ) ) ) { - $this->errors = new WP_Error( 'theme_paused', __( 'This theme failed to load properly and was paused within the admin backend.' ) ); - } - // We're good. If we didn't retrieve from cache, set it. if ( ! is_array( $cache ) ) { $cache = array( diff --git a/src/wp-includes/error-protection.php b/src/wp-includes/error-protection.php deleted file mode 100644 index c9b5740960..0000000000 --- a/src/wp-includes/error-protection.php +++ /dev/null @@ -1,199 +0,0 @@ -record( $extension, $error ); -} - -/** - * Forgets a previously recorded extension error again. - * - * @since 5.1.0 - * - * @param string $type Type of the extension. - * @param string $extension Relative path of the extension. - * @param bool $network_wide Optional. Whether to resume the plugin for the entire - * network. Default false. - * @return bool Whether the extension error was successfully forgotten. - */ -function wp_forget_extension_error( $type, $extension, $network_wide = false ) { - switch ( $type ) { - case 'plugins': - $callback = 'wp_paused_plugins'; - list( $extension ) = explode( '/', $extension ); - break; - case 'themes': - $callback = 'wp_paused_themes'; - list( $extension ) = explode( '/', $extension ); - break; - } - - if ( empty( $callback ) || empty( $extension ) ) { - return false; - } - - // Handle manually since the regular APIs do not expose this functionality. - if ( $network_wide && is_site_meta_supported() ) { - $site_meta_query_clause = call_user_func( $callback )->get_site_meta_query_clause( $extension ); - return delete_metadata( 'blog', 0, $site_meta_query_clause['key'], '', true ); - } - - return call_user_func( $callback )->forget( $extension ); -} - -/** - * Determines whether we are dealing with an error that WordPress should handle - * in order to protect the admin backend against WSODs. - * - * @param array $error Error information retrieved from error_get_last(). - * - * @return bool Whether WordPress should handle this error. - */ -function wp_should_handle_error( $error ) { - if ( ! isset( $error['type'] ) ) { - return false; - } - - $error_types_to_handle = array( - E_ERROR, - E_PARSE, - E_USER_ERROR, - E_COMPILE_ERROR, - E_RECOVERABLE_ERROR, - ); - - return in_array( $error['type'], $error_types_to_handle, true ); -} - -/** - * Registers the shutdown handler for fatal errors. - * - * The handler will only be registered if {@see wp_is_fatal_error_handler_enabled()} returns true. - * - * @since 5.1.0 - */ -function wp_register_fatal_error_handler() { - if ( ! wp_is_fatal_error_handler_enabled() ) { - return; - } - - $handler = null; - if ( defined( 'WP_CONTENT_DIR' ) && is_readable( WP_CONTENT_DIR . '/fatal-error-handler.php' ) ) { - $handler = include WP_CONTENT_DIR . '/fatal-error-handler.php'; - } - - if ( ! is_object( $handler ) || ! is_callable( array( $handler, 'handle' ) ) ) { - $handler = new WP_Fatal_Error_Handler(); - } - - register_shutdown_function( array( $handler, 'handle' ) ); -} - -/** - * Checks whether the fatal error handler is enabled. - * - * A constant `WP_DISABLE_FATAL_ERROR_HANDLER` can be set in `wp-config.php` to disable it, or alternatively the - * {@see 'wp_fatal_error_handler_enabled'} filter can be used to modify the return value. - * - * @since 5.1.0 - * - * @return bool True if the fatal error handler is enabled, false otherwise. - */ -function wp_is_fatal_error_handler_enabled() { - $enabled = ! defined( 'WP_DISABLE_FATAL_ERROR_HANDLER' ) || ! WP_DISABLE_FATAL_ERROR_HANDLER; - - /** - * Filters whether the fatal error handler is enabled. - * - * @since 5.1.0 - * - * @param bool $enabled True if the fatal error handler is enabled, false otherwise. - */ - return apply_filters( 'wp_fatal_error_handler_enabled', $enabled ); -} diff --git a/src/wp-includes/load.php b/src/wp-includes/load.php index 7fe5566e7e..245ecb1e61 100644 --- a/src/wp-includes/load.php +++ b/src/wp-includes/load.php @@ -697,43 +697,6 @@ function wp_get_active_and_valid_plugins() { } } - /* - * Remove plugins from the list of active plugins when we're on an endpoint - * that should be protected against WSODs and the plugin is paused. - */ - if ( is_protected_endpoint() ) { - $plugins = wp_skip_paused_plugins( $plugins ); - } - - return $plugins; -} - -/** - * Filters a given list of plugins, removing any paused plugins from it. - * - * @since 5.1.0 - * - * @param array $plugins List of absolute plugin main file paths. - * @return array Filtered value of $plugins, without any paused plugins. - */ -function wp_skip_paused_plugins( array $plugins ) { - $paused_plugins = wp_paused_plugins()->get_all(); - - if ( empty( $paused_plugins ) ) { - return $plugins; - } - - foreach ( $plugins as $index => $plugin ) { - list( $plugin ) = explode( '/', plugin_basename( $plugin ) ); - - if ( array_key_exists( $plugin, $paused_plugins ) ) { - unset( $plugins[ $index ] ); - - // Store list of paused plugins for displaying an admin notice. - $GLOBALS['_paused_plugins'][ $plugin ] = $paused_plugins[ $plugin ]; - } - } - return $plugins; } @@ -762,48 +725,6 @@ function wp_get_active_and_valid_themes() { $themes[] = TEMPLATEPATH; - /* - * Remove themes from the list of active themes when we're on an endpoint - * that should be protected against WSODs and the theme is paused. - */ - if ( is_protected_endpoint() ) { - $themes = wp_skip_paused_themes( $themes ); - - // If no active and valid themes exist, skip loading themes. - if ( empty( $themes ) ) { - add_filter( 'wp_using_themes', '__return_false' ); - } - } - - return $themes; -} - -/** - * Filters a given list of themes, removing any paused themes from it. - * - * @since 5.1.0 - * - * @param array $themes List of absolute theme directory paths. - * @return array Filtered value of $themes, without any paused themes. - */ -function wp_skip_paused_themes( array $themes ) { - $paused_themes = wp_paused_themes()->get_all(); - - if ( empty( $paused_themes ) ) { - return $themes; - } - - foreach ( $themes as $index => $theme ) { - $theme = basename( $theme ); - - if ( array_key_exists( $theme, $paused_themes ) ) { - unset( $themes[ $index ] ); - - // Store list of paused themes for displaying an admin notice. - $GLOBALS['_paused_themes'][ $theme ] = $paused_themes[ $theme ]; - } - } - return $themes; } @@ -1289,88 +1210,6 @@ function wp_using_themes() { return apply_filters( 'wp_using_themes', defined( 'WP_USE_THEMES' ) && WP_USE_THEMES ); } -/** - * Determines whether we are currently on an endpoint that should be protected against WSODs. - * - * @since 5.1.0 - * - * @return bool True if the current endpoint should be protected. - */ -function is_protected_endpoint() { - // Protect login pages. - if ( isset( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) { - return true; - } - - // Protect the admin backend. - if ( is_admin() && ! wp_doing_ajax() ) { - return true; - } - - // Protect AJAX actions that could help resolve a fatal error should be available. - if ( is_protected_ajax_action() ) { - return true; - } - - /** - * Filters whether the current request is against a protected endpoint. - * - * This filter is only fired when an endpoint is requested which is not already protected by - * WordPress core. As such, it exclusively allows providing further protected endpoints in - * addition to the admin backend, login pages and protected AJAX actions. - * - * @since 5.1.0 - * - * @param bool $is_protected_endpoint Whether the currently requested endpoint is protected. Default false. - */ - return (bool) apply_filters( 'is_protected_endpoint', false ); -} - -/** - * Determines whether we are currently handling an AJAX action that should be protected against WSODs. - * - * @since 5.1.0 - * - * @return bool True if the current AJAX action should be protected. - */ -function is_protected_ajax_action() { - if ( ! wp_doing_ajax() ) { - return false; - } - - if ( ! isset( $_REQUEST['action'] ) ) { - return false; - } - - $actions_to_protect = array( - 'edit-theme-plugin-file', // Saving changes in the core code editor. - 'heartbeat', // Keep the heart beating. - 'install-plugin', // Installing a new plugin. - 'install-theme', // Installing a new theme. - 'search-plugins', // Searching in the list of plugins. - 'search-install-plugins', // Searching for a plugin in the plugin install screen. - 'update-plugin', // Update an existing plugin. - 'update-theme', // Update an existing theme. - ); - - /** - * Filters the array of protected AJAX actions. - * - * This filter is only fired when doing AJAX and the AJAX request has an 'action' property. - * - * @since 5.1.0 - * - * @param array $actions_to_protect Array of strings with AJAX actions to protect. - */ - $actions_to_protect = (array) apply_filters( 'wp_protected_ajax_actions', $actions_to_protect ); - - if ( ! in_array( $_REQUEST['action'], $actions_to_protect, true ) ) { - return false; - } - - return true; -} - /** * Determines whether the current request is a WordPress cron request. * diff --git a/src/wp-includes/ms-load.php b/src/wp-includes/ms-load.php index 4f630cce27..91c9c8c301 100644 --- a/src/wp-includes/ms-load.php +++ b/src/wp-includes/ms-load.php @@ -53,14 +53,6 @@ function wp_get_active_network_plugins() { } } - /* - * Remove plugins from the list of active plugins when we're on an endpoint - * that should be protected against WSODs and the plugin is paused. - */ - if ( is_protected_endpoint() ) { - $plugins = wp_skip_paused_plugins( $plugins ); - } - return $plugins; } diff --git a/src/wp-settings.php b/src/wp-settings.php index e48208beb6..b54d6f39d9 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -17,15 +17,9 @@ define( 'WPINC', 'wp-includes' ); // Include files required for initialization. require( ABSPATH . WPINC . '/load.php' ); -require( ABSPATH . WPINC . '/class-wp-paused-extensions-storage.php' ); -require( ABSPATH . WPINC . '/class-wp-fatal-error-handler.php' ); -require( ABSPATH . WPINC . '/error-protection.php' ); require( ABSPATH . WPINC . '/default-constants.php' ); require_once( ABSPATH . WPINC . '/plugin.php' ); -// Make sure we register the shutdown handler for fatal errors as soon as possible. -wp_register_fatal_error_handler(); - /* * These can't be directly globalized in version.php. When updating, * we're including version.php from another installation and don't want @@ -530,12 +524,3 @@ if ( is_multisite() ) { * @since 3.0.0 */ do_action( 'wp_loaded' ); - -/* - * Store the fact that we could successfully execute the entire WordPress - * lifecycle. This is used to skip the premature shutdown handler, as it cannot - * be unregistered. - */ -if ( ! defined( 'WP_EXECUTION_SUCCEEDED' ) ) { - define( 'WP_EXECUTION_SUCCEEDED', true ); -} diff --git a/tests/phpunit/tests/user/capabilities.php b/tests/phpunit/tests/user/capabilities.php index 69742c3972..3bc5264d52 100644 --- a/tests/phpunit/tests/user/capabilities.php +++ b/tests/phpunit/tests/user/capabilities.php @@ -257,7 +257,6 @@ class Tests_User_Capabilities extends WP_UnitTestCase { 'export_others_personal_data' => array( 'administrator' ), 'erase_others_personal_data' => array( 'administrator' ), 'manage_privacy_options' => array( 'administrator' ), - 'resume_themes' => array( 'administrator' ), 'edit_categories' => array( 'administrator', 'editor' ), 'delete_categories' => array( 'administrator', 'editor' ), @@ -297,7 +296,6 @@ class Tests_User_Capabilities extends WP_UnitTestCase { 'customize' => array( 'administrator' ), 'delete_site' => array( 'administrator' ), 'add_users' => array( 'administrator' ), - 'resume_themes' => array( 'administrator' ), 'edit_categories' => array( 'administrator', 'editor' ), 'delete_categories' => array( 'administrator', 'editor' ), @@ -456,7 +454,6 @@ class Tests_User_Capabilities extends WP_UnitTestCase { // Singular object meta capabilities (where an object ID is passed) are not tested: $expected['activate_plugin'], $expected['deactivate_plugin'], - $expected['resume_plugin'], $expected['remove_user'], $expected['promote_user'], $expected['edit_user'],