正しく指摘されているように、元の投稿は「ひどく不十分」だったため、これを言い換えています。さまざまな設定のタブ付きのビューを持つプラグインを作成しました。これは Wordpressプラグインテンプレート に基づいています。テンプレートはWP設定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
)。
問題は次のとおりです。タブのナンスを作成し、ページが読み込まれたとき、またはユーザーがいずれかのタブを選択したときにそれをチェックする方法です。
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()
}
フォーム出力。選択されているタブによって変わります。
<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>
元の投稿:WordCSでPHPCSを使用しています-追加 プラグインコードをチェックするためのコーディング標準。私はこの警告を受けています:
警告| 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がノンスを自動的に処理すると思いましたか?心配すべきですか?または、コードはそのままでも大丈夫ですか?そうでない場合、どうすれば修正できますか?
編集:回答に照らして、APIはデフォルトのナンス<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'] ) );
}
}
}
* -options nonceを出力するsettings_fields
呼び出しを使用しており、データをoptions.php
ファイルに渡して保存するため、APIがフォームパーツのnonceを処理します。設定を保存する前に、あなたのためにそのナンス。この部分は、Settings APIが実際に行います。
ただし、タブコードはその形式ではありません。それは単なるリンクです。リンクにはノンスがありません。GETパーツからのデータを使用する必要があるコードは、ノンスチェックを行いません。
ここで、データをここに保存しない限り、技術的にはこれで問題ありません。 nonceの目的は、データを送信するときに意図を確認することです。実際の方法で保存または使用されるデータを送信しない場合は、その意図を確認する必要はありません。ここでタブを選択する唯一のことは、ページに表示されるフィールドを変更することです。
タブを完全になくし、フォーム全体をすべての設定で同じページに表示することを検討してください。タブ形式の編成が必要な場合は、JavaScriptまたはCSSを使用してページを装飾することをお勧めします。複数のページで構成したり、ページの上部にあるリンクを使用してそれらに移動したりするよりも、一度にすべての変更を行いたいユーザーにとって、フォームをそのままにする方がよい場合があるため、アクセシビリティについても検討してください。
これは適切な警告です。 nonceを処理するAPIはありません。
$ _GETまたは$ _POSTから読み取る前に、verify_nonce()またはcheck_admin_referer()を使用する必要があります。
そして、単にWordPressという名前のコーディング標準の完全なセットを使用することをお勧めします。これには、Core、Docs、Extraが含まれます。