自分のWordPressで学ぶPHP講座(AIの変更を判定する)

区分2 読むための最低限★★★

No.05 フック(add_action・add_filter)

WordPressのPHPは、ほぼ全部が「フック」に引っかけて動きます。AIが出す変更の大半が add_action か add_filter で始まるので、この単元は区分2の中でいちばん大事です。しっかりやります。
目次
  1. 第0章 前提知識
  2. 第1章 登録の書き方
  3. 第2章 いつ動くかの読み方
  4. 確認問題

第0章 前提知識

① フック :「この時点になったら、これも動かして」と登録する差し込み口

WordPressは、ページを作る途中の決まった場所に、小さな差し込み口をたくさん用意しています。 差し込み口の名前がフック名で、自分の関数をそこに登録しておくと、その時点で呼んでもらえます。

駅の発車ベルにたとえると分かりやすいと思います。 「3番線に電車が入ったらベルを鳴らす」と登録しておけば、電車が入る時刻は気にしなくても、その瞬間に勝手に鳴ります。コードを上から順に実行していくのではなく、「いつ動くか」を登録で決める仕組みです。

No.02で見たとおり、子テーマの functions.php は、毎回のアクセスで読み込まれます。 そこに書かれているのは、実行したい処理というより、フックへの登録の一覧だと思ってください。

② 似た言葉との違い

何のことか
アクション(action) 「その時点で、これをやって」。何かを出力したり、設定したりする。値は返さない
フィルター(filter) 「その値を、通す前に加工して」。受け取った値を直して、必ず返す
コールバック 登録する側の関数のこと。フックが「呼び返す」ので、この名前で呼ばれる
優先度 同じフックに複数登録された時の順番。小さい数ほど先に動く。何も書かなければ 10

アクションとフィルターの違いが、この単元のいちばん大きな分かれ目です。

第1章 登録の書き方

① add_action と add_filter 必ず覚える

どちらも同じ形で書きます。

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' );

読み方は次のとおりです。

  1. add_filter なので、フィルター。値を加工する側
  2. フック名は lightning_top_slide_count_max。Lightning(親テーマ)が用意した、スライドの最大枚数を決める差し込み口
  3. 動かす関数は my_slide_max。8 を返している
  4. 優先度と値の数は省略されているので、10 と 1

つまり「親テーマが最大枚数を聞いてくる場面で、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点だけ押さえておけば、順番が気になる場面で読み解けます。

第2章 いつ動くかの読み方

① よく出るフック名 必ず覚える

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。