我之所以重写这篇文章,是因为正如有人正确指出的那样,原来的帖子“严重不足”我制作了一个插件,它有各种设置的选项卡视图。基于此Wordpress Plugin Template. 该模板使用WP Settings API构建设置页面并显示选项卡。表单使用默认值_wpnonce
用于“提交/保存设置”按钮。
选项卡是改变页面URL查询字符串的链接元素,例如/wp-admin/options-general.php?page=my_plugin_settings&tab=tab_1
到/wp-admin/options-general.php?page=my_plugin_settings&tab=tab_2
.
现在的问题是:如何为选项卡创建一个nonce,并在页面加载或用户选择其中一个选项卡时进行检查。
class-my-plugin.php. 为了清晰起见,简化了代码示例。
class My_Plugin {
private static $_instance = null;
public $admin = null;
public $settings = null;
public $_token;
public function __construct( $file = \'\', $version = \'1.0.0\' ) {
$this->_version = $version;
$this->_token = \'my_plugin\';
}
}
class-my-plugin-settings.php. 为了清晰起见,简化了代码示例。
class My_Plugin_Settings {
private static $_instance = null;
public $parent = null;
public $base = \'\';
public $settings = array();
public function __construct( $parent ) {
$this->parent = $parent;
$this->base = \'wpt_\';
add_action( \'init\', array( $this, \'init_settings\' ), 11 );
add_action( \'admin_init\', array( $this, \'register_settings\' ) );
add_action( \'admin_menu\', array( $this, \'add_menu_item\' ) );
add_filter( $this->base . \'menu_settings\', array( $this, \'configure_settings\' ) );
}
/**
* Initialise settings
*/
public function init_settings() {
$this->settings = $this->settings_fields();
}
/**
* Add settings page to admin menu
*/
public function add_menu_item() {
// Code omitted for brevity.
}
/**
* Prepare default settings page arguments
*/
private function menu_settings() {
// Code omitted for brevity.
}
/**
* Container for settings page arguments
*/
public function configure_settings( $settings = array() ) {
return $settings;
}
/**
* Build settings fields
*/
private function settings_fields() {
$settings[\'tab_1\'] = array(
\'title\' => __( \'Tab 1\', \'my_plugin\' ),
\'description\' => __( \'The first settings screen.\', \'my_plugin\' ),
\'fields\' => array(
// Form fields etc. here
),
);
$settings[\'tab_2\'] = array(
\'title\' => __( \'Tab 2\', \'my_plugin\' ),
\'description\' => __( \'The second settings screen.\', \'my_plugin\' ),
\'fields\' => array(
// Form fields etc. here
),
);
$settings = apply_filters( $this->parent->_token . \'_settings_fields\', $settings );
return $settings;
}
/**
* Register plugin settings
*/
public function register_settings() {
if ( is_array( $this->settings ) ) {
// Check posted/selected tab.
$current_section = \'\';
if ( isset( $_POST[\'tab\'] ) && $_POST[\'tab\'] ) { // NONCE warning
$current_section = $_POST[\'tab\']; // NONCE warning
} else {
if ( isset( $_GET[\'tab\'] ) && $_GET[\'tab\'] ) { // NONCE warning
$current_section = $_GET[\'tab\']; // Nonce warning
}
}
foreach ( $this->settings as $section => $data ) {
if ( $current_section && $current_section !== $section ) {
continue;
}
// Add section to page.
add_settings_section( $section, $data[\'title\'], array( $this, \'settings_section\' ), $this->parent->_token . \'_settings\' );
foreach ( $data[\'fields\'] as $field ) {
// Validation callback for field.
$validation = \'\';
if ( isset( $field[\'callback\'] ) ) {
$validation = $field[\'callback\'];
}
// Register field.
$option_name = $this->base . $field[\'id\'];
register_setting( $this->parent->_token . \'_settings\', $option_name, $validation );
// Add field to page.
add_settings_field(
$field[\'id\'],
$field[\'label\'],
array( $this->parent->admin, \'display_field\' ),
$this->parent->_token . \'_settings\',
$section,
array(
\'field\' => $field,
\'prefix\' => $this->base,
)
);
}
if ( ! $current_section ) {
break;
}
}
}
}
/**
* Settings section.
*
* @param array $section Array of section ids.
* @return void
*/
public function settings_section( $section ) {
$html = \'<p> \' . $this->settings[ $section[\'id\'] ][\'description\'] . \'</p>\' . "\\n";
echo $html;
}
/**
* Load settings page content.
*
* @return void
*/
public function settings_page() {
// Build page HTML.
$html = \'<div class="wrap" id="\' . $this->parent->_token . \'_settings">\' . "\\n";
$html .= \'<h2>\' . __( \'Plugin Settings\', \'my_plugin\' ) . \'</h2>\' . "\\n";
$tab = \'\';
//phpcs:disable
if ( isset( $_GET[\'tab\'] ) && $_GET[\'tab\'] ) {
$tab .= $_GET[\'tab\'];
}
//phpcs:enable
// Show page tabs.
if ( is_array( $this->settings ) && 1 < count( $this->settings ) ) {
$html .= \'<h2 class="nav-tab-wrapper">\' . "\\n";
$c = 0;
foreach ( $this->settings as $section => $data ) {
// Set tab class.
$class = \'nav-tab\';
if ( ! isset( $_GET[\'tab\'] ) ) { // NONCE warning
if ( 0 === $c ) {
$class .= \' nav-tab-active\';
}
} else {
if ( isset( $_GET[\'tab\'] ) && $section == $_GET[\'tab\'] ) { // Nonce warning
$class .= \' nav-tab-active\';
}
}
// Set tab link.
$tab_link = add_query_arg( array( \'tab\' => $section ) );
if ( isset( $_GET[\'settings-updated\'] ) ) { // NONCE warning
$tab_link = remove_query_arg( \'settings-updated\', $tab_link );
}
// Output tab.
$html .= \'<a href="\' . $tab_link . \'" class="\' . esc_attr( $class ) . \'">\' . esc_html( $data[\'title\'] ) . \'</a>\' . "\\n";
++$c;
}
$html .= \'</h2>\' . "\\n";
}
$html .= \'<form method="post" action="options.php" enctype="multipart/form-data">\' . "\\n";
// Get settings fields.
ob_start();
settings_fields( $this->parent->_token . \'_settings\' );
do_settings_sections( $this->parent->_token . \'_settings\' );
$html .= ob_get_clean();
$html .= \'<p class="submit">\' . "\\n";
$html .= \'<input type="hidden" name="tab" value="\' . esc_attr( $tab ) . \'" />\' . "\\n";
$html .= \'<input name="Submit" type="submit" class="button-primary" value="\' . esc_attr( __( \'Save Settings\', \'my_plugin\' ) ) . \'" />\' . "\\n";
$html .= \'</p>\' . "\\n";
$html .= \'</form>\' . "\\n";
$html .= \'</div>\' . "\\n";
echo $html;
}
/**
* Main My_Plugin_Settings Instance
*
* Ensures only one instance of My_Plugin_Settings is loaded or can be loaded.
*
* @since 1.0.0
* @static
* @see My_Plugin()
* @param object $parent Object instance.
* @return object My_Plugin_Settings instance
*/
public static function instance( $parent ) {
if ( is_null( self::$_instance ) ) {
self::$_instance = new self( $parent );
}
return self::$_instance;
} // End instance()
}
Form output. 根据选择的选项卡进行更改。
<div class="wrap">
<h2>Heading</h2>
<p>Plugin description.</p>
<h2 class="nav-tab-wrapper">
<a href="/wp-admin/options-general.php?page=my_plugin_settings&tab=tab_1" class="nav-tab nav-tab-active">Tab 1</a>
<a href="/wp-admin/options-general.php?page=my_plugin_settings&tab=tab_2" class="nav-tab">Tab 2</a>
</h2>
<form method="post" action="options.php" enctype="multipart/form-data">
<input type="hidden" name="option_page" value="my_plugin_settings"><input type="hidden" name="action" value="update"><input type="hidden" id="_wpnonce" name="_wpnonce" value="$integer"><input type="hidden" name="_wp_http_referer" value="/wp-admin/options-general.php?page=my_plugin_settings&tab=tab_1">
<h2>Tab 1</h2>
<p>
Description for Tab 1 screen.</p>
<table class="form-table">
<!-- Form table contents -->
</table>
<p class="submit">
<input type="hidden" name="tab" value="upload">
<input name="Submit" type="submit" class="button-primary" value="Save Settings">
</p>
</form>
</div>
原始帖子:我正在使用PHPC和Wordpress-Extra编码标准来检查我的插件代码。我收到以下警告:
警告|在未进行nonce验证的情况下处理表单数据。
相关代码在设置页面中显示选项卡式导航:
class Example_Class {
public function settings_page() {
$tab = \'\';
if ( isset( $_GET[\'tab\'] ) && $_GET[\'tab\'] ) { // WARNING
$tab .= $_GET[\'tab\']; // WARNING
}
if ( isset( $_GET[\'tab\'] ) && $_GET[\'tab\'] ) { // WARNING
$tab .= $_GET[\'tab\']; // WARNING
}
// Show page tabs
if ( is_array( $this->settings ) && 1 < count( $this->settings ) ) {
$html .= \'<h2 class="nav-tab-wrapper">\' . chr( 0x0D ) . chr( 0x0A );
$c = 0;
foreach ( $this->settings as $section => $data ) {
// Set tab class
$class = \'nav-tab\';
if ( ! isset( $_GET[\'tab\'] ) ) { // WARNING
if ( 0 === $c ) {
$class .= \' nav-tab-active\';
}
} else {
if ( isset( $_GET[\'tab\'] ) && $section === $_GET[\'tab\'] ) { // WARNING
$class .= \' nav-tab-active\';
}
}
// Set tab link
$tab_link = add_query_arg( array( \'tab\' => $section ) );
if ( isset( $_GET[\'settings-updated\'] ) ) { // WARNING
$tab_link = remove_query_arg( \'settings-updated\', $tab_link );
}
// Output tab
$html .= \'<a href="\' . $tab_link . \'" class="\' . esc_attr( $class ) . \'">\' . esc_html( $data[\'title\'] ) . \'</a>\' . chr( 0x0D ) . chr( 0x0A );
++$c;
}
$html .= \'</h2>\' . chr( 0x0D ) . chr( 0x0A );
}
}
public function register_settings() {
if ( is_array( $this->settings ) ) {
// Check posted/selected tab
$current_section = \'\';
if ( isset( $_POST[\'tab\'] ) && $_POST[\'tab\'] ) { // WARNING
$current_section = $_POST[\'tab\'];
} else {
if ( isset( $_GET[\'tab\'] ) && $_GET[\'tab\'] ) { // WARNING
$current_section = $_GET[\'tab\']; // WARNING
}
}
// Unrelated code omitted
}
}
}
我以为API自动处理nonce?我应该担心吗?或者代码是否正常?如果没有,我应该如何解决这个问题?
编辑:根据答案,API提供默认nonce<input type="hidden" id="_wpnonce" name="_wpnonce" value="$int">
&;关联的隐藏字段<input type="hidden" name="_wp_http_referer" value="/wp-admin/options-general.php?page=_settings">
. 如何验证?我尝试过,但没有成功:
if ( isset( $_POST[\'tab\'] ) && $_POST[\'tab\'] ) {
if ( ! wp_verify_nonce( \'_wpnonce\' ) ) {
wp_die( \'Go away!\' );
} else {
$current_section = sanitize_text_field( wp_unslash( $_POST[\'tab\'] ) );
}
} else {
if ( isset( $_GET[\'tab\'] ) && $_GET[\'tab\'] ) {
if ( ! wp_verify_nonce( \'_wpnonce\' ) ) {
wp_die( \'Go away!\' );
} else {
$current_section = sanitize_text_field( wp_unslash( $_GET[\'tab\'] ) );
}
}
}