WordPress函数:do_settings_fields 输出设置字段组
编辑文章简介
do_settings_sections 函数用于在WordPress后台设置页面中输出已注册的设置节(sections)及其包含的所有设置字段,是构建自定义设置页面的核心工具。
语法
do_settings_sections( string $page )
文件位置:wp-admin/includes/template.php
内部调用:函数会调用 do_settings_fields() 来输出每个节内的具体字段。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
$page |
string | 是 | 无 | 设置页面的菜单slug,需要与 add_settings_section() 和 add_settings_field() 注册时使用的页面slug一致 |
返回值:无返回值,直接输出HTML内容。
用法
基础用法
最基本的用法是在设置页面回调函数中调用 do_settings_sections(),通常与 settings_fields() 函数配合使用:
// 在主题的 functions.php 或插件主文件中添加
add_action('admin_menu', 'myplugin_register_settings_page');
function myplugin_register_settings_page() {
add_options_page(
'我的插件设置',
'我的插件',
'manage_options',
'myplugin-settings',
'myplugin_render_settings_page'
);
// 注册设置、节和字段
register_setting('myplugin_options', 'myplugin_settings');
add_settings_section(
'myplugin_main_section',
'主要设置',
'myplugin_section_callback',
'myplugin-settings'
);
add_settings_field(
'api_key',
'API密钥',
'myplugin_field_callback',
'myplugin-settings',
'myplugin_main_section',
['label_for' => 'api_key']
);
}
// 设置页面渲染函数
function myplugin_render_settings_page() {
?>
<div class="wrap">
<h1>我的插件设置</h1>
<form action="options.php" method="post">
<?php
// 输出必要的安全字段
settings_fields('myplugin_options');
// 输出所有设置节和字段
do_settings_sections('myplugin-settings');
// 输出提交按钮
submit_button();
?>
</form>
</div>
<?php
}
// 节描述回调函数
function myplugin_section_callback($args) {
echo '<p>这里是主要设置的说明文字。</p>';
}
// 字段渲染回调函数
function myplugin_field_callback($args) {
$options = get_option('myplugin_settings');
$value = isset($options[$args['label_for']]) ? $options[$args['label_for']] : '';
?>
<input type="text"
id="<?php echo esc_attr($args['label_for']); ?>"
name="myplugin_settings[<?php echo esc_attr($args['label_for']); ?>]"
value="<?php echo esc_attr($value); ?>"
class="regular-text" />
<?php
}
为什么需要完整的流程:do_settings_sections() 只是输出环节,它依赖于之前正确的注册流程。如果忘记调用 settings_fields(),WordPress的安全验证会失败;如果忘记注册设置节,函数将无内容可输出。
进阶用法
自定义节的输出顺序
虽然 do_settings_sections() 按注册顺序输出,但可以通过调整注册顺序或使用优先级来控制:
// 在插件初始化时按特定顺序注册节
add_action('admin_init', 'myplugin_register_sections_in_order', 5);
function myplugin_register_sections_in_order() {
// 先注册的节会先显示
add_settings_section('section_one', '第一部分', '__return_empty_string', 'myplugin-settings');
add_settings_section('section_two', '第二部分', '__return_empty_string', 'myplugin-settings');
// 为每个节添加字段
add_settings_field('field1', '字段1', 'render_field1', 'myplugin-settings', 'section_one');
add_settings_field('field2', '字段2', 'render_field2', 'myplugin-settings', 'section_two');
}
条件性输出节
在某些情况下,你可能需要根据条件显示或隐藏整个设置节:
function myplugin_conditional_settings_page() {
settings_fields('myplugin_options');
// 只输出特定节
global $wp_settings_sections;
$page = 'myplugin-settings';
if (isset($wp_settings_sections[$page])) {
foreach ((array) $wp_settings_sections[$page] as $section) {
// 检查用户权限或其他条件
if (current_user_can('manage_options') || $section['id'] === 'public_section') {
echo '<div class="settings-section" id="' . esc_attr($section['id']) . '">';
if ($section['title']) {
echo "<h2>{$section['title']}</h2>\n";
}
if ($section['callback']) {
call_user_func($section['callback'], $section);
}
echo '<table class="form-table" role="presentation">';
do_settings_fields($page, $section['id']);
echo '</table></div>';
}
}
}
submit_button();
}
易错点
页面slug不匹配
最常见的错误是 do_settings_sections() 的参数与注册时使用的页面slug不一致:
// 错误示例
add_settings_section('my_section', '我的节', 'callback', 'myplugin-page');
// 在渲染时使用了不同的slug
do_settings_sections('myplugin-page-2'); // 这里不会输出任何内容
// 正确做法:保持slug一致
do_settings_sections('myplugin-page'); // 与注册时使用的slug相同
忘记调用 settings_fields()
如果忘记在表单中调用 settings_fields(),WordPress的安全nonce验证会失败,导致设置无法保存:
// 错误:缺少安全字段
<form method="post" action="options.php">
<?php do_settings_sections('myplugin-settings'); ?>
<?php submit_button(); ?>
</form>
// 正确:包含安全字段
<form method="post" action="options.php">
<?php settings_fields('myplugin_options'); ?>
<?php do_settings_sections('myplugin-settings'); ?>
<?php submit_button(); ?>
</form>
在错误的钩子中注册
设置节和字段必须在 admin_init 钩子或之前注册,否则 do_settings_sections() 调用时它们还不存在:
// 错误:在太晚的钩子中注册
add_action('admin_menu', function() {
// 当 do_settings_sections() 在页面回调中执行时,这里的注册还未发生
add_settings_section('my_section', '我的节', 'callback', 'myplugin-settings');
});
// 正确:在 admin_init 钩子中注册
add_action('admin_init', function() {
add_settings_section('my_section', '我的节', 'callback', 'myplugin-settings');
});
输出转义不足
在字段回调函数中直接输出用户数据而不转义会导致XSS安全漏洞:
// 错误:未转义输出
function myplugin_field_callback($args) {
$options = get_option('myplugin_settings');
echo '<input value="' . $options['api_key'] . '">'; // 未转义!
}
// 正确:始终转义输出
function myplugin_field_callback($args) {
$options = get_option('myplugin_settings');
$value = isset($options['api_key']) ? esc_attr($options['api_key']) : '';
echo '<input value="' . $value . '">';
}
最佳实践
模块化组织设置代码
对于包含多个设置节的复杂插件,将设置代码模块化可以提高可维护性:
// 主设置类
class MyPlugin_Settings {
private $page_slug = 'myplugin-settings';
public function __construct() {
add_action('admin_init', [$this, 'register_settings']);
add_action('admin_menu', [$this, 'add_menu_page']);
}
public function register_settings() {
register_setting('myplugin_options', 'myplugin_settings', [
'sanitize_callback' => [$this, 'sanitize_settings']
]);
// 使用单独的方法注册每个节
$this->register_general_section();
$this->register_advanced_section();
}
private function register_general_section() {
add_settings_section(
'general',
'常规设置',
function() {
echo '<p>常规设置说明</p>';
},
$this->page_slug
);
add_settings_field(
'site_title',
'网站标题',
[$this, 'render_text_field'],
$this->page_slug,
'general',
[
'label_for' => 'site_title',
'description' => '输入网站标题'
]
);
}
public function render_text_field($args) {
$options = get_option('myplugin_settings');
$value = isset($options[$args['label_for']]) ? $options[$args['label_for']] : '';
?>
<input type="text"
id="<?php echo esc_attr($args['label_for']); ?>"
name="myplugin_settings[<?php echo esc_attr($args['label_for']); ?>]"
value="<?php echo esc_attr($value); ?>"
class="regular-text" />
<?php if (!empty($args['description'])): ?>
<p class="description"><?php echo esc_html($args['description']); ?></p>
<?php endif;
}
}
与现代JavaScript框架结合
当使用React或Vue开发WordPress插件时,可以结合REST API和设置API:
// 注册REST API端点用于设置
add_action('rest_api_init', function() {
register_rest_route('myplugin/v1', '/settings', [
'methods' => 'GET',
'callback' => 'myplugin_get_settings_api',
'permission_callback' => function() {
return current_user_can('manage_options');
}
]);
register_rest_route('myplugin/v1', '/settings', [
'methods' => 'POST',
'callback' => 'myplugin_update_settings_api',
'permission_callback' => function() {
return current_user_can('manage_options');
}
]);
});
// 同时保留传统的设置页面作为备选
function myplugin_hybrid_settings_page() {
?>
<div class="wrap">
<h1>混合设置页面</h1>
<!-- 传统表单部分 -->
<div id="traditional-settings">
<form method="post" action="options.php">
<?php settings_fields('myplugin_options'); ?>
<?php do_settings_sections('myplugin-settings'); ?>
<?php submit_button(); ?>
</form>
</div>
<!-- React应用容器 -->
<div id="react-settings-app"></div>
<script>
// 根据用户选择切换界面
if (window.myPluginUseReact) {
document.getElementById('traditional-settings').style.display = 'none';
// 初始化React应用
}
</script>
</div>
<?php
}
性能优化
对于有大量设置的页面,可以考虑分页或选项卡式显示:
function myplugin_tabbed_settings_page() {
$tabs = ['general', 'advanced', 'tools'];
$current_tab = isset($_GET['tab']) && in_array($_GET['tab'], $tabs) ? $_GET['tab'] : 'general';
?>
<div class="wrap">
<h1>我的插件设置</h1>
<nav class="nav-tab-wrapper">
<?php foreach ($tabs as $tab): ?>
<a href="?page=myplugin-settings&tab=<?php echo esc_attr($tab); ?>"
class="nav-tab <?php echo $current_tab === $tab ? 'nav-tab-active' : ''; ?>">
<?php echo esc_html(ucfirst($tab)); ?>
</a>
<?php endforeach; ?>
</nav>
<form method="post" action="options.php">
<?php settings_fields('myplugin_options'); ?>
<!-- 只输出当前选项卡的节 -->
<?php
// 根据选项卡显示不同的节
switch ($current_tab) {
case 'general':
do_settings_sections('myplugin-settings-general');
break;
case 'advanced':
do_settings_sections('myplugin-settings-advanced');
break;
}
?>
<?php submit_button(); ?>
</form>
</div>
<?php
}
可访问性增强
确保设置页面符合WCAG标准:
function myplugin_accessible_field_callback($args) {
$options = get_option('myplugin_settings');
$value = isset($options[$args['label_for']]) ? $options[$args['label_for']] : '';
$describedby = !empty($args['description']) ? ' aria-describedby="' . esc_attr($args['label_for']) . '-description"' : '';
?>
<input type="text"
id="<?php echo esc_attr($args['label_for']); ?>"
name="myplugin_settings[<?php echo esc_attr($args['label_for']); ?>]"
value="<?php echo esc_attr($value); ?>"
class="regular-text"
<?php echo $describedby; ?>
aria-required="<?php echo !empty($args['required']) ? 'true' : 'false'; ?>" />
<?php if (!empty($args['description'])): ?>
<p id="<?php echo esc_attr($args['label_for']); ?>-description" class="description">
<?php echo esc_html($args['description']); ?>
</p>
<?php endif;
}
通过遵循这些最佳实践,你可以创建出既安全又易用、既高效又可维护的设置页面,同时为未来的功能扩展和现代开发模式做好准备。