解释 `register_setting()` 函数的源码,它是如何将设置选项注册到 WordPress 后台的?

咳咳,麦克风试音,1 2 3… 大家好!我是老码农,今天咱们聊聊 WordPress 后台设置选项的“户口”是怎么上的,也就是 register_setting() 函数的工作原理。

说白了,register_setting() 就是个“登记员”,它负责告诉 WordPress:“嘿,老兄,我这里有几个新来的设置选项,你得给它们在数据库里安排个位置,顺便让它们在后台页面上露个脸。”

咱们先从最简单的用法开始,然后逐步深入,看看这个函数背后到底藏着哪些玄机。

1. register_setting() 的基本结构

register_setting() 函数的原型是这样的:

register_setting( string $option_group, string $option_name, array $args = array() );
  • $option_group: 选项组名,它就像一个“文件夹”,把相关的设置选项放在一起,方便管理。
  • $option_name: 选项名,每个设置选项的唯一标识符,就像它的“身份证号”。
  • $args: 一个可选的参数数组,里面可以设置一些额外的选项,比如验证回调函数。

2. 简单示例:注册一个文本框设置

假设我们要创建一个“网站标题背景颜色”的设置选项,让用户可以自定义网站标题的背景颜色。我们可以这样做:

<?php
add_action( 'admin_init', 'my_theme_register_settings' );

function my_theme_register_settings() {
    register_setting( 'my_theme_options', 'my_theme_title_bg_color', 'sanitize_hex_color' );
}
?>

这段代码做了什么?

  • add_action( 'admin_init', 'my_theme_register_settings' );: 这行代码告诉 WordPress,在后台初始化的时候(admin_init 钩子),执行 my_theme_register_settings() 函数。
  • register_setting( 'my_theme_options', 'my_theme_title_bg_color', 'sanitize_hex_color' );: 这行代码是核心。
    • 'my_theme_options': 选项组名,我们把它叫做 my_theme_options。你可以随便起个名字,只要保证唯一性就好。
    • 'my_theme_title_bg_color': 选项名,我们把它叫做 my_theme_title_bg_color。 将来可以通过这个名字来获取和更新这个设置的值。
    • 'sanitize_hex_color': 验证回调函数,用来对用户输入的值进行验证和过滤。 sanitize_hex_color() 是 WordPress 内置的函数,用来确保用户输入的是有效的十六进制颜色值。

3. 验证回调函数:守门员的角色

验证回调函数非常重要,它就像一个“守门员”,防止用户输入一些不合法的数据,导致程序出错或者安全问题。

如果没有指定验证回调函数,WordPress 会默认使用 sanitize_text_field() 函数,它会对用户输入的值进行简单的过滤,去除 HTML 标签和特殊字符。

除了使用 WordPress 内置的验证函数,我们也可以自定义验证函数。例如:

<?php
function my_theme_validate_title_bg_color( $input ) {
    // 如果用户输入的值不是有效的十六进制颜色值,就返回默认值 #ffffff
    if ( ! preg_match( '/^#([0-9a-f]{3}){1,2}$/i', $input ) ) {
        add_settings_error(
            'my_theme_title_bg_color',
            'invalid_color',
            '请输入有效的十六进制颜色值,例如 #ffffff',
            'error'
        );
        return '#ffffff';
    }

    return $input;
}

add_action( 'admin_init', 'my_theme_register_settings' );

function my_theme_register_settings() {
    register_setting( 'my_theme_options', 'my_theme_title_bg_color', 'my_theme_validate_title_bg_color' );
}
?>

这段代码做了什么?

  • my_theme_validate_title_bg_color( $input ): 自定义的验证回调函数。
    • $input: 用户输入的值。
    • preg_match( '/^#([0-9a-f]{3}){1,2}$/i', $input ): 使用正则表达式判断用户输入的值是否是有效的十六进制颜色值。
    • add_settings_error(): 如果用户输入的值不合法,就添加一个错误信息,显示在后台页面上。
    • return '#ffffff';: 如果用户输入的值不合法,就返回默认值 #ffffff
  • register_setting( 'my_theme_options', 'my_theme_title_bg_color', 'my_theme_validate_title_bg_color' );: 在 register_setting() 函数中指定使用我们自定义的验证函数。

