• WordPress 4.7のWP_Hookは$wp_filterを直接変えるcodeから見直す

WordPress 4.7のWP_Hookは$wp_filterを直接変えるcodeから見直す

公開日: 執筆:佐藤

WordPress 4.7を試す前に、themeとpluginの中で$wp_filterを直接操作しているcodeを探します。今回変わるのは、actionとfilterを保持する内部構造です。通常のadd_action()、add_filter()、remove_action()、remove_filter()を使うcodeは、ほとんどの場合そのまま動きます。一方で、$wp_filterを配列として追加、削除、判定しているcodeは、更新後に動かない箇所があります。

重要なのは、$wp_filterを見つけたすべてのcodeが同じ危険度ではないことです。callbackを直接追加する処理はfatal errorになります。callbackを直接削除する処理も失敗します。配列かどうかを調べる処理は、WP_Hook objectになるため期待どおりに判定できません。反対に、callbackを読むだけの一部の処理は互換性のため残ります。検索結果を一律に書き替えず、何をしているかで分けます。

$wp_filterは、画面の設定値ではありません。WordPressがhookへ登録された処理を管理するための内部の変数です。内部の配列に触れるcodeは、たまたま現在の形で動いていることがあります。しかし、配列の並びや中身を前提にしたままでは、本体が管理方法を変えたときに保守する側が追いつけません。公開APIを使うのは書き方をそろえるためだけでなく、本体が約束している操作だけを使うためです。

themeのfunctions.phpだけを検索して終えないことも大切です。独自plugin、mu-plugin、外部から読み込むPHP、子themeを使っていれば親themeも対象になります。以前の制作会社が追加した小さなpluginに直接操作が残っていることがあります。管理画面の見た目に異常がなくても、投稿保存、メール送信、本文の書き換えのように、特定の操作で初めてhookが動く処理があります。どの画面で問題が起きるかではなく、どのhookを使っているかから確認範囲を決めます。

やりたい処理を公開APIへ置き替える

やりたいこと 避ける操作 使う方法
callbackを追加する $wp_filterの配列へ直接追加する add_action() または add_filter()
callbackを外す $wp_filterの配列から直接削除する remove_action() または remove_filter()
callbackの有無を調べる 配列かどうかを調べる has_action() または has_filter()
実行順を替える 配列pointerをnext()やprev()で動かす priorityを指定して登録する

説明用の仮例として、投稿を保存した直後に独自の処理を追加する場合を考えます。$wp_filter['save_post']の中身を組み替えるのではなく、add_action( 'save_post', '関数名', 優先度, 引数の数 )の形で登録します。外すときも、同じhook名、関数名、優先度を指定してremove_action()を使います。何を追加し、いつ動かし、どの引数を受け取るかがcodeから読める形になります。

優先度を変えたいときも、配列の順番を直接動かしません。たとえば、標準の処理より先に動かしたい理由があるなら、その理由に合うpriorityを登録時に指定します。更新前の内部構造に合わせて順番を動かす方法は、WordPress側が実行順を管理する前提とぶつかります。

callbackを外せない問題は、関数名だけを見て直すと残ることがあります。remove_action()とremove_filter()は、追加したときと同じhook名、callback、priorityを指定して初めて対象を見つけます。追加した側がpriorityを省略し、外す側だけ別の値を指定すれば、見た目にはremoveするcodeがあっても処理は残ります。直接配列から消していたcodeを置き替えるときは、登録している場所も同時に探し、追加と削除を対にして確認します。

仮に、あるpluginが本文を表示する前に文言を加え、theme側がそれを外そうとしている場合を考えます。themeがpluginの内部配列へ触れて外すのではなく、pluginがどのhookへ、どのcallbackを、どのpriorityで追加しているかを確認します。その条件で公開APIから外せるなら、WordPressの更新後も意図がcodeに残ります。外せない場合は、pluginの設定で止められないか、表示する側の設計を替えるべきかを検討します。無理に内部構造へ触れる方法を増やさない方が、次の更新で調べる範囲を小さくできます。

更新作業と保守の責任を分ける

PHPの警告や停止が心配だからと、WordPress本体の更新を先延ばしにしても、独自codeの問題は残ります。更新する担当者と、themeやpluginを直せる担当者が別なら、両方が同じ場所を確認できるようにします。本体の更新だけを依頼して、独自codeの検査が抜けると、公開後に原因を切り分けることになります。

まず、theme、plugin、独自に読み込むPHPを対象に$wp_filterを検索します。見つかった箇所は次の三つへ分けます。

  1. 公開APIを呼び出しているだけの箇所。
  2. callbackを追加または削除している箇所。
  3. 配列かどうか、callbackの順番、priorityを直接調べたり動かしたりしている箇所。

二番と三番が見つかったら、該当するhook、追加または削除したいcallback、必要なpriorityを記録します。修正後は、そのhookが動く管理画面の操作と公開ページを一つずつ確認します。投稿保存なら投稿を保存し、公開画面を変えるfilterなら該当ページを表示します。PHPの文法確認だけでは、callbackの順番や二重実行までは分かりません。

確認は、公開siteを最初の試験場所にしない方がよいです。更新前のfilesとdatabaseを戻せる場所で、本体と独自codeの組み合わせを先に試します。確認中に画面が白くなった場合、PHP errorの表示だけを消して作業を続けると、何が止まったか分からなくなります。変更したfile、検索で見つけた箇所、置き替えたAPI、試した操作、結果を短く残します。復旧できる状態を用意することと、修正が正しいと確かめることは別の作業です。

一つのhookで問題が見つかったからといって、ほかの直接操作まで同じ修正で片付くとは限りません。追加、削除、実行順の変更、存在確認では、必要な公開APIと確認方法が違います。検索結果の件数を減らすことを目的にせず、各箇所で何を実現したいのかを言葉にします。そのうえで公開APIへ置き替えられるものから進めれば、必要な確認を省かずに更新作業を進められます。

今回の変更はnightly buildで試せる段階です。正式版への更新日を決める前に、独自codeを使うsiteで確認する範囲を決めます。最初の一手は、$wp_filterを検索し、直接callbackを追加または削除している一箇所を公開APIへ置き替えることです。その一箇所で確認方法を固めてから、残りへ広げます。

戻る