解读 WordPress `_doing_it_wrong()` 函数源码:如何利用 `trigger_error()` 辅助开发。

大家好!欢迎参加今天的 “WordPress 错误处理小课堂”。我是你们今天的讲师,阿伟。今天咱们不搞那些虚头巴脑的概念,直接来啃 WordPress 里的一个实用函数:_doing_it_wrong()

这玩意儿听起来好像在指责你,实际上,它是 WordPress 用来辅助开发者排查代码问题的利器。咱们今天就来扒一扒它的源码,看看它到底是怎么“指责”我们的,以及我们如何利用它来提高开发效率。

1. 啥是 _doing_it_wrong()

简单来说,_doing_it_wrong() 是 WordPress 框架提供的一个函数,用于在代码中使用了不推荐的方法、过时的函数或者出现了潜在问题时,触发一个 E_USER_NOTICE 级别的错误。这个错误不会中断程序的运行,但会在调试模式下显示出来,提醒你:“嘿,哥们儿,你这么写不太对劲啊!”

2. 源码剖析:_doing_it_wrong() 的庐山真面目

让我们先来看一下 _doing_it_wrong() 的源码 (基于 WordPress 6.4.2):

/**
 * Marks a function, method, or class as deprecated and triggers a user error when accessed.
 *
 * Use of already deprecated functions, methods, or classes should trigger
 * a user error to help developers identify them during code audits.
 *
 * @since 2.3.0
 * @since 4.5.0 The `$message` argument supports้านstring interpolation.
 *
 * @param string $function    The function that was called.
 * @param string $version     The version of WordPress that deprecated the function.
 * @param string $message     Optional. A message regarding the change.
 *                              Default null.
 */
function _doing_it_wrong( $function, $version, $message = null ) {
    $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );
    $bt_file   = isset( $backtrace[0]['file'] ) ? wp_basename( $backtrace[0]['file'] ) : '';
    $bt_line   = isset( $backtrace[0]['line'] ) ? $backtrace[0]['line'] : '';

    $message = sprintf(
        /* translators: 1: PHP function name, 2: WordPress version number. */
        __( '%1$s was called incorrectly.' ),
        $function
    );
    if ( ! is_null( $message ) ) {
        $message .= ' ' . sprintf( __( '%s.' ), $message );
    }

    /* translators: 1: File name, 2: Line number. */
    $message .= ' ' . sprintf( __( 'Please see <a href="%s">Debugging in WordPress</a> for more information.' ), 'https://wordpress.org/documentation/article/debugging-in-wordpress/' );
    $message .= " (This message was added in version $version.)";

    if ( WP_DEBUG && apply_filters( 'doing_it_wrong_trigger_error', true ) ) {
        trigger_error( sprintf( '%s in %s on line %s', $message, $bt_file, $bt_line ), E_USER_NOTICE );
    }

    do_action( 'doing_it_wrong_run', $function, $message, $version, $bt_file, $bt_line );
}