4. 选项组:设置选项的“家”

选项组就像一个“文件夹”,把相关的设置选项放在一起。 在后台页面上,通常会把同一个选项组的设置选项放在同一个表单里。

WordPress 内置了一些常用的选项组,比如 general(常规选项)、reading(阅读选项)、writing(撰写选项)等等。 我们也可以自定义选项组。

5. $args 参数:更多配置选项

register_setting() 函数的第三个参数 $args 是一个数组,里面可以设置一些额外的选项。

参数名 类型 描述
type string 设置选项的数据类型。 默认是 'string'。常用的类型有 'string''integer''number''boolean''array''object'。 WordPress 会根据这个类型对用户输入的值进行验证。
description string 设置选项的描述信息,显示在后台页面上。
sanitize_callback mixed 验证回调函数。 如果指定了这个参数,register_setting() 函数会优先使用这个参数指定的验证函数,而不是使用第三个参数指定的验证函数。
show_in_rest bool 是否在 REST API 中显示这个设置选项。 默认是 false。 如果设置为 true,就可以通过 REST API 来获取和更新这个设置的值。
default mixed 设置选项的默认值。

例如:

<?php
add_action( 'admin_init', 'my_theme_register_settings' );

function my_theme_register_settings() {
    register_setting(
        'my_theme_options',
        'my_theme_title_font_size',
        array(
            'type'              => 'integer',
            'description'       => '网站标题字体大小(像素)',
            'sanitize_callback' => 'absint',
            'default'           => 16,
            'show_in_rest'      => true,
        )
    );
}
?>

这段代码做了什么?

  • 'type' => 'integer': 指定 my_theme_title_font_size 选项的数据类型是整数。
  • 'description' => '网站标题字体大小(像素)': 设置选项的描述信息,显示在后台页面上。
  • 'sanitize_callback' => 'absint': 指定验证回调函数是 absint()absint() 是 WordPress 内置的函数,用来将用户输入的值转换为绝对整数。
  • 'default' => 16: 设置选项的默认值是 16。
  • 'show_in_rest' => true: 允许通过 REST API 来获取和更新 my_theme_title_font_size 选项的值。

6. 如何在后台页面上显示设置选项?

register_setting() 函数只是负责注册设置选项,并不会自动在后台页面上显示它们。 我们需要手动编写代码,在后台页面上显示设置选项的表单。

通常,我们会使用 add_settings_section()add_settings_field() 函数来创建设置选项的表单。

<?php
add_action( 'admin_menu', 'my_theme_add_admin_menu' );
add_action( 'admin_init', 'my_theme_register_settings' );

function my_theme_add_admin_menu() {
    add_theme_page(
        '主题设置',
        '主题设置',
        'manage_options',
        'my-theme-options',
        'my_theme_options_page'
    );
}

function my_theme_options_page() {
    ?>
    <div class="wrap">
        <h1>主题设置</h1>
        <form method="post" action="options.php">
            <?php
            settings_fields( 'my_theme_options' ); // 输出选项组的隐藏字段
            do_settings_sections( 'my-theme-options' ); // 输出选项组的表单
            submit_button(); // 输出提交按钮
            ?>
        </form>
    </div>
    <?php
}

function my_theme_register_settings() {
    register_setting(
        'my_theme_options',
        'my_theme_title_bg_color',
        'sanitize_hex_color'
    );

    add_settings_section(
        'my_theme_title_section',
        '网站标题设置',
        'my_theme_title_section_callback',
        'my-theme-options'
    );

    add_settings_field(
        'my_theme_title_bg_color',
        '背景颜色',
        'my_theme_title_bg_color_callback',
        'my-theme-options',
        'my_theme_title_section'
    );

    register_setting(
        'my_theme_options',
        'my_theme_title_font_size',
        array(
            'type'              => 'integer',
            'description'       => '网站标题字体大小(像素)',
            'sanitize_callback' => 'absint',
            'default'           => 16,
            'show_in_rest'      => true,
        )
    );

    add_settings_field(
        'my_theme_title_font_size',
        '字体大小',
        'my_theme_title_font_size_callback',
        'my-theme-options',
        'my_theme_title_section'
    );
}

