WP_Fatal_Error_Handler
云策文档标注
概述
WP_Fatal_Error_Handler 是 WordPress 核心类,用作致命错误的默认关闭处理器。它允许通过 drop-in 文件进行自定义扩展,以处理 PHP 致命错误并防止管理后台出现白屏死机(WSOD)。
关键要点
- WP_Fatal_Error_Handler 是 WordPress 5.2.0 引入的默认致命错误处理器,通过 register_shutdown_function() 注册。
- 支持自定义实现:可通过创建 'fatal-error-handler.php' drop-in 文件来覆盖此类,自定义类需继承 WP_Fatal_Error_Handler 并可重写其方法。
- 主要方法包括 handle()(运行关闭处理器)、detect_error()(检测错误)、should_handle_error()(判断是否处理错误)、display_error_template()(显示错误模板)和 display_default_error_template()(显示默认错误模板)。
- 错误处理逻辑:在非维护模式下,检测特定错误类型(如 E_ERROR、E_PARSE 等),可触发恢复模式,并根据情况显示自定义或默认错误模板。
- 提供过滤器:wp_should_handle_php_error 用于添加错误处理规则,wp_php_error_message 和 wp_php_error_args 用于修改默认错误消息和参数。
代码示例
// 示例:自定义 fatal-error-handler.php 文件
if ( ! class_exists( 'WP_Fatal_Error_Handler' ) ) {
require_once ABSPATH . WPINC . '/class-wp-fatal-error-handler.php';
}
class My_Custom_Fatal_Error_Handler extends WP_Fatal_Error_Handler {
public function handle() {
// 自定义处理逻辑
if ( defined( 'MY_CUSTOM_CONDITION' ) && MY_CUSTOM_CONDITION ) {
return;
}
parent::handle();
}
}
return new My_Custom_Fatal_Error_Handler();注意事项
- 自定义 drop-in 文件必须返回类实例,且文件应放置在 WordPress 内容目录(如 wp-content)中。
- display_error_template() 方法会优先加载自定义 'php-error.php' drop-in 模板,若无则调用默认模板。
- 在早期引导阶段,使用核心函数前需检查其可用性,避免因依赖未加载而引发错误。
原文内容
Core class used as the default shutdown handler for fatal errors.
Description
A drop-in ‘fatal-error-handler.php’ can be used to override the instance of this class and use a custom implementation for the fatal error handler that WordPress registers. The custom class should extend this class and can override its methods individually as necessary. The file must return the instance of the class that should be registered.
Methods
| Name | Description |
|---|---|
| WP_Fatal_Error_Handler::detect_error | Detects the error causing the crash if it should be handled. |
| WP_Fatal_Error_Handler::display_default_error_template | Displays the default PHP error template. |
| WP_Fatal_Error_Handler::display_error_template | Displays the PHP error template and sends the HTTP status code, typically 500. |
| WP_Fatal_Error_Handler::handle | Runs the shutdown handler. |
| WP_Fatal_Error_Handler::should_handle_error | Determines whether we are dealing with an error that WordPress should handle in order to protect the admin backend against WSODs. |
Source
class WP_Fatal_Error_Handler {
/**
* Runs the shutdown handler.
*
* This method is registered via `register_shutdown_function()`.
*
* @since 5.2.0
*
* @global WP_Locale $wp_locale WordPress date and time locale object.
*/
public function handle() {
if ( defined( 'WP_SANDBOX_SCRAPING' ) && WP_SANDBOX_SCRAPING ) {
return;
}
// Do not trigger the fatal error handler while updates are being installed.
if ( wp_is_maintenance_mode() ) {
return;
}
try {
// Bail if no error found.
$error = $this->detect_error();
if ( ! $error ) {
return;
}
if ( ! isset( $GLOBALS['wp_locale'] ) && function_exists( 'load_default_textdomain' ) ) {
load_default_textdomain();
}
$handled = false;
if ( ! is_multisite() && wp_recovery_mode()->is_initialized() ) {
$handled = wp_recovery_mode()->handle_error( $error );
}
// Display the PHP error template if headers not sent.
if ( is_admin() || ! headers_sent() ) {
$this->display_error_template( $error, $handled );
}
} catch ( Exception $e ) {
// Catch exceptions and remain silent.
}
}
/**
* Detects the error causing the crash if it should be handled.
*
* @since 5.2.0
*
* @return array|null Error information returned by `error_get_last()`, or null
* if none was recorded or 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 ( ! $this->should_handle_error( $error ) ) {
return null;
}
return $error;
}
/**
* Determines whether we are dealing with an error that WordPress should handle
* in order to protect the admin backend against WSODs.
*
* @since 5.2.0
*
* @param array $error Error information retrieved from `error_get_last()`.
* @return bool Whether WordPress should handle this error.
*/
protected function should_handle_error( $error ) {
$error_types_to_handle = array(
E_ERROR,
E_PARSE,
E_USER_ERROR,
E_COMPILE_ERROR,
E_RECOVERABLE_ERROR,
);
if ( isset( $error['type'] ) && in_array( $error['type'], $error_types_to_handle, true ) ) {
return true;
}
/**
* Filters whether a given thrown error should be handled by the fatal error handler.
*
* This filter is only fired if the error is not already configured to be handled by WordPress core. As such,
* it exclusively allows adding further rules for which errors should be handled, but not removing existing
* ones.
*
* @since 5.2.0
*
* @param bool $should_handle_error Whether the error should be handled by the fatal error handler.
* @param array $error Error information retrieved from `error_get_last()`.
*/
return (bool) apply_filters( 'wp_should_handle_php_error', false, $error );
}
/**
* 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 <a href="https://developer.wordpress.org/reference/classes/WP_Fatal_Error_Handler/display_default_error_template/">WP_Fatal_Error_Handler::display_default_error_template()</a>.
*
* @since 5.2.0
* @since 5.3.0 The `$handled` parameter was added.
*
* @param array $error Error information retrieved from `error_get_last()`.
* @param true|WP_Error $handled Whether Recovery Mode handled the fatal error.
*/
protected function display_error_template( $error, $handled ) {
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( $error, $handled );
}
/**
* Displays the default PHP error template.
*
* This method is called conditionally if no 'php-error.php' drop-in is available.
*
* It calls <a href="https://developer.wordpress.org/reference/functions/wp_die/">wp_die()</a> with a message indicating that the site is experiencing technical difficulties and a
* login link to the admin backend. The 'wp_php_error_message' and 'wp_php_error_args' filters can
* be used to modify these parameters.
*
* @since 5.2.0
* @since 5.3.0 The `$handled` parameter was added.
*
* @param array $error Error information retrieved from `error_get_last()`.
* @param true|WP_Error $handled Whether Recovery Mode handled the fatal error.
*/
protected function display_default_error_template( $error, $handled ) {
if ( ! function_exists( '__' ) ) {
wp_load_translations_early();
}
if ( ! function_exists( 'wp_die' ) ) {
require_once ABSPATH . WPINC . '/functions.php';
}
if ( ! class_exists( 'WP_Error' ) ) {
require_once ABSPATH . WPINC . '/class-wp-error.php';
}
if ( true === $handled && wp_is_recovery_mode() ) {
$message = __( 'There has been a critical error on this website, putting it in recovery mode. Please check the Themes and Plugins screens for more details. If you just installed or updated a theme or plugin, check the relevant page for that first.' );
} elseif ( is_protected_endpoint() && wp_recovery_mode()->is_initialized() ) {
if ( is_multisite() ) {
$message = __( 'There has been a critical error on this website. Please reach out to your site administrator, and inform them of this error for further assistance.' );
} else {
$message = sprintf(
/* translators: %s: Support forums URL. */
__( 'There has been a critical error on this website. Please check your site admin email inbox for instructions. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
__( 'https://wordpress.org/support/forums/' )
);
}
} else {
$message = __( 'There has been a critical error on this website.' );
}
$message = sprintf(
'<p>%s</p><p><a href="%s">%s</a></p>',
$message,
/* translators: Documentation about troubleshooting. */
__( 'https://wordpress.org/documentation/article/faq-troubleshooting/' ),
__( 'Learn more about troubleshooting WordPress.' )
);
$args = array(
'response' => 500,
'exit' => false,
);
/**
* Filters the message that the default PHP error template displays.
*
* @since 5.2.0
*
* @param string $message HTML error message to display.
* @param array $error Error information retrieved from `error_get_last()`.
*/
$message = apply_filters( 'wp_php_error_message', $message, $error );
/**
* Filters the arguments passed to <a href="https://developer.wordpress.org/reference/functions/wp_die/">wp_die()</a> for the default PHP error template.
*
* @since 5.2.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.
* @param array $error Error information retrieved from `error_get_last()`.
*/
$args = apply_filters( 'wp_php_error_args', $args, $error );
$wp_error = new WP_Error(
'internal_server_error',
$message,
array(
'error' => $error,
)
);
wp_die( $wp_error, '', $args );
}
}
Changelog
| Version | Description |
|---|---|
| 5.2.0 | Introduced. |