让我们一行一行地解读:

  • function _doing_it_wrong( $function, $version, $message = null ): 定义函数,接收三个参数:

    • $function: 出错的函数/方法/类的名字。
    • $version: 从哪个 WordPress 版本开始不推荐使用。
    • $message: 可选的错误信息,用于更详细地说明问题。
  • $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );: 获取函数调用堆栈信息。 debug_backtrace() 是一个 PHP 函数,可以获取当前脚本的调用堆栈。 DEBUG_BACKTRACE_IGNORE_ARGS 参数表示我们不需要函数参数的信息,而 2 表示我们只需要获取调用堆栈中的两层信息(当前函数和调用它的函数)。这个信息主要用来确定出错的文件和行号。

  • $bt_file = isset( $backtrace[0]['file'] ) ? wp_basename( $backtrace[0]['file'] ) : '';: 从调用堆栈信息中提取文件名。 wp_basename() 函数用于获取文件名,不包含路径。

  • $bt_line = isset( $backtrace[0]['line'] ) ? $backtrace[0]['line'] : '';: 从调用堆栈信息中提取行号。

  • $message = sprintf( /* translators: 1: PHP function name, 2: WordPress version number. */ __( '%1$s was called incorrectly.' ), $function );: 构建错误信息。 sprintf() 函数用于格式化字符串。 __( '%1$s was called incorrectly.' ) 是一个本地化函数,用于翻译错误信息。 %1$s 会被 $function 的值替换。

  • if ( ! is_null( $message ) ) { $message .= ' ' . sprintf( __( '%s.' ), $message ); }: 如果提供了额外的错误信息,则将其添加到错误信息中。

  • $message .= ' ' . sprintf( __( 'Please see <a href="%s">Debugging in WordPress</a> for more information.' ), 'https://wordpress.org/documentation/article/debugging-in-wordpress/' );: 添加一个指向 WordPress 调试文档的链接,方便开发者查找更多信息。

  • $message .= " (This message was added in version $version.)";: 添加错误信息添加的版本号。

  • if ( WP_DEBUG && apply_filters( 'doing_it_wrong_trigger_error', true ) ) { trigger_error( sprintf( '%s in %s on line %s', $message, $bt_file, $bt_line ), E_USER_NOTICE ); }: 这是最关键的一步。 如果 WP_DEBUG 常量为 true(表示开启了调试模式),并且 apply_filters( 'doing_it_wrong_trigger_error', true ) 返回 true(允许触发错误),则使用 trigger_error() 函数触发一个 E_USER_NOTICE 级别的错误。

    • WP_DEBUG: WordPress 的调试模式开关。 通常在 wp-config.php 文件中定义。
    • apply_filters( 'doing_it_wrong_trigger_error', true ): 允许通过 filter 来控制是否触发错误。 默认情况下,会触发错误。
    • trigger_error( sprintf( '%s in %s on line %s', $message, $bt_file, $bt_line ), E_USER_NOTICE );: PHP 的内置函数,用于触发一个用户级别的错误。 E_USER_NOTICE 表示这是一个通知级别的错误,不会中断程序运行。
  • do_action( 'doing_it_wrong_run', $function, $message, $version, $bt_file, $bt_line );: 触发一个 action hook,允许其他插件或主题监听并执行一些操作。例如,可以记录错误日志或者发送通知。

3. trigger_error():错误报告的幕后英雄

_doing_it_wrong() 的核心在于使用了 PHP 的 trigger_error() 函数。 trigger_error() 允许开发者手动触发一个错误。 它的语法如下:

trigger_error ( string $error_msg [, int $error_type = E_USER_NOTICE ] ) : bool
  • $error_msg: 错误信息。
  • $error_type: 错误类型。 可以是以下常量之一:

    常量 描述
    E_USER_ERROR 用户产生的错误信息。 程序会终止运行。
    E_USER_WARNING 用户产生的警告信息。 程序会继续运行。
    E_USER_NOTICE 用户产生的提示信息。 程序会继续运行。 _doing_it_wrong() 默认使用这个级别。
    E_USER_DEPRECATED 用户产生的提示信息,表明代码已过时。 这个级别从 PHP 5.3.0 开始可用。

trigger_error() 的返回值是一个布尔值,表示是否成功触发错误。 如果错误处理机制被禁用,或者提供的 $error_type 无效,则返回 false

4. 如何利用 _doing_it_wrong() 辅助开发?

_doing_it_wrong() 并不是 WordPress 框架的专利。 你完全可以在自己的插件或主题中使用它,来帮助你:

  • 标记过时的函数或方法: 当你想废弃一个函数或方法时,可以使用 _doing_it_wrong() 来提醒开发者不要再使用它。

  • 提示潜在的问题: 如果你的代码中存在一些潜在的问题,但又不想直接抛出异常,可以使用 _doing_it_wrong() 来提示开发者。

  • 强制执行最佳实践: 如果你想强制开发者遵循某些最佳实践,可以使用 _doing_it_wrong() 来提醒他们。

5. 实战演练:在插件中使用 _doing_it_wrong()

假设你正在开发一个插件,其中有一个函数 my_plugin_old_function() 已经过时了,你想提醒开发者不要再使用它。 你可以这样做:

