サーバーサイドの PHP スクリプトには、AJAX 通信を実装するために必要な2つの部分があります。一つは、jQuery スクリプトを Web ページ上でエンキューし、jQuery スクリプトが必要とする PHP の値をローカライズすることです。もう一つは、実際の AJAX リクエストの処理です。
スクリプトのエンキュー
このセクションでは、WordPress を初めて使用する経験豊富なコーダーがつまずく、WordPress における AJAX の2つの大きな癖について説明します。ひとつは、ページの head セクションにメタリンクを正しく表示するために、スクリプトをエンキューする必要があることです。もうひとつは、すべての AJAX リクエストは wp-admin/admin-ajax.php
を通して送られる必要があるということです。プラグインのページに直接リクエストを送ってはいけません。
エンキュー
wp_enqueue_script()
関数を使って、WordPress にあなたのスクリプトへのメタリンクをページのセクションに挿入してもらいましょう。このようなリンクをヘッダーテンプレートにハードコードしないでください。プラグイン開発者として、あなたはヘッダーテンプレートにすぐにアクセスできませんが、このルールについては言及しておく価値があります。
エンキュー関数は、以下の5つのパラメータを受け付けます:
$handle
は、スクリプトの名前です。$src
は、スクリプトの場所を定義します。移植性のために、plugins_url()
を使用して適切な URL を作成してください。プラグイン以外のスクリプトをエンキューする場合は、関連する関数を使用して適切な URL を作成してください – 決してハードコードしないでください。$deps
は、配列で、新しいスクリプトが依存するスクリプト (jQuery など) を処理できます。jQuery を使用して AJAX リクエストを送信するので、少なくとも配列に'jquery'
をリストアップする必要があります。$ver
は、バージョン番号をリストアップします。$args
は、(in_footer
キーを介して) フッターの出力や、defer
やasync
など (strategy
キーを介して) スクリプトを読み込む方法を定義する引数の配列です。これは WordPress バージョン6.3のパラメータ$in_footer
を置き換え/オーバーロードします。
wp_enqueue_script(
'ajax-script',
plugins_url( '/js/myjquery.js', __FILE__ ),
array( 'jquery' ),
'1.0.,0',
array(
'in_footer' => true,
)
);
プラグインのコードページが読み込まれたときに、そこから直接スクリプトをエンキューできません。スクリプトは、いくつかのアクションフックのいずれかからエンキューする必要があります。どれを使うかは、スクリプトがどのようなページにリンクされる必要があるかによって決まります。管理ページには admin_enqueue_scripts
を使います。ログインページを除き、フロントエンドのページでは wp_enqueue_scripts
を使い、ログインページの場合は login_enqueue_scripts
を使います。
フック admin_enqueue_scripts
は、現在のページのファイル名をコールバックに渡します。この情報を使って、スクリプトが必要なページでのみスクリプトをエンキューします。フロントエンド版は、何も渡しません。その場合は、is_home()
、is_single()
などのテンプレートタグを使い、スクリプトを必要な場所にだけエンキューします。これが今回の例の完全なエンキューコードです:
add_action( 'admin_enqueue_scripts', 'my_enqueue' );
function my_enqueue( $hook ) {
if ( 'myplugin_settings.php' !== $hook ) {
return;
}
wp_enqueue_script(
'ajax-script',
plugins_url( '/js/myjquery.js', __FILE__ ),
array( 'jquery' ),
'1.0.0',
array(
'in_footer' => true,
)
);
}
なぜここでは名前付き関数を使うのに、jQuery では匿名関数を使うのでしょうか ? クロージャが PHP でサポートされたのは最近のことだからです。jQuery ではかなり以前からサポートされていました。古いバージョンの PHP を使用している人もいるかもしれないので、互換性を最大化するために常に名前付き関数を使用しています。PHP のバージョンが最近のもので、自分のインストールのためだけに開発するのであれば、どうぞお好きな様にクロージャを使ってください。
登録とエンキュー
他のチュートリアルでは、wp_register_script()
を使用する例をよく見かけます。これは問題ありませんが、使用は任意です。任意ではないのは wp_enqueue_script()
です。この関数は、スクリプトファイルが Web ページに正しくリンクされるために呼び出されなければなりません。では、なぜスクリプトを登録するのでしょうか ? スクリプトを登録すると、便利なタグやハンドルが作成され、必要に応じてコードのさまざまな部分でスクリプトを簡単に参照できる様になります。スクリプトを読み込むだけで、コード内の他の場所でスクリプトを参照しない場合は、スクリプトを登録する必要はありません。
スクリプトの遅延読み込み
WordPress は、WordPress 6.3で導入された新しい配列パラメータ $args
内のキー strategy
によって、wp_register_script()
関数と wp_enqueue_script()
関数を介して、スクリプトを読み込む方法を指定するためのサポートを提供します。
サポートされているオプションは、以下の通りです:
- defer
- パラメータ
$args
に、'strategy' => 'defer'
の配列キーと値のペアを指定して追加されます。 - スクリプト属性 defer を介して遅延実行と指定されたスクリプトは、DOM ツリーが完全に読み込まれた後 (ただし
DOMContentLoaded
イベントやウィンドウロードイベントの前) に実行されます。遅延されたスクリプトは、非同期スクリプトとは異なり、DOM に出力/追加された順番に実行されます。
- パラメータ
- async
- パラメータ
$args
に、'strategy' => 'async'
の配列キーと値のペアを指定することで追加されます。 - スクリプト属性
async
を介して非同期実行と指定されたスクリプトは、ブラウザに読み込まれるとすぐに実行されます。非同期スクリプトは実行順序が保証されていません。スクリプト B は (スクリプト A の後に DOM に追加されたが) スクリプト A よりも先に読み込みが完了する可能性があるため、最初に実行される可能性があります。このようなスクリプトは、DOM が完全に構築される前に実行されることもあれば、DOMContentLoaded
イベントの後に実行されることもあります。
- パラメータ
以下は、プラグイン内の追加スクリプト・エンキューの読み込み方法を指定する例です:
wp_register_script(
'ajax-script-two',
plugins_url( '/js/myscript.js', __FILE__ ),
array( ajax-script ),
'1.0.,0',
array(
'strategy' => 'defer',
)
);
wp_enqueue_script()
を使う場合も、同じアプローチが適用されます。上の例では、遅延処理でスクリプト 'ajax-script-two'
を読み込むことを示しています。
遅延スクリプトを読み込む方法を指定する場合、スクリプトの依存関係ツリー (その依存関係および / または従属関係) を考慮することで、あるスクリプトには有効ですが、ツリー内の他のスクリプトには有害な手順を適用して、意図しない実行順序のずれを引き起こさない様に、「適格な方法」を決定します。このようなロジックの結果、パラメータ $args
を介して渡された意図した読み込み方法は、最終的な (選択された) 順序にはならないかもしれませんが、意図した方法に対して不利になる (または意図した方法よりも厳しくなる) ことはありません。
Nonce
jQuery AJAX リクエストを検証できるように、nonce を作成する必要があります。そうすることで、jQuery の AJAX リクエストを、未知の悪意のあるリクエストではなく、正当なリクエストとして検証できる様になります。この値を知っているのは、PHP スクリプトと jQuery スクリプトだけです。リクエストを受け取ったときに、ここで作成したのと同じ値であることを検証できます。これは、nonce を作成する一つの方法です:
$title_nonce = wp_create_nonce( 'title_example' );
パラメータ title_example
には、任意の文字列を指定できます。この文字列は nonce が何に使われるかに関連したものであることが推奨されますが、何でもかまいません。
ローカライズ
jQuery セクションを思い起こせば、jQuery で使用するために PHP が作成したデータは、my_ajax_obj
という名前のグローバルオブジェクトで渡されました。私たちの例では、このデータは nonce と admin-ajax.php
への完全な URL でした。オブジェクトのプロパティを割り当て、グローバルな jQuery オブジェクトを作成するプロセスを「ローカライズ」と呼びます。これは、wp_localize_script()
を使用した例で使用したローカライズのコードです。
wp_localize_script(
'ajax-script',
'my_ajax_obj',
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => $title_nonce,
)
);
スクリプトハンドル ajax-script
がどのように使用され、グローバルオブジェクトが正しいスクリプトに割り当てられるかに注意してください。このオブジェクトは、すべてのスクリプトに対してではなく、私たちのスクリプトに対してグローバルです。ローカライズは、スクリプトをエンキューするのと同じフックから呼び出すこともできます。nonce の作成も同様ですが、この特定の関数は事実上どこでも呼び出すことができます。これらすべてを1つのフック・コールバックにまとめると、次のようになります:
add_action( 'admin_enqueue_scripts', 'my_enqueue' );
/**
* Enqueue my scripts and assets.
*
* @param $hook
*/
function my_enqueue( $hook ) {
if ( 'myplugin_settings.php' !== $hook ) {
return;
}
wp_enqueue_script(
'ajax-script',
plugins_url( '/js/myjquery.js', __FILE__ ),
array( 'jquery' ),
'1.0.0',
true
);
wp_localize_script(
'ajax-script',
'my_ajax_obj',
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'title_example' ),
)
);
}
current_user_can()
を使用することを忘れないでください。
AJAX アクション
サーバーサイドの PHP コードのもう一つの主要な部分は、POST されたデータを受け取り、それを使って何かを行い、適切なレスポンスをブラウザに送り返す実際の AJAX ハンドラです。これは WordPress のアクションフックの形を採ります。どのフックタグを使うかは、ユーザーがログインしているかどうか、そして jQuery スクリプトが action: の値としてどのような値を渡したかによって決まります。
$_GET
、$_POST
、$_COOKIE
vs. $_REQUEST
$_GET
や $_POST
のような PHP のスーパーグローバルを使って、フォームや ($_COOKIE
を使って) Cookie から値を取得したことがあるでしょう。もしかすると、代わりに $_REQUEST
を使用することを好むかもしれませんし、少なくとも使用されているのを見たことがあるかもしれません。ちょっと格好よく – リクエストメソッド (POST
または GET
) に関係なく、フォームの値を取得できます。両方のメソッドを使用するページには最適です。その上、Cookie の値も取得できます。非常に便利ですが、そこに悲劇的な欠点があります。名前が衝突した場合、Cookie の値がフォームの値を上書きします。従って、悪意ある誰かがブラウザ上で偽造 Cookie を作成するのはとんでもなく簡単で、リクエストから期待されるかもしれないフォームの値を上書きしてしまいます。$_REQUEST
は、ハッカーにとって フォームの値に任意のデータを注入する簡単なルートです。安全性を高めるために、特定の変数に固執し、1つの方法ですべてを満たすことは避けてください。
AJAX 交換はプラグインの設定ページに対して行われるので、ユーザーはログインしている必要があります。jQuery セクションを思い起こしてほしいのですが、action:
の値は "my_tag_count"
です。つまり、アクションフックタグは wp_ajax_my_tag_count
になることを意味します。AJAX 交換が現在のログインしていないユーザーによって利用される場合、アクションフックタグは wp_ajax_nopriv_my_tag_count
となります。アクションをフックするための基本的なコードは次のようになります:
add_action( 'wp_ajax_my_tag_count', 'my_ajax_handler' );
/**
* Handles my AJAX request.
*/
function my_ajax_handler() {
// Handle the ajax request here
wp_die(); // All ajax handlers die when finished
}
AJAX ハンドラが最初に行うべきことは、jQuery が送信した nonce をcheck_ajax_referer()
で確認することで、スクリプトがエンキューされたときにローカライズされた値と同じでなければなりません。
check_ajax_referer( 'title_example' );
提供されるパラメータは、wp_create_nonce()
に 以前に提供されたパラメータと同じでなければなりません。nonce がチェックアウトされない場合、この関数は単に強制終了します。これが真の nonce であった場合、それが使用されたので、その値はもはや無意味です。新しい nonce を生成してコールバックスクリプトに送り、次のリクエストで使えるようにします。しかし、WordPress の nonce は24時間有効ですので、それをチェックする以外には何もする必要はありません。
データ
nonce を避けて、ハンドラは、$_POST['title']
に含まれる jQuery スクリプトによって送信されたデータを処理できます。まず、値を新しい変数に代入し、wp_unslash()
を通して予期しない引用符を削除します。
$title = wp_unslash( $_POST['title'] );
ユーザーが選択した内容は、update_user_meta()
を使ってユーザーメタに保存できます。
update_user_meta( get_current_user_id(), 'title_preference', sanitize_post_title( $title ) );
次に、選択したタイトルタグの投稿数を取得するためのクエリーを作成します。
$args = array(
'tag' => $title,
);
$the_query = new WP_Query( $args );
最後に、レスポンスを jQuery スクリプトに送り返します。データを送信するにはいくつかの方法があります。この例の詳細を説明する前に、いくつかのオプションを見てみましょう。
XML
PHP による XML のサポートには改善の余地があります。幸いなことに、WordPress は WP_Ajax_Response
クラスを提供しており、このタスクを簡単に行うことができます。WP_Ajax_Response
クラスは、XML フォーマットのレスポンスを生成し、ヘッダーに正しいコンテンツタイプを設定し、レスポンス xml を出力して終了し — 適切な XML レスポンスを保証します。
JSON
このフォーマットは軽量で使いやすく、WordPress はレスポンスを json エンコードして表示し、終了し – 効果的に WP_Ajax_Response
を置き換えることができる wp_send_json
関数を提供しています。WordPress はまた、wp_send_json_success
関数と wp_send_json_error
関数を提供し、適切な done() または fail() コールバックを JS で発生させることができます。
その他
送出側と受信側が協調している限り、どのような方法でもデータを転送できます。カンマ区切りやタブ区切りのようなテキストフォーマットは、多くの選択肢の一つです。少量のデータであれば、生のストリームを送信すれば十分かもしれません。この例ではそうします – 実際の置き換え HTML を送るだけで、他には何もしません。
echo esc_html( $title ) . ' (' . $the_query->post_count . ') ';
実際のアプリケーションでは、何らかの理由でアクションが失敗する可能性、たとえば、データベースサーバーがダウンしている場合など、を考慮しなければなりません。レスポンスは、この不測の事態を許容するものでなければならず、そして、レスポンスを受け取った jQuery スクリプトは、それに応じて動作する必要があり、おそらく、後で再試行するようにユーザーに伝えることでしょう。
終了
ハンドラがすべてのタスクを終了したら、終了する必要があります。WP_Ajax_Response
または wp_send_json*
関数を使用している場合、これは自動的に処理されます。そうでない場合は、WordPress の wp_die()
関数を使用するだけです。
AJAX ハンドラの概要
この例の完全な AJAX ハンドラは、次のようになります:
/**
* AJAX handler using JSON
*/
function my_ajax_handler__json() {
check_ajax_referer( 'title_example' );
$title = wp_unslash( $_POST['title'] );
update_user_meta( get_current_user_id(), 'title_preference', sanitize_post_title( $title ) );
$args = array(
'tag' => $title,
);
$the_query = new WP_Query( $args );
wp_send_json( esc_html( $title ) . ' (' . $the_query->post_count . ') ' );
}
/**
* AJAX handler not using JSON.
*/
function my_ajax_handler() {
check_ajax_referer( 'title_example' );
$title = wp_unslash( $_POST['title'] );
update_user_meta( get_current_user_id(), 'title_preference', sanitize_post_title( $title ) );
$args = array(
'tag' => $title,
);
$the_query = new WP_Query( $args );
echo esc_html( $title ) . ' (' . $the_query->post_count . ') ';
wp_die(); // All ajax handlers should die when finished
}