WordPressでカスタムフィールドなどのフォームを使う際、入力内容を保存する必要があります。
この保存の部分がなかなか理解できず、苦手意識がありました。
そのため克服の意味でも保存時に重要な要素であるwp_nonce_fieldについて調べたことをメモとして残しておきたいと思います。
nonce
本題ですが、まず先にnonceに関してコーデックスより引用します(改行は筆者追加)。
nonce(ノンス)は、ある種の誤使用や悪意のある操作から URL やフォームを守るための「一度だけ使われる数値」です。
WordPress の nonce は実際には数値ではなく、数字と英字で作られたハッシュです。
また、本当に一度だけ使われるのではなく、無効になるまでの「有効期間」を持っています。
その期間では、同じユーザーの同じコンテキストに対して、同じ nonce が生成されます。
つまり、ある操作に対する nonce は、 nonce の有効期間が切れるまで同じユーザーについて同じ値を保ちます。
上記nonceを実際に使うことを想定し、以下の2つに分けて記載します。
- form入力時のwp_nonce_field
- 保存時のcheck_admin_referer
form入力時のwp_nonce_field
wp_nonce_fieldに関しては、コーデックスの以下のページから説明文を引用します。
フォームへ hidden フィールドとして追加するために nonce を取得または表示します。
nonce フィールドは、フォームの内容が現在のサイトから来たものであり、他のサイトからではないということを認証するために使われます。nonce は完全な保護を提供するものではありませんが、ほとんどの場合に有効な保護になります。フォーム内で nonce フィールドを使うのはとても重要です。
このままでは少し分かり辛いので、実際にどうなっているのかをもう少し分かりやすくしてみたいと思います。
まず、上記コーデックスにある以下のコードをphpファイル(テンプレートファイルやfunctions.phpなど)form内に入れ込みます。
<form method="post">
<?php wp_nonce_field( 'アクションの名前', 'nonce フィールドの名前' ); ?>
</form>
上記がHTMLとして出力されると以下のようになります。
<form method="post">
<input type="hidden" id="フィールドの名前" name="フィールドの名前" value="生成された値">
</form>
つまり、form内にhidden属性でinputタグが追加されます。
そしてこの生成されたinputタグの値を用いて、正規の手順で送信されたフォームかどうかなどを保存動作前に判断させ、比較的安全な保存実行を行っています。
formタグの必要性 1
あくまでwp_nonce_fieldの設置に関してのみの話になりますが。
出力結果を見ると、wp_nonce_fieldを設置する際にはformタグは必須ではありません。
単にwp_nonce_fieldを記載するだけで、hidden属性のinputタグがちゃんと生成されます。
formタグの必要性 2
カスタムフィールドの値を入力するという点で考える場合も、どうやらformタグは必須では無い模様です。
記事投稿画面内のカスタムフィールドの値は、fromタグのactionで送信されるわけではなく、投稿保存時にsave_postフックで保存が行われるためです。
そのため、以下のような状態でもエラーにはなりません。
<form method="post" action="">
<input>
</form>
最小の記述
前出のコーデックスに載っていますが、一応以下のコードだけでnoceフィールドが生成されます。
<?php wp_nonce_field(); ?>
値保存時の check_admin_referer
WordPressでは、値入力用のフォーム表示動作と値保存用の動作は別で行われます。
そのため、入力時にwp_nonce_fieldで生成した値を、保存時に別の方法で確認しなければなりません。
WordPressでは、noceフィールドの値をテストするためにcheck_admin_refererというものが用意されていますので、以下のコーデックスより引用します。
現在のリクエストが有効な nonce を持っているか、または現在のリクエストが管理画面から参照されたものであるかをテストします。どちらをテストするかは $action が与えられている(これが推奨)かどうかによります。
値をテストする関数なので、これをif文に用いて分岐させることで、想定されたnoceフィールドをもっている場合のみ値の保存を実行できます。
具体的な部分をコーデックスから引用します。
// 失敗すると check_admin_referer() は自動的に "failed" ページを表示して終了 (die) する。
if ( ! empty( $_POST ) && check_admin_referer( 'name_of_my_action', 'name_of_nonce_field' ) ) {
// フォームのデータを処理する。例えばフィールドを更新。
}
この部分のチェックの書き方自体はいくつもあるようで、以下のような書き方もされます。
if (isset($_POST['name_of_nonce_field']) && $_POST['name_of_nonce_field']) {
if (check_admin_referer('name_of_my_action', 'name_of_nonce_field' )) {
// フォームのデータを処理する。
}
}
書き方が違うということは行っていることも違うのですが、どの方法であれ主目的は同じで、nonceフィールドの値を調べています。
check_admin_referer と wp_verify_nonce
nonceフィールドの値をテストする関数として、以下の2つが用意されています。
- check_admin_referer
- wp_verify_nonce
両者の違いを、以下のコーデックスから引用して記載します。
check_admin_referer
WordPress の管理画面内でフォームを送信して処理する場合は、check_admin_referer() 関数を使って nonce を認証します:
現在のリクエストが有効な nonce を持っているか、または現在のリクエストが管理画面から参照されたものであるかをテストします。
管理画面ではcheck_admin_refererの利用が指示されています。
wp_verify_nonce
フォームが送信された先のページで、wp_verify_nonce() 関数を使ってそれを認証できます
nonce が正しいもので有効期限が切れていないことを、指定されたアクションとの関係も含めて確かめます。
check_admin_refererと違い、nonceの値をみているだけ(ではないようですが詳細がわかりませんでした)のため、どこから参照されているのかは判断されない模様です。
記事保存のエラー
今回記事を書く最大の動機だったのですが、カスタムフィールドでnonceを用いた際に以下のエラーがでて内容が保存できない状態に陥りました。
本当に実行していいですか?
今回のようにカスタムフィールドの追加後である場合は、明らかにnonce値がおかしく、フォームに入力した内容が疑われている状態です。
エラーの原因
今回の場合、最初に作成した以下の記述に問題がありました。
wp_nonce_field( 'name_of_my_action','_wpnonce' );
_wpnonceはwp_nonce_fieldにおけるnonceフィールドの初期値なのですが、初期値のままではエラーになってしまいます。
原因調査時になかなかここに気がつかず、かなり時間を食いました…。
参考資料
今回の記事といいますかwp_nonce_fieldの概要や実際のコードに関しては、購入してから2年ぐらい読めていなかった(Kindle版の存在は初めて知りました)下記の本から主に学びました。
率直に書きますと、当記事を読むぐらいならこの本を買って読んだ方が遥かに勉強になります。
個人的には紙の本の方が参照しやすく頭に入りやすいので、コーデックスを見るにしてもこの本がお勧めできることに違いはありませんが。
結び
理解しきれていない所もありますが、以上が現在個人的に理解している範囲の事柄です。
内容に間違いがあればご指摘いただけますと幸いです。
8人がこの記事を評価
役に立ったよという方は上の「記事を評価する」ボタンをクリックしてもらえると嬉しいです。
連投防止のためにCookie使用。SNSへの投稿など他サービスとの連動は一切ありません。