/**
 * @deprecated 1.0.0  Use my_plugin_new_function() instead.
 */
function my_plugin_old_function() {
    _doing_it_wrong(
        'my_plugin_old_function',
        '1.0.0',
        'Use my_plugin_new_function() instead.'
    );

    //  旧函数的逻辑
    echo 'This function is deprecated. Please use my_plugin_new_function() instead.';
}

function my_plugin_new_function() {
    //  新函数的逻辑
    echo 'This is the new function.';
}

//  调用旧函数
my_plugin_old_function();

在这个例子中,当 my_plugin_old_function() 被调用时,_doing_it_wrong() 函数会被执行,从而触发一个 E_USER_NOTICE 级别的错误。

注意:你需要开启 WordPress 的调试模式 (WP_DEBUG = true) 才能看到这个错误信息。

6. 进阶技巧:使用 filter 禁用 _doing_it_wrong() 的错误提示

有时候,你可能不想在某些情况下显示 _doing_it_wrong() 的错误提示。 例如,你可能只想在开发环境中显示错误提示,而在生产环境中禁用它。 你可以使用 doing_it_wrong_trigger_error filter 来实现这一点。

add_filter( 'doing_it_wrong_trigger_error', 'my_plugin_disable_doing_it_wrong_error', 10, 1 );

function my_plugin_disable_doing_it_wrong_error( $trigger_error ) {
    if ( defined( 'WP_ENVIRONMENT_TYPE' ) && WP_ENVIRONMENT_TYPE === 'production' ) {
        return false; // 在生产环境中禁用错误提示
    }

    return $trigger_error; // 其他环境保持默认行为
}

在这个例子中,我们定义了一个 filter 函数 my_plugin_disable_doing_it_wrong_error(),它会检查当前环境是否为生产环境。 如果是生产环境,则返回 false,从而禁用 _doing_it_wrong() 的错误提示。

7. _deprecated_function()_deprecated_argument():两位好兄弟

WordPress 还有两个与 _doing_it_wrong() 类似的函数:_deprecated_function()_deprecated_argument()

  • _deprecated_function( string $function, string $version, string $replacement = null ): 用于标记一个函数已经过时。 与 _doing_it_wrong() 类似,但更专注于标记过时函数。 它会触发一个 E_USER_DEPRECATED 级别的错误。

  • _deprecated_argument( string $function, string $version, string $message = null ): 用于标记一个函数的参数已经过时。 如果函数使用了过时的参数,它会触发一个 E_USER_DEPRECATED 级别的错误。

这三个函数可以帮助你更好地管理你的代码,并确保开发者使用正确的方法。

8. 总结:拥抱错误,拥抱进步

_doing_it_wrong() 并不是一个让人害怕的函数。 相反,它是一个帮助你提高代码质量的工具。 通过合理地使用 _doing_it_wrong()trigger_error(),你可以更早地发现代码中的问题,并及时修复它们。

记住,错误是进步的阶梯。 拥抱错误,拥抱进步,让我们的代码更加健壮!

表格总结

函数/常量/Filter 描述
_doing_it_wrong() 用于标记代码使用了不推荐的方法、过时的函数或者出现了潜在问题时,触发一个 E_USER_NOTICE 级别的错误。
trigger_error() PHP 内置函数,用于触发一个用户级别的错误。
E_USER_NOTICE trigger_error() 函数可以使用的错误类型,表示用户产生的提示信息。程序会继续运行。
WP_DEBUG WordPress 的调试模式开关。通常在 wp-config.php 文件中定义。
doing_it_wrong_trigger_error filter,用于控制是否触发 _doing_it_wrong() 的错误提示。
_deprecated_function() 用于标记一个函数已经过时。与 _doing_it_wrong() 类似,但更专注于标记过时函数。 它会触发一个 E_USER_DEPRECATED 级别的错误。
_deprecated_argument() 用于标记一个函数的参数已经过时。如果函数使用了过时的参数,它会触发一个 E_USER_DEPRECATED 级别的错误。

好了,今天的课程就到这里。 希望大家能有所收获,并在实际开发中灵活运用 _doing_it_wrong()。 下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注