WordPress/wp-includes/error-protection.php

196 lines
5.2 KiB
PHP
Raw Normal View History

Bootstrap/Load: Introduce fatal error recovery mechanism allowing users to still log in to their admin dashboard. This changeset introduces a `WP_Shutdown_Handler` class that detects fatal errors and which extension (plugin or theme) causes them. Such an error is then recorded, and an error message is displayed. Subsequently, in certain protected areas, for example the admin, the broken extension will be paused, ensuring that the website is still usable in the respective area. The major benefit is that this mechanism allows site owners to still log in to their website, to fix the problem by either disabling the extension or solving the bug and then resuming the extension. Extensions are only paused in certain designated areas. The frontend for example stays unaffected, as it is impossible to know what pausing the extension would cause to be missing, so it might be preferrable to clearly see that the website is temporarily not accessible instead. The fatal error recovery is especially important in scope of encouraging the switch to a maintained PHP version, as not necessarily every WordPress extension is compatible with all PHP versions. If problems occur now, non-technical site owners that do not have immediate access to the codebase are not locked out of their site and can at least temporarily solve the problem quickly. Websites that have custom requirements in that regard can implement their own shutdown handler by adding a `shutdown-handler.php` drop-in that returns the handler instance to use, which must be based on a class that inherits `WP_Shutdown_Handler`. That handler will then be used in place of the default one. Websites that would like to modify specifically the error template displayed in the frontend can add a `php-error.php` drop-in that works similarly to the existing `db-error.php` drop-in. Props afragen, bradleyt, flixos90, ocean90, schlessera, SergeyBiryukov, spacedmonkey. Fixes #44458. Built from https://develop.svn.wordpress.org/trunk@44524 git-svn-id: http://core.svn.wordpress.org/trunk@44355 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2019-01-09 21:05:49 +01:00
<?php
/**
* Error Protection API: Functions
*
* @package WordPress
* @since 5.1.0
*/
/**
* Gets the instance for storing paused plugins.
*
* @since 5.1.0
*
* @return WP_Paused_Extensions_Storage Paused plugins storage.
*/
function wp_paused_plugins() {
static $wp_paused_plugins_storage = null;
if ( null === $wp_paused_plugins_storage ) {
$wp_paused_plugins_storage = new WP_Paused_Extensions_Storage( 'paused_plugins', 'paused_plugin_' );
}
return $wp_paused_plugins_storage;
}
/**
* Gets the instance for storing paused themes.
*
* @since 5.1.0
*
* @return WP_Paused_Extensions_Storage Paused themes storage.
*/
function wp_paused_themes() {
static $wp_paused_themes_storage = null;
if ( null === $wp_paused_themes_storage ) {
$wp_paused_themes_storage = new WP_Paused_Extensions_Storage( 'paused_themes', 'paused_theme_' );
}
return $wp_paused_themes_storage;
}
/**
* Records the extension error as a database option.
*
* @since 5.1.0
*
* @global array $wp_theme_directories
*
* @param array $error Error that was triggered.
* @return bool Whether the error was correctly recorded.
*/
function wp_record_extension_error( $error ) {
global $wp_theme_directories;
if ( ! isset( $error['file'] ) ) {
return false;
}
if ( ! defined( 'WP_PLUGIN_DIR' ) ) {
return false;
}
$error_file = wp_normalize_path( $error['file'] );
$wp_plugin_dir = wp_normalize_path( WP_PLUGIN_DIR );
if ( 0 === strpos( $error_file, $wp_plugin_dir ) ) {
$callback = 'wp_paused_plugins';
$path = str_replace( $wp_plugin_dir . '/', '', $error_file );
} else {
foreach ( $wp_theme_directories as $theme_directory ) {
$theme_directory = wp_normalize_path( $theme_directory );
if ( 0 === strpos( $error_file, $theme_directory ) ) {
$callback = 'wp_paused_themes';
$path = str_replace( $theme_directory . '/', '', $error_file );
}
}
}
if ( empty( $callback ) || empty( $path ) ) {
return false;
}
$parts = explode( '/', $path );
$extension = array_shift( $parts );
return call_user_func( $callback )->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 );
}
/**
Bootstrap/Load: Change `shutdown handler` naming to final `fatal error handler` and allow disabling the handler entirely via a constant. The `WP_Shutdown_Handler` name plus related function names were premature when originally committed, as there can be multiple shutdown handlers in PHP, and WordPress makes use of that feature. This changeset modifies the name to a more appropriate `WP_Fatal_Error_Handler`, and related to that changes the following names: * The drop-in to override the handler is now called `fatal-error-handler.php`. * The internal function `wp_register_premature_shutdown_handler` is now called `wp_register_fatal_error_handler()`. In addition to these naming changes, a new constant `WP_DISABLE_FATAL_ERROR_HANDLER` is introduced that can be set in `wp-config.php` to entirely disable the fatal error handler. That constant's value is and should be accessed indirectly via a new `wp_is_fatal_error_handler_enabled()` function and is filterable via a new `wp_fatal_error_handler_enabled` hook. Note that disabling the fatal error handler will skip the new functionality entirely, including the potentially used `fatal-error-handler.php` drop-in. The new set of constant, filter and function provide for an easier-to-use mechanism to disable the fatal error handler altogether, rather than requiring developers to implement a drop-in for purely that purpose. Props afragen, flixos90, joyously, knutsp, markjaquith, ocean90, schlessera, spacedmonkey. Fixes #46047. See #44458. Built from https://develop.svn.wordpress.org/trunk@44674 git-svn-id: http://core.svn.wordpress.org/trunk@44505 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2019-01-21 21:15:50 +01:00
* Registers the shutdown handler for fatal errors.
*
* The handler will only be registered if {@see wp_is_fatal_error_handler_enabled()} returns true.
Bootstrap/Load: Introduce fatal error recovery mechanism allowing users to still log in to their admin dashboard. This changeset introduces a `WP_Shutdown_Handler` class that detects fatal errors and which extension (plugin or theme) causes them. Such an error is then recorded, and an error message is displayed. Subsequently, in certain protected areas, for example the admin, the broken extension will be paused, ensuring that the website is still usable in the respective area. The major benefit is that this mechanism allows site owners to still log in to their website, to fix the problem by either disabling the extension or solving the bug and then resuming the extension. Extensions are only paused in certain designated areas. The frontend for example stays unaffected, as it is impossible to know what pausing the extension would cause to be missing, so it might be preferrable to clearly see that the website is temporarily not accessible instead. The fatal error recovery is especially important in scope of encouraging the switch to a maintained PHP version, as not necessarily every WordPress extension is compatible with all PHP versions. If problems occur now, non-technical site owners that do not have immediate access to the codebase are not locked out of their site and can at least temporarily solve the problem quickly. Websites that have custom requirements in that regard can implement their own shutdown handler by adding a `shutdown-handler.php` drop-in that returns the handler instance to use, which must be based on a class that inherits `WP_Shutdown_Handler`. That handler will then be used in place of the default one. Websites that would like to modify specifically the error template displayed in the frontend can add a `php-error.php` drop-in that works similarly to the existing `db-error.php` drop-in. Props afragen, bradleyt, flixos90, ocean90, schlessera, SergeyBiryukov, spacedmonkey. Fixes #44458. Built from https://develop.svn.wordpress.org/trunk@44524 git-svn-id: http://core.svn.wordpress.org/trunk@44355 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2019-01-09 21:05:49 +01:00
*
* @since 5.1.0
*/
Bootstrap/Load: Change `shutdown handler` naming to final `fatal error handler` and allow disabling the handler entirely via a constant. The `WP_Shutdown_Handler` name plus related function names were premature when originally committed, as there can be multiple shutdown handlers in PHP, and WordPress makes use of that feature. This changeset modifies the name to a more appropriate `WP_Fatal_Error_Handler`, and related to that changes the following names: * The drop-in to override the handler is now called `fatal-error-handler.php`. * The internal function `wp_register_premature_shutdown_handler` is now called `wp_register_fatal_error_handler()`. In addition to these naming changes, a new constant `WP_DISABLE_FATAL_ERROR_HANDLER` is introduced that can be set in `wp-config.php` to entirely disable the fatal error handler. That constant's value is and should be accessed indirectly via a new `wp_is_fatal_error_handler_enabled()` function and is filterable via a new `wp_fatal_error_handler_enabled` hook. Note that disabling the fatal error handler will skip the new functionality entirely, including the potentially used `fatal-error-handler.php` drop-in. The new set of constant, filter and function provide for an easier-to-use mechanism to disable the fatal error handler altogether, rather than requiring developers to implement a drop-in for purely that purpose. Props afragen, flixos90, joyously, knutsp, markjaquith, ocean90, schlessera, spacedmonkey. Fixes #46047. See #44458. Built from https://develop.svn.wordpress.org/trunk@44674 git-svn-id: http://core.svn.wordpress.org/trunk@44505 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2019-01-21 21:15:50 +01:00
function wp_register_fatal_error_handler() {
if ( ! wp_is_fatal_error_handler_enabled() ) {
return;
}
Bootstrap/Load: Introduce fatal error recovery mechanism allowing users to still log in to their admin dashboard. This changeset introduces a `WP_Shutdown_Handler` class that detects fatal errors and which extension (plugin or theme) causes them. Such an error is then recorded, and an error message is displayed. Subsequently, in certain protected areas, for example the admin, the broken extension will be paused, ensuring that the website is still usable in the respective area. The major benefit is that this mechanism allows site owners to still log in to their website, to fix the problem by either disabling the extension or solving the bug and then resuming the extension. Extensions are only paused in certain designated areas. The frontend for example stays unaffected, as it is impossible to know what pausing the extension would cause to be missing, so it might be preferrable to clearly see that the website is temporarily not accessible instead. The fatal error recovery is especially important in scope of encouraging the switch to a maintained PHP version, as not necessarily every WordPress extension is compatible with all PHP versions. If problems occur now, non-technical site owners that do not have immediate access to the codebase are not locked out of their site and can at least temporarily solve the problem quickly. Websites that have custom requirements in that regard can implement their own shutdown handler by adding a `shutdown-handler.php` drop-in that returns the handler instance to use, which must be based on a class that inherits `WP_Shutdown_Handler`. That handler will then be used in place of the default one. Websites that would like to modify specifically the error template displayed in the frontend can add a `php-error.php` drop-in that works similarly to the existing `db-error.php` drop-in. Props afragen, bradleyt, flixos90, ocean90, schlessera, SergeyBiryukov, spacedmonkey. Fixes #44458. Built from https://develop.svn.wordpress.org/trunk@44524 git-svn-id: http://core.svn.wordpress.org/trunk@44355 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2019-01-09 21:05:49 +01:00
$handler = null;
Bootstrap/Load: Change `shutdown handler` naming to final `fatal error handler` and allow disabling the handler entirely via a constant. The `WP_Shutdown_Handler` name plus related function names were premature when originally committed, as there can be multiple shutdown handlers in PHP, and WordPress makes use of that feature. This changeset modifies the name to a more appropriate `WP_Fatal_Error_Handler`, and related to that changes the following names: * The drop-in to override the handler is now called `fatal-error-handler.php`. * The internal function `wp_register_premature_shutdown_handler` is now called `wp_register_fatal_error_handler()`. In addition to these naming changes, a new constant `WP_DISABLE_FATAL_ERROR_HANDLER` is introduced that can be set in `wp-config.php` to entirely disable the fatal error handler. That constant's value is and should be accessed indirectly via a new `wp_is_fatal_error_handler_enabled()` function and is filterable via a new `wp_fatal_error_handler_enabled` hook. Note that disabling the fatal error handler will skip the new functionality entirely, including the potentially used `fatal-error-handler.php` drop-in. The new set of constant, filter and function provide for an easier-to-use mechanism to disable the fatal error handler altogether, rather than requiring developers to implement a drop-in for purely that purpose. Props afragen, flixos90, joyously, knutsp, markjaquith, ocean90, schlessera, spacedmonkey. Fixes #46047. See #44458. Built from https://develop.svn.wordpress.org/trunk@44674 git-svn-id: http://core.svn.wordpress.org/trunk@44505 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2019-01-21 21:15:50 +01:00
if ( defined( 'WP_CONTENT_DIR' ) && is_readable( WP_CONTENT_DIR . '/fatal-error-handler.php' ) ) {
$handler = include WP_CONTENT_DIR . '/fatal-error-handler.php';
Bootstrap/Load: Introduce fatal error recovery mechanism allowing users to still log in to their admin dashboard. This changeset introduces a `WP_Shutdown_Handler` class that detects fatal errors and which extension (plugin or theme) causes them. Such an error is then recorded, and an error message is displayed. Subsequently, in certain protected areas, for example the admin, the broken extension will be paused, ensuring that the website is still usable in the respective area. The major benefit is that this mechanism allows site owners to still log in to their website, to fix the problem by either disabling the extension or solving the bug and then resuming the extension. Extensions are only paused in certain designated areas. The frontend for example stays unaffected, as it is impossible to know what pausing the extension would cause to be missing, so it might be preferrable to clearly see that the website is temporarily not accessible instead. The fatal error recovery is especially important in scope of encouraging the switch to a maintained PHP version, as not necessarily every WordPress extension is compatible with all PHP versions. If problems occur now, non-technical site owners that do not have immediate access to the codebase are not locked out of their site and can at least temporarily solve the problem quickly. Websites that have custom requirements in that regard can implement their own shutdown handler by adding a `shutdown-handler.php` drop-in that returns the handler instance to use, which must be based on a class that inherits `WP_Shutdown_Handler`. That handler will then be used in place of the default one. Websites that would like to modify specifically the error template displayed in the frontend can add a `php-error.php` drop-in that works similarly to the existing `db-error.php` drop-in. Props afragen, bradleyt, flixos90, ocean90, schlessera, SergeyBiryukov, spacedmonkey. Fixes #44458. Built from https://develop.svn.wordpress.org/trunk@44524 git-svn-id: http://core.svn.wordpress.org/trunk@44355 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2019-01-09 21:05:49 +01:00
}
if ( ! is_object( $handler ) || ! is_callable( array( $handler, 'handle' ) ) ) {
Bootstrap/Load: Change `shutdown handler` naming to final `fatal error handler` and allow disabling the handler entirely via a constant. The `WP_Shutdown_Handler` name plus related function names were premature when originally committed, as there can be multiple shutdown handlers in PHP, and WordPress makes use of that feature. This changeset modifies the name to a more appropriate `WP_Fatal_Error_Handler`, and related to that changes the following names: * The drop-in to override the handler is now called `fatal-error-handler.php`. * The internal function `wp_register_premature_shutdown_handler` is now called `wp_register_fatal_error_handler()`. In addition to these naming changes, a new constant `WP_DISABLE_FATAL_ERROR_HANDLER` is introduced that can be set in `wp-config.php` to entirely disable the fatal error handler. That constant's value is and should be accessed indirectly via a new `wp_is_fatal_error_handler_enabled()` function and is filterable via a new `wp_fatal_error_handler_enabled` hook. Note that disabling the fatal error handler will skip the new functionality entirely, including the potentially used `fatal-error-handler.php` drop-in. The new set of constant, filter and function provide for an easier-to-use mechanism to disable the fatal error handler altogether, rather than requiring developers to implement a drop-in for purely that purpose. Props afragen, flixos90, joyously, knutsp, markjaquith, ocean90, schlessera, spacedmonkey. Fixes #46047. See #44458. Built from https://develop.svn.wordpress.org/trunk@44674 git-svn-id: http://core.svn.wordpress.org/trunk@44505 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2019-01-21 21:15:50 +01:00
$handler = new WP_Fatal_Error_Handler();
Bootstrap/Load: Introduce fatal error recovery mechanism allowing users to still log in to their admin dashboard. This changeset introduces a `WP_Shutdown_Handler` class that detects fatal errors and which extension (plugin or theme) causes them. Such an error is then recorded, and an error message is displayed. Subsequently, in certain protected areas, for example the admin, the broken extension will be paused, ensuring that the website is still usable in the respective area. The major benefit is that this mechanism allows site owners to still log in to their website, to fix the problem by either disabling the extension or solving the bug and then resuming the extension. Extensions are only paused in certain designated areas. The frontend for example stays unaffected, as it is impossible to know what pausing the extension would cause to be missing, so it might be preferrable to clearly see that the website is temporarily not accessible instead. The fatal error recovery is especially important in scope of encouraging the switch to a maintained PHP version, as not necessarily every WordPress extension is compatible with all PHP versions. If problems occur now, non-technical site owners that do not have immediate access to the codebase are not locked out of their site and can at least temporarily solve the problem quickly. Websites that have custom requirements in that regard can implement their own shutdown handler by adding a `shutdown-handler.php` drop-in that returns the handler instance to use, which must be based on a class that inherits `WP_Shutdown_Handler`. That handler will then be used in place of the default one. Websites that would like to modify specifically the error template displayed in the frontend can add a `php-error.php` drop-in that works similarly to the existing `db-error.php` drop-in. Props afragen, bradleyt, flixos90, ocean90, schlessera, SergeyBiryukov, spacedmonkey. Fixes #44458. Built from https://develop.svn.wordpress.org/trunk@44524 git-svn-id: http://core.svn.wordpress.org/trunk@44355 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2019-01-09 21:05:49 +01:00
}
register_shutdown_function( array( $handler, 'handle' ) );
}
Bootstrap/Load: Change `shutdown handler` naming to final `fatal error handler` and allow disabling the handler entirely via a constant. The `WP_Shutdown_Handler` name plus related function names were premature when originally committed, as there can be multiple shutdown handlers in PHP, and WordPress makes use of that feature. This changeset modifies the name to a more appropriate `WP_Fatal_Error_Handler`, and related to that changes the following names: * The drop-in to override the handler is now called `fatal-error-handler.php`. * The internal function `wp_register_premature_shutdown_handler` is now called `wp_register_fatal_error_handler()`. In addition to these naming changes, a new constant `WP_DISABLE_FATAL_ERROR_HANDLER` is introduced that can be set in `wp-config.php` to entirely disable the fatal error handler. That constant's value is and should be accessed indirectly via a new `wp_is_fatal_error_handler_enabled()` function and is filterable via a new `wp_fatal_error_handler_enabled` hook. Note that disabling the fatal error handler will skip the new functionality entirely, including the potentially used `fatal-error-handler.php` drop-in. The new set of constant, filter and function provide for an easier-to-use mechanism to disable the fatal error handler altogether, rather than requiring developers to implement a drop-in for purely that purpose. Props afragen, flixos90, joyously, knutsp, markjaquith, ocean90, schlessera, spacedmonkey. Fixes #46047. See #44458. Built from https://develop.svn.wordpress.org/trunk@44674 git-svn-id: http://core.svn.wordpress.org/trunk@44505 1a063a9b-81f0-0310-95a4-ce76da25c4cd
2019-01-21 21:15:50 +01:00
/**
* 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 );
}