自分のWordPressで学ぶPHP講座(AIの変更を判定する)
区分2 読むための最低限★★★WordPressは、ページを作る途中の決まった場所に、小さな差し込み口をたくさん用意しています。 差し込み口の名前がフック名で、自分の関数をそこに登録しておくと、その時点で呼んでもらえます。
駅の発車ベルにたとえると分かりやすいと思います。 「3番線に電車が入ったらベルを鳴らす」と登録しておけば、電車が入る時刻は気にしなくても、その瞬間に勝手に鳴ります。コードを上から順に実行していくのではなく、「いつ動くか」を登録で決める仕組みです。
No.02で見たとおり、子テーマの functions.php は、毎回のアクセスで読み込まれます。 そこに書かれているのは、実行したい処理というより、フックへの登録の一覧だと思ってください。
| 何のことか | |
|---|---|
| アクション(action) | 「その時点で、これをやって」。何かを出力したり、設定したりする。値は返さない |
| フィルター(filter) | 「その値を、通す前に加工して」。受け取った値を直して、必ず返す |
| コールバック | 登録する側の関数のこと。フックが「呼び返す」ので、この名前で呼ばれる |
| 優先度 | 同じフックに複数登録された時の順番。小さい数ほど先に動く。何も書かなければ 10 |
アクションとフィルターの違いが、この単元のいちばん大きな分かれ目です。
どちらも同じ形で書きます。
add_action( 'フック名', 'コールバック関数', 優先度, 受け取る値の数 );
add_filter( 'フック名', 'コールバック関数', 優先度, 受け取る値の数 );
後ろの2つは省略できます。省略すると、優先度は 10、受け取る値は 1 つです。 自分のサーバーの functions.php には、こういう実物があります。
function my_slide_max() {
return 8;
}
add_filter( 'lightning_top_slide_count_max', 'my_slide_max' );
読み方は次のとおりです。
add_filter なので、フィルター。値を加工する側lightning_top_slide_count_max。Lightning(親テーマ)が用意した、スライドの最大枚数を決める差し込み口my_slide_max。8 を返しているつまり「親テーマが最大枚数を聞いてくる場面で、8 と答える」という登録です。 このように、フックの名前は WordPress 本体だけでなく、テーマやプラグインも自分で作れます。名前から出どころを探すと、どこの話か分かります。
アクションとフィルターの見分け方は1つです。関数の最後に return で値を返しているのがフィルター、返さずに出力や設定をしているのがアクション。 フィルターなのに return が無いコードは、受け取った値を握りつぶすので、AIが出してきた時はまず疑います。
コールバックの書き方は2種類あります。 1つは上のように、関数に名前を付けて、名前の文字列で渡す書き方です。もう1つは、名前を付けずにその場で書く書き方で、無名関数(クロージャとも呼びます)といいます。
実物の robots.txt に足している部分です。
add_filter( 'robots_txt', function ( $output, $public ) {
$additions = "\n# ゴミURL対策\n";
$additions .= "Disallow: /*?feed=\n";
$additions .= "Disallow: /*?paged=\n";
return $output . $additions;
}, 10, 2 );
function ( ... ) { ... } の部分が無名関数で、これが第2引数の位置に入っています。
最後の }, 10, 2 ); が、閉じ括弧のあとの優先度(10)と、受け取る値の数(2)です。ここが読み取りのコツで、関数の中身が長くても、登録の条件は閉じ括弧のすぐ後ろに書いてあると覚えてください。
2つの値を受け取っているのは、robots_txt が「現在の内容」と「公開設定」の2つを渡してくるからです。
受け取る数を 1 のままにして、関数の側で2つ要求すると、PHPが引数不足のエラーを出します。
無名関数には、後から他のコードが登録を外しにくい、という性質があります。 外す側は名前で指定するからです。見た目の違いだけで、書き方の良し悪しではありません。
同じフックに複数の登録があると、優先度の小さい順に動きます。同じ数なら、先に登録された方が先です。
実物の functions.php では、wp_head(ページの先頭部分を出す時点)に3つの登録があり、数字が違います。比べるために、何も書かない場合の 10 も並べました。
| 内容 | 優先度 |
|---|---|
| /line/ ページに noindex(検索結果に載せないでという目印)を出す | 0 |
| 店の構造化データ(JSON-LD。検索エンジン向けに店の情報を決まった形で書いた部分) | 1 |
| (参考)何も書かない場合 | 10 |
| よくある質問の構造化データ | 20 |
数字が小さいほど先に動きます。 コメントに「優先度を下げて」とあれば、それは数字を大きくして後ろに回した、という意味です。この1点だけ押さえておけば、順番が気になる場面で読み解けます。
AIの変更を読む時、まずフック名を見て、「いつ動くか」を言えるようにします。 全部は覚えず、次の表を引ける状態にしておけば十分です。
| フック名 | いつ動くか | 種類 | よくある使い道 |
|---|---|---|---|
| init | WordPressの準備が終わった直後。まだ画面は出していない | アクション | cookie を送る、設定を決める |
| wp_enqueue_scripts | CSS・JavaScriptの読み込みを決める時(公開側の画面) | アクション | 追加のCSSやフォントを読み込む |
| wp_head | HTMLの head の中を出す時 | アクション | メタタグ、構造化データを出す |
| body_class | body タグの class を作る時 | フィルター | 見た目の切り替え用の class を足す |
| robots_txt | robots.txt(検索エンジンのロボットに、見てよい所・見ないでほしい所を伝えるファイル)を作る時 | フィルター | クロールさせたくないURLを足す |
見分け方は名前にあります。
wp_ が付くものは画面を作る流れの中、init は画面の前の準備、_class・_txt のように「何を作るか」が名前にあるものは、その中身を加工するフィルターです。
ここが、フックが初めての人のいちばんの落とし穴です。 functions.php に書かれた順に動くのではなく、登録したフックが来た順(同じ時点なら優先度の順)に動きます。
次の実物で確かめてみましょう。子テーマに足された、cookie を扱う部分です。
add_action( 'init', 'lk_look_preview_cookie' );
add_action( 'init', 'lk_look_no_cache_when_previewing', 1 );
function lk_look_no_cache_when_previewing() { ... }
function lk_look_preview_cookie() { ... }
add_filter( 'body_class', 'lk_look_body_class' );
コード中の ... は、この講座で中身を省略した印です(実物には処理が入っています)。
ファイルでは cookie の方が先に登録されています。 でも2行目は優先度が 1 なので、同じ init の中でも先に動きます。そのあとに cookie が送られて、さらに後ろの body_class の時点で、class が足されます。
動く順は、init の優先度1、init の優先度10、body_class の順です。 init は画面を出し始める前の準備の時点、wp_head は画面の head の中を出している途中の時点なので、init の方が先に動きます。
body_class のように値を加工するフィルターの関数は、受け取った値($classes という、class名を並べた配列)を直して、return で返します。返した配列が、そのまま body タグの class として使われます。 この順番が大事なのは、たとえば cookie を送る処理が、画面の出力を始めたあとに動いてしまうと、送れなくなるからです(詳しくはNo.07、後で出ます)。
AIの変更でフックが変わっていたら、「前より早い時点か、遅い時点か」を見てください。 init を wp_head に変えただけでも、動けるかどうかが変わります。
実物には、見慣れない名前も出てきます。
add_action( 'lightning_top_slide_after', 'lk_look_front_hero' );
add_action( 'lightning_site_body_before', 'lk_look_front_hero' );
lightning_ で始まるものは、親テーマ Lightning が用意したフックです。
トップページのスライダーの直後と、body の直前に、帯を出す登録です。同じ関数を2つのフックに付けているのは、片方が動かない場合の保険だと、コメントにあります。
ここでは「テーマやプラグインが作ったフックは、名前の頭にそのテーマ・プラグインの名前が付く」と分かれば十分です。 中身の細かい所は、AIに「この関数はどのフックで動くか」と聞けば返ってきます。
○ よくある勘違い:「関数を書いたから動く」→ 登録(add_action/add_filter)が無いと、関数は書いてあるだけで一度も呼ばれません
○ よくある勘違い:「フィルターは出力する」→ フィルターは値を返す側です。その場で echo するのは多くの場合アクションです
○ 頼み方の例:「この変更で追加されたフックを全部、フック名・いつ動くか・優先度の3点で表にして」
○ 公式の一覧は developer.wordpress.org の Plugin API/Action Reference・Filter Reference にあります。調べたい名前が出てきたら、そこで検索します
○ 関係する単元:No.02(子テーマの functions.php)/No.04(if と isset)/No.07(cookie と出力の安全)
add_action はやってほしい事を登録し、add_filter は値を加工して return する。コードはフック名の「その時点」で動く。優先度は小さい数が先。
問1 AIが「トップのスライドを6枚にしました」と言って、add_filter( 'lightning_top_slide_count_max', 'my_slide_max' ); と、function my_slide_max() { return 6; } を出してきました。登録として正しい読み方は?
問2 AIが body_class に class を足す関数を出してきました。関数の中身は $classes[] = 'look-new'; の1行だけで、return がありません。問題点は?
問3 次の2行を見て、先に動くのはどちらでしょうか。add_action( 'init', 'func_a' ); と add_action( 'init', 'func_b', 1 ); の2行があります。
問4 AIが wp_head に出していた処理を init に変えました。確認すべき点として合っているのは?
出典:WordPress公式 Plugin Handbook「Hooks」「Actions」「Filters」(https://developer.wordpress.org/plugins/hooks/)/add_action(https://developer.wordpress.org/reference/functions/add_action/)/add_filter(https://developer.wordpress.org/reference/functions/add_filter/)/PHP公式「無名関数」(https://www.php.net/manual/ja/functions.anonymous.php)/自分のサーバーの子テーマの functions.php。