各位码友们,晚上好!欢迎来到“WordPress底层探秘”小课堂。今晚咱们要聊聊WordPress里一个“一言不合就罢工”的函数——wp_die()
,以及它背后的“背锅侠”——wp_die_handler
过滤器。
如果你写WordPress插件或者主题的时候遇到过“啊!页面白屏了,只显示一堆错误信息!”的场景,那么你肯定跟 wp_die()
打过交道。这玩意儿就像个紧急刹车,一旦触发,WordPress就会立刻停止执行,并显示错误信息。但别怕,它不是个蛮不讲理的家伙,它给了我们一个机会,通过 wp_die_handler
过滤器,来定制我们自己的错误页面,让用户体验更上一层楼。
废话不多说,咱们直接上代码,深入了解一下 wp_die()
的源码:
/**
* Kills WordPress execution and displays an HTML page with an error message.
*
* If `$title` is empty, a generic title is used.
*
* The error message is HTML-encoded for safe usage in HTML page templates.
*
* This function should be called when WordPress is unable to continue running
* due to an error.
*
* @since 2.0.0
*
* @global WP_Error $wp_error WordPress error object.
*
* @param string|WP_Error $message Error message. May contain HTML.
* @param string $title Optional. Error title. Default empty.
* @param array|string $args Optional. Array or string of arguments. See wp_die().
* @return void
*/
function wp_die( $message, $title = '', $args = array() ) {
global $wp_error;
$defaults = array(
'response' => 500,
'code' => '',
'heading' => '',
'exit' => true,
'back_link' => false,
);
$args = wp_parse_args( $args, $defaults );
$response = $args['response'];
// Use WP_Error, if possible.
if ( is_wp_error( $message ) ) {
if ( empty( $title ) ) {
$errors = $message->get_error_messages();
if ( empty( $errors ) ) {
$title = __( 'WordPress › Error' );
} else {
$title = sprintf( __( 'WordPress › Error: %s' ), $message->get_error_code() );
}
}
$errors = $message->get_error_messages();
if ( ! empty( $errors ) ) {
$message = '<ul><li>' . implode( '</li><li>', $errors ) . '</li></ul>';
}
} elseif ( is_string( $message ) ) {
$message = sprintf( '<p>%s</p>', $message );
}
if ( defined( 'XMLRPC_REQUEST' ) || defined( 'REST_REQUEST' ) ) {
wp_die( $message, $title, array_merge( $args, array( 'exit' => false ) ) ); // Recursive call.
}
if ( function_exists( 'is_admin' ) ) {
if ( is_admin() ) {
if ( did_action( 'admin_enqueue_scripts' ) ) {
wp_enqueue_style( 'wp-admin-css' );
wp_enqueue_style( 'colors-fresh' );
} else {
add_action(
'admin_enqueue_scripts',
function() {
wp_enqueue_style( 'wp-admin-css' );
wp_enqueue_style( 'colors-fresh' );
}
);
}
}
}
$have_gettext = function_exists( '__' );
if ( ! empty( $title ) ) {
$title = strip_tags( $title );
if ( $have_gettext ) {
$title = __( $title );
}
}
$heading = $args['heading'];
if ( empty( $heading ) ) {
$heading = $title;
}
if ( ! did_action( 'wp_die_handler' ) ) {
if ( WP_DEBUG && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) && apply_filters( 'wp_die_ajax_backcompat', true ) ) {
/**
* Fires before outputting an error message.
*
* @since 2.0.0
*/
do_action( 'admin_head' );
if ( 'E_ALL' === error_reporting() ) {
$error_reporting_string = __( 'E_ALL' );
} else {
$error_reporting_string = error_reporting();
}
$message = sprintf(
'<div class="error"><p>%s</p><p>%s</p><p>%s</p></div>',
__( 'WordPress has encountered a problem.' ),
sprintf(
/* translators: 1: Documentation URL, 2: support forums URL */
__( 'For more information, visit the <a href="%1$s">Debugging in WordPress</a> page or the <a href="%2$s">WordPress Support Forums</a>.' ),
__( 'https://wordpress.org/documentation/article/debugging-in-wordpress/' ),
__( 'https://wordpress.org/support/forums/' )
),
sprintf(
/* translators: %s: error reporting value */
__( 'Error reporting has been turned on. %s' ),
sprintf( '<code>%s</code>', $error_reporting_string )
)
) . $message;
}
/**
* Filters the callback used to handle calls to wp_die().
*
* @since 2.0.0
*
* @param callable $callback The callback function used to handle calls to wp_die().
* Default 'wp_default_die_handler'.
*/
$handler = apply_filters( 'wp_die_handler', '_default_wp_die_handler' );
call_user_func( $handler, $message, $title, $args );
}
if ( $args['exit'] ) {
die();
}
}
咱们来逐行分析一下这个“刹车”是怎么工作的:
-
函数签名:
function wp_die( $message, $title = '', $args = array() )
$message
: 错误信息,可以是字符串或者WP_Error
对象。 这是必填的,告诉用户哪里出错了。$title
: 错误页面的标题,可选。 如果为空,会使用默认标题。$args
: 一个数组,包含一些额外的参数,可选。 稍后会详细介绍。
-
默认参数:
$defaults = array( 'response' => 500, 'code' => '', 'heading' => '', 'exit' => true, 'back_link' => false, );
wp_die()
函数允许我们通过$args
数组传递一些参数来控制错误页面的行为。默认参数如下:参数名 默认值 说明 response
500
HTTP 响应码。 500 表示服务器内部错误。你可以改成其他合适的响应码,例如 403 表示禁止访问,404 表示未找到。 code
''
错误代码,字符串类型,用于标识错误的类型。例如 ‘invalid_username’。这个参数并没有被WordPress核心直接使用,但是可以在自定义的错误处理函数中使用。 heading
''
错误页面的标题。 如果为空,会使用 $title
参数的值。exit
true
是否立即停止执行脚本。 如果设置为 false
,wp_die()
会执行错误处理逻辑,但不会调用die()
函数停止脚本。这在某些特殊情况下很有用。back_link
false
是否显示一个返回链接。 如果设置为 true
,会显示一个返回到前一页的链接。 -
处理
WP_Error
对象:if ( is_wp_error( $message ) ) { if ( empty( $title ) ) { $errors = $message->get_error_messages(); if ( empty( $errors ) ) { $title = __( 'WordPress › Error' ); } else { $title = sprintf( __( 'WordPress › Error: %s' ), $message->get_error_code() ); } } $errors = $message->get_error_messages(); if ( ! empty( $errors ) ) { $message = '<ul><li>' . implode( '</li><li>', $errors ) . '</li></ul>'; } } elseif ( is_string( $message ) ) { $message = sprintf( '<p>%s</p>', $message ); }
如果
$message
是一个WP_Error
对象,代码会尝试从WP_Error
对象中提取错误信息和错误代码,并将其格式化为 HTML。 如果$message
是一个字符串,会将其包装在一个<p>
标签中。 -
处理 XMLRPC 和 REST 请求:
if ( defined( 'XMLRPC_REQUEST' ) || defined( 'REST_REQUEST' ) ) { wp_die( $message, $title, array_merge( $args, array( 'exit' => false ) ) ); // Recursive call. }
如果当前是一个 XMLRPC 或者 REST 请求,
wp_die()
会被递归调用,并将exit
参数设置为false
。 这是为了防止在 API 请求中直接调用die()
函数,而是将错误信息返回给客户端。 -
加载 Admin CSS:
if ( function_exists( 'is_admin' ) ) { if ( is_admin() ) { if ( did_action( 'admin_enqueue_scripts' ) ) { wp_enqueue_style( 'wp-admin-css' ); wp_enqueue_style( 'colors-fresh' ); } else { add_action( 'admin_enqueue_scripts', function() { wp_enqueue_style( 'wp-admin-css' ); wp_enqueue_style( 'colors-fresh' ); } ); } } }
如果在后台管理界面,代码会尝试加载 WordPress 的后台 CSS 样式,以保证错误页面看起来更美观。
-
处理标题:
if ( ! empty( $title ) ) { $title = strip_tags( $title ); if ( $have_gettext ) { $title = __( $title ); } } $heading = $args['heading']; if ( empty( $heading ) ) { $heading = $title; }
代码会对标题进行一些处理,例如去除 HTML 标签,进行本地化等等。 如果
$args['heading']
参数为空,会使用$title
参数的值作为标题。 -
关键:
wp_die_handler
过滤器:if ( ! did_action( 'wp_die_handler' ) ) { if ( WP_DEBUG && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) && apply_filters( 'wp_die_ajax_backcompat', true ) ) { /** * Fires before outputting an error message. * * @since 2.0.0 */ do_action( 'admin_head' ); if ( 'E_ALL' === error_reporting() ) { $error_reporting_string = __( 'E_ALL' ); } else { $error_reporting_string = error_reporting(); } $message = sprintf( '<div class="error"><p>%s</p><p>%s</p><p>%s</p></div>', __( 'WordPress has encountered a problem.' ), sprintf( /* translators: 1: Documentation URL, 2: support forums URL */ __( 'For more information, visit the <a href="%1$s">Debugging in WordPress</a> page or the <a href="%2$s">WordPress Support Forums</a>.' ), __( 'https://wordpress.org/documentation/article/debugging-in-wordpress/' ), __( 'https://wordpress.org/support/forums/' ) ), sprintf( /* translators: %s: error reporting value */ __( 'Error reporting has been turned on. %s' ), sprintf( '<code>%s</code>', $error_reporting_string ) ) ) . $message; } /** * Filters the callback used to handle calls to wp_die(). * * @since 2.0.0 * * @param callable $callback The callback function used to handle calls to wp_die(). * Default 'wp_default_die_handler'. */ $handler = apply_filters( 'wp_die_handler', '_default_wp_die_handler' ); call_user_func( $handler, $message, $title, $args ); }
这部分代码是
wp_die()
函数的核心。它使用apply_filters( 'wp_die_handler', '_default_wp_die_handler' )
来获取一个用于处理错误信息的函数。 默认情况下,这个函数是_default_wp_die_handler
。但是,我们可以使用wp_die_handler
过滤器来替换这个函数,从而自定义错误页面的显示方式。call_user_func( $handler, $message, $title, $args )
这行代码会调用我们指定的错误处理函数,并将错误信息、标题和参数传递给它。 -
停止执行:
if ( $args['exit'] ) { die(); }
如果
$args['exit']
参数为true
(默认值),代码会调用die()
函数来立即停止执行脚本。
_default_wp_die_handler()
函数:
默认的错误处理函数是 _default_wp_die_handler()
,它的代码如下:
/**
* Default handler for wp_die().
*
* It exits with a status code of 500.
*
* @since 4.7.0
* @access private
*
* @param string $message Error message.
* @param string $title Optional. Error title. Default empty.
* @param array $args Optional. Array of arguments.
*/
function _default_wp_die_handler( $message, $title = '', $args = array() ) {
$defaults = array( 'response' => 500 );
$args = wp_parse_args( $args, $defaults );
$have_gettext = function_exists( '__' );
if ( function_exists( 'is_admin' ) ) {
if ( is_admin() ) {
/**
* Fires before the administration header is written to output.
*
* @since 3.0.0
*/
do_action( 'admin_head' );
?>
<div class="wrap">
<h1><?php echo esc_html( $title ); ?></h1>
<?php echo $message; ?>
</div>
<?php
} else {
if ( did_action( 'login_head' ) ) {
$login_header = 'login_header';
} else {
$login_header = 'wp_login_header';
}
/**
* Fires in the login page header for displaying login form header elements.
*
* @since 3.5.0
*/
do_action( 'login_enqueue_scripts' );
/**
* Fires in the login page header for displaying login form header elements.
*
* @since 2.1.0
*
* @param string $title Login page title to display.
* @param string $message Message to display in the header.
*/
do_action( $login_header, $title, $message );
?>
<p><?php echo $message; ?></p>
<?php
}
} else {
if ( ! headers_sent() ) {
header( 'Content-Type: text/html; charset=utf-8' );
header( sprintf( 'HTTP/1.1 %d %s', $args['response'], get_status_header_desc( $args['response'] ) ) );
}
?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" <?php language_attributes(); ?>>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width">
<title><?php echo esc_html( $title ); ?></title>
<style type="text/css">
html {
background: #f1f1f1;
}
body {
background: #fff;
color: #444;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
margin: 2em auto;
padding: 1em 2em;
max-width: 700px;
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, .13);
box-shadow: 0 1px 3px rgba(0, 0, 0, .13);
}
h1 {
border-bottom: 1px solid #dadada;
clear: both;
color: #666;
font-size: 2em;
margin: 30px 0 0 0;
padding: 0;
padding-bottom: 7px;
}
#error-page {
margin-top: 50px;
}
#error-page p {
font-size: 14px;
line-height: 1.5;
margin: 25px 0 20px;
}
#error-page code {
font-family: Consolas, Monaco, monospace;
}
ul li {
margin-bottom: 10px;
font-size: 14px;
}
a {
color: #0073aa;
}
a:hover {
color: #006799;
}
.button {
background: #f7f7f7;
border: 1px solid #cccccc;
color: #555;
display: inline-block;
text-decoration: none;
font-size: 13px;
line-height: 2;
height: 28px;
margin: 0;
padding: 0 10px 1px;
cursor: pointer;
-webkit-border-radius: 3px;
border-radius: 3px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: 0 1px 0 #fff inset, 0 0 0 1px rgba(0, 0, 0, .08);
box-shadow: 0 1px 0 #fff inset, 0 0 0 1px rgba(0, 0, 0, .08);
vertical-align: top;
}
.button:hover {
background: #fafafa;
border-color: #999;
color: #222;
}
.button:focus {
background: #fff;
border-color: #999;
color: #333;
-webkit-box-shadow: 0 1px 0 #fff inset, 0 0 0 1px rgba(0, 0, 0, .08), 0 0 5px rgba(0, 0, 0, .08);
box-shadow: 0 1px 0 #fff inset, 0 0 0 1px rgba(0, 0, 0, .08), 0 0 5px rgba(0, 0, 0, .08);
outline: none;
}
.button:active {
background: #eee;
border-color: #999;
color: #333;
-webkit-box-shadow: 0 2px 5px -3px rgba(0, 0, 0, .5) inset;
box-shadow: 0 2px 5px -3px rgba(0, 0, 0, .5) inset;
}
<?php if ( is_rtl() ) : ?>
body {
font-family: Tahoma, Arial, sans-serif;
}
<?php endif; ?>
</style>
</head>
<body id="error-page">
<div class="container">
<h1 class="headline"><?php echo esc_html( $title ); ?></h1>
<p class="message"><?php echo $message; ?></p>
</div>
</body>
</html>
<?php
}
if ( $args['exit'] ) {
die( 1 );
}
}
这个函数会根据当前环境(后台、前台、登录页面)显示不同的错误页面。 它会设置 HTTP 响应码,输出 HTML 头部信息,以及显示错误信息和标题。
自定义错误页面:
现在,我们来看看如何使用 wp_die_handler
过滤器来定制错误页面。 假设我们需要创建一个自定义的错误页面,显示一个友好的错误信息,并提供一个返回首页的链接。
首先,我们需要创建一个自定义的错误处理函数:
function my_custom_die_handler( $message, $title = '', $args = array() ) {
$defaults = array(
'response' => 500,
'back_link' => home_url(), // 添加返回首页链接
);
$args = wp_parse_args( $args, $defaults );
$response = $args['response'];
// 输出 HTTP 响应码
status_header( $response );
// 设置 Content-Type
header( 'Content-Type: text/html; charset=utf-8' );
// 输出 HTML 头部
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php echo esc_html( $title ); ?></title>
<style>
body {
font-family: sans-serif;
text-align: center;
padding: 50px;
}
h1 {
font-size: 2em;
margin-bottom: 20px;
}
p {
font-size: 1.2em;
margin-bottom: 30px;
}
a {
color: #0073aa;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<h1><?php echo esc_html( $title ); ?></h1>
<p><?php echo $message; ?></p>
<?php if ( $args['back_link'] ) : ?>
<a href="<?php echo esc_url( $args['back_link'] ); ?>">返回首页</a>
<?php endif; ?>
</body>
</html>
<?php
// 停止执行
die();
}
然后,我们需要使用 wp_die_handler
过滤器来替换默认的错误处理函数:
add_filter( 'wp_die_handler', 'my_custom_die_handler' );
这段代码会将 my_custom_die_handler
函数注册为 wp_die_handler
过滤器的回调函数。 这样,当 wp_die()
函数被调用时,就会调用 my_custom_die_handler
函数来处理错误信息。
示例:在插件中使用 wp_die()
和 wp_die_handler
:
<?php
/**
* Plugin Name: Custom WP_Die Handler
* Description: Demonstrates how to customize the wp_die() output.
* Version: 1.0.0
*/
// 自定义错误处理函数
function my_custom_die_handler( $message, $title = '', $args = array() ) {
$defaults = array(
'response' => 500,
'back_link' => home_url(), // 添加返回首页链接
);
$args = wp_parse_args( $args, $defaults );
$response = $args['response'];
// 输出 HTTP 响应码
status_header( $response );
// 设置 Content-Type
header( 'Content-Type: text/html; charset=utf-8' );
// 输出 HTML 头部
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php echo esc_html( $title ); ?></title>
<style>
body {
font-family: sans-serif;
text-align: center;
padding: 50px;
}
h1 {
font-size: 2em;
margin-bottom: 20px;
}
p {
font-size: 1.2em;
margin-bottom: 30px;
}
a {
color: #0073aa;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<h1><?php echo esc_html( $title ); ?></h1>
<p><?php echo $message; ?></p>
<?php if ( $args['back_link'] ) : ?>
<a href="<?php echo esc_url( $args['back_link'] ); ?>">返回首页</a>
<?php endif; ?>
</body>
</html>
<?php
// 停止执行
die();
}
// 注册 wp_die_handler 过滤器
add_filter( 'wp_die_handler', 'my_custom_die_handler' );
// 示例:在插件激活时触发 wp_die()
function my_plugin_activation() {
// 检查 PHP 版本
if ( version_compare( PHP_VERSION, '7.0', '<' ) ) {
wp_die( 'This plugin requires PHP version 7.0 or higher.', 'Plugin Activation Error', array( 'back_link' => false ) );
}
}
register_activation_hook( __FILE__, 'my_plugin_activation' );
在这个示例中,我们创建了一个名为 "Custom WP_Die Handler" 的插件。
- 插件激活时,会检查 PHP 版本。 如果 PHP 版本低于 7.0,会调用
wp_die()
函数显示一个错误信息,并停止插件的激活过程。 - 我们使用
wp_die_handler
过滤器来替换默认的错误处理函数,使用我们自定义的my_custom_die_handler
函数。
现在,当你尝试激活这个插件时,如果你的 PHP 版本低于 7.0,你将会看到我们自定义的错误页面,而不是 WordPress 默认的错误页面。
总结:
wp_die()
函数是 WordPress 中一个非常重要的函数,用于处理错误和异常情况。 通过 wp_die_handler
过滤器,我们可以自定义错误页面的显示方式,从而提高用户体验。
希望今天的讲解能够帮助大家更好地理解 wp_die()
函数和 wp_die_handler
过滤器。 记住,错误并不可怕,可怕的是我们不知道如何优雅地处理它们。
下次再见!