function my_theme_title_section_callback() {
    echo '<p>在这里可以设置网站标题的样式。</p>';
}

function my_theme_title_bg_color_callback() {
    $value = get_option( 'my_theme_title_bg_color', '#ffffff' );
    ?>
    <input type="color" name="my_theme_title_bg_color" value="<?php echo esc_attr( $value ); ?>">
    <?php
}

function my_theme_title_font_size_callback() {
    $value = get_option( 'my_theme_title_font_size', 16 );
    ?>
    <input type="number" name="my_theme_title_font_size" value="<?php echo esc_attr( $value ); ?>">
    <?php
}
?>

这段代码比较长,我们来逐步分析:

  • add_action( 'admin_menu', 'my_theme_add_admin_menu' );: 在后台菜单中添加一个“主题设置”页面。
  • my_theme_add_admin_menu(): 创建后台菜单项。
  • my_theme_options_page(): 显示“主题设置”页面的内容。
    • settings_fields( 'my_theme_options' );: 输出选项组的隐藏字段,用于安全验证。
    • do_settings_sections( 'my-theme-options' );: 输出选项组的表单,根据之前定义的 section 和 field 来显示设置选项。
    • submit_button();: 输出提交按钮。
  • add_settings_section(): 创建一个设置选项的 section。
    • 'my_theme_title_section': section 的 ID。
    • '网站标题设置': section 的标题,显示在后台页面上。
    • 'my_theme_title_section_callback': section 的回调函数,用于输出 section 的描述信息。
    • 'my-theme-options': 页面 ID,指定 section 显示在哪个页面上。
  • add_settings_field(): 创建一个设置选项的 field。
    • 'my_theme_title_bg_color': field 的 ID,与 register_setting() 函数中的选项名一致。
    • '背景颜色': field 的标题,显示在后台页面上。
    • 'my_theme_title_bg_color_callback': field 的回调函数,用于输出 field 的 HTML 代码。
    • 'my-theme-options': 页面 ID,指定 field 显示在哪个页面上。
    • 'my_theme_title_section': section ID,指定 field 显示在哪个 section 中。
  • my_theme_title_section_callback(): 输出 section 的描述信息。
  • my_theme_title_bg_color_callback(): 输出“背景颜色”设置选项的 HTML 代码。 这里使用 <input type="color"> 创建一个颜色选择器。
    • get_option( 'my_theme_title_bg_color', '#ffffff' ): 获取 my_theme_title_bg_color 选项的值。 如果选项不存在,就返回默认值 #ffffff
    • esc_attr(): 对输出的属性值进行转义,防止 XSS 攻击。
  • my_theme_title_font_size_callback(): 输出 "字体大小" 设置选项的 HTML 代码。 这里使用 <input type="number"> 创建一个数字输入框。

7. 如何获取设置选项的值?

可以使用 get_option() 函数来获取设置选项的值。

<?php
$title_bg_color = get_option( 'my_theme_title_bg_color', '#ffffff' );
$title_font_size = get_option( 'my_theme_title_font_size', 16 );
?>
  • get_option( 'my_theme_title_bg_color', '#ffffff' ): 获取 my_theme_title_bg_color 选项的值。 如果选项不存在,就返回默认值 #ffffff
  • get_option( 'my_theme_title_font_size', 16 ): 获取 my_theme_title_font_size 选项的值。 如果选项不存在,就返回默认值 16

8. register_setting() 的源码剖析

咱们现在来扒一扒 register_setting() 函数的源码,看看它到底做了什么。

(由于 WordPress 版本更新迭代快,以下代码片段基于 WordPress 相对较新的版本,具体请参考你使用的 WordPress 版本)

首先,register_setting() 函数位于 wp-admin/includes/plugin.php 文件中。 它的主要逻辑如下:

function register_setting( $option_group, $option_name, $sanitize_callback = '', $deprecated = false ) {
    global $new_whitelist_options;

    // Back compat for the $deprecated argument.
    if ( ! is_scalar( $sanitize_callback ) && ! is_callable( $sanitize_callback ) && false !== $deprecated ) {
        _deprecated_argument( __FUNCTION__, '4.7.0', sprintf(
            /* translators: 1: The name of the function. 2: The name of the argument. */
            __( 'The third argument to %1$s must be a string or callable, not boolean. To pass a boolean as a default value, use the %2$s argument to the %1$s function instead.' ),
            'register_setting()',
            'default'
        ) );
        $args             = $sanitize_callback;
        $sanitize_callback = $deprecated;
    } else {
        $args = func_get_args();
        $args = ( isset( $args[2] ) && is_array( $args[2] ) ) ? $args[2] : array();
    }

    $args = wp_parse_args( $args, array(
        'type'              => 'string',
        'description'       => '',
        'sanitize_callback' => $sanitize_callback,
        'show_in_rest'     => false,
        'default'           => null,
    ) );

    $new_whitelist_options[ $option_group ][ $option_name ] = true;

    add_action( 'sanitize_option_' . $option_name, array( _WP_Dependency::get_instance(), 'sanitize_option' ), 10, 2 );

    // Register the setting to the REST API.
    if ( ! empty( $args['show_in_rest'] ) ) {
        register_rest_field( 'options', $option_name, array(
            'get_callback'    => function() use ( $option_name ) {
                return get_option( $option_name );
            },
            'update_callback' => function( $value ) use ( $option_name ) {
                return update_option( $option_name, $value );
            },
            'schema'          => array(
                'type'        => $args['type'],
                'description' => $args['description'],
                'default'     => $args['default'],
            ),
        ) );
    }
}

这段代码做了什么?

  1. 兼容性处理: 检查参数,处理一些旧版本的兼容性问题。
  2. 参数解析: 使用 wp_parse_args() 函数将传入的参数和默认参数合并,得到最终的参数数组 $args
  3. 添加到白名单: 将选项组和选项名添加到 $new_whitelist_options 全局变量中。 这个变量用于存储所有注册的设置选项。
  4. 注册验证钩子: 使用 add_action() 函数注册一个钩子,当保存 option_name 选项时,会执行 _WP_Dependency::sanitize_option() 函数进行验证。
  5. 注册 REST API 字段: 如果 $args['show_in_rest']true,则使用 register_rest_field() 函数将该选项注册到 REST API 中,使其可以通过 REST API 进行访问。

_WP_Dependency::sanitize_option() 函数

_WP_Dependency::sanitize_option() 函数负责调用验证回调函数,对用户输入的值进行验证和过滤。

public function sanitize_option( $value, $option ) {
    // Get the registered setting for the option.
    $registered_setting = $this->get_registered_setting( $option );

    // If no registered setting, return the value as is.
    if ( ! $registered_setting ) {
        return $value;
    }

    // Get the sanitize callback.
    $sanitize_callback = $registered_setting['sanitize_callback'];

    // If no sanitize callback, return the value as is.
    if ( ! $sanitize_callback ) {
        return $value;
    }

    // If sanitize callback is a string, check if it is a valid function.
    if ( is_string( $sanitize_callback ) && ! is_callable( $sanitize_callback ) ) {
        return $value;
    }

    // Call the sanitize callback.
    $value = call_user_func( $sanitize_callback, $value );

    return $value;
}

这段代码做了什么?

  1. 获取注册的设置: 调用 $this->get_registered_setting() 函数获取选项的注册信息。
  2. 检查验证回调函数: 检查是否存在验证回调函数。 如果没有,直接返回原始值。
  3. 调用验证回调函数: 使用 call_user_func() 函数调用验证回调函数,对用户输入的值进行验证和过滤。

总结

register_setting() 函数就像 WordPress 后台设置选项的“户籍管理员”,它负责将设置选项注册到系统中,并指定验证回调函数,确保数据的安全性。 通过 add_settings_section()add_settings_field() 函数,我们可以将这些设置选项在后台页面上显示出来,让用户可以自定义网站的各种设置。 最后,可以使用 get_option() 函数来获取设置选项的值,并在主题或插件中使用。

希望这次的“讲座”能帮助大家更好地理解 register_setting() 函数的工作原理。 下次有机会,咱们再聊聊 WordPress 的其他有趣话题! 谢谢大家!

发表回复

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