WordPress 徹底解析(アクションフックのプラグインをつくる編)

「WordPress 徹底解析」シリーズです。

異常に忙しくなったり体の調子を悪くしてみたりで、できていなくてごめんなさい。がんばろう。というわけで、お久しぶりです!

なので脈略がなくなってきましたが「アクションフックのプラグインをつくる編」やってみまます。 🙂


アクションフック

前回書きました「WordPress 徹底解析(カスタマイズとフィルターとアクション編)」からアクショフックの実例になります。

WordPress 徹底解析(カスタマイズとフィルターとアクション編)

アクションの代表例はテンプレートファイルに記述する wp_head() や wp_footer() です。 これらを記述した部分にコアやプラグインから必要な HTML などが出力されているのはアクションフックの力です。 wp_head や wp_footer が動作したタイミングに合わせて、それぞれの処理が HTML タグを出力しています。

今回は、wp-otenki というプラグインのソースコードを元にアクションフックを解説してみようと思います。

これは、記事の公開時にライブドアの「お天気Webサービス(LWWS)」に問い合わせを行い、記事の書いた日にお天気情報を付与するプラグインです。

実はこのプラグイン 2006年に初版がかかれたもので、このたびついにライブドア側の API の変更で動かなくなったというお知らせを頂き、せっかくですので近代 WordPress に合わせて完全リライトしてみることにした次第です。(wp-cron がまだなくて自前で fsockopen してバックグラウンド処理していたくらい古かったです・・・)

プラグインの仕様は以下のようになります。

  • 「記事の公開時」にその日のお天気を LWWS API に問い合わせカスタムフィールドに格納する。
  • 格納されたお天気情報をテンプレートファイルの記述でアイコン出力する。

「記事の公開時」というタイミング。タイミングへの処理の追加といえばアクションフックの登場です。

アクションフックを用いると、さまざまな WordPress の動作(アクション)に対して、プラグインやテーマの functions.php に定義したコールバック関数(フック)で独自の追加処理を行うことが出来ます。

(フック)と注釈を書く場所に迷ってしまいましたが、、なんだろう、、「引っかける」という意味合いでしょうか。「デバッガを引っかける」とかぼくらの世界でよく言いますが、外部から中身(ここでは WordPress 本体)をいじらずに処理を乗っ取るとか追加するとかそういうイメージの言葉だと思います。

WordPress ではおおよそ考えられるほとんどの動作にフックができるようになっており、たとえば、<head>の出力時、WordPress の初期化終了時、テンプレートファイルへの処理移動時、記事保存前、記事保存時、管理画面の一覧カラム表示時、、等々等々・・・。いくらでも思いつきます。

どのようなアクションフックがあるかは codex から知ることができます。

プラグイン API/アクションフック一覧

この記事は(おそらくほぼ完全な)アクションフックの一覧です。アクションフックはバージョン2.1以降から、プラグインおよびテーマ開発時に利用できるようになっています。詳しい情報については下記もご覧下さい。

さて、アクションに処理(関数)を定義するには、WordPressの API 関数「add_action」を用います。また、直接使う機会は少ないかもしれませんが、その呼び出し元となる関数は「do_action」です。

add_action 関数に指定する引数のアクション名で、どの場合の動作に処理の追加を行うのかが決定されます。

フックはプラグインや functions.php での拡張向け機能として存在しているだけではなく、 WordPress 本体の機能でも数多く使われいます。

たとえば、サイトの <head> 部分に主に HTML を挿入する働きを担うアクション名「wp_head」フックだけに注目すると WordPress は以下のようなシーケンスで動作します。

action_hook_new

図中、add_action の 3引数目は関数呼出し順の優先度(プライオリティー)になります。同一アクション名に複数の関数が登録されている場合は、この値が少ない方から呼び出しが開始され、無指定の場合は 10 という値が使われます。

関数2、関数3 が登録順ではなくプライオリティー順で呼び出されていることに注目してください。処理順で不具合が起きる場合は、この値を調整することで呼出し順をコントロールすることができます。

ユーザがフックを活用する場合は、プラグインや functions.php に処理を行う関数を定義し、事前に add_action を用いてその関数を追加する、というのが大きなつくりかたの流れです。あとは、そのタイミングになれば自動的に WordPress が do_action を契機に登録された関数を呼び出してくれます。

では wp_head アクションフックの場合、契機となる do_action(‘wp_head’); を呼出してしているのは WordPress 本体のどこかを調査してみます。

wp-includes/general-template.php

/**
 * Fire the wp_head action
 *
 * @since 1.2.0
 * @uses do_action() Calls 'wp_head' hook.
 */
function wp_head() {
    do_action('wp_head');
}

wp_head() テンプレートタグ。

[tegaki]欧米か![/tegaki]

・・・言ってみたかっただけです。すいません。。

ということで wp_head アクションフックは、単純にテンプレートファイルの header.php にかくことになっている wp_head() テンプレートタグが do_action を呼び出しているのでした。

この場合は自分で do_action 呼び出しているのに近い感じですが、、多くのフックは WordPress によってひっそりと呼ばれております。

記事公開のフック

さて、今回つくりたいプラグインは「記事公開時」に「お天気取得処理」を追加したいということで、codex からそれっぽいアクション名を探します。

このような場合は、管理画面から公開ボタンを押した、ドラフト状態の記事を更新ボタンから公開した、xmlrpc 経由で記事を投稿した等に呼び出されれる「publish_post」 アクションが使えそうです。

処理を追加したいアクション名が分かれば、実装開始です。

プラグインファイル(もしくは functions.php)に以下の記述をしてみましょう。(プラグインヘッダコメントは省略しています)

function prepare($post_ID, $post) {
    die("test");
}

add_action('publish_post', 'prepare', 10, 2);

prepare は任意の関数名で、function で定義した関数を add_action(‘publish_post’, ‘prepare’ として登録しています。

とりあえずこれで、管理画面から公開ボタンを押すと「test」という真っ白画面が登場してくると思います。処理をのっとった!、、勝利の確信の瞬間です(笑) die(); しているので、思ったタイミングで落ちれば正解です。まずは関数が呼ばれることを確認するのがアクションフックをかく第一歩です。

add_action('publish_post', 'prepare', /*ここ→*/10, 2);

ソースコード中、add_action 関数の3番目引数(10)が先ほどでてきた同一フックに対する実行順を決定するプライオリティーです。今回のプラグインはいつ呼ばれても良くデフォルト値(10)で本来は省略できるのですが、、次の引数(2)を指定したかったのでしょうがなく書いています。

add_action('publish_post', 'prepare', 10, /*ここ→*/2);

問題の4番目引数(2)は、登録した関数に渡される引数の数になります。「prepare($post_ID, $post)」の個数である 2 が該当します。デフォルトは1で、指定しない場合は $post_ID だけ渡されるイメージです。

function prepare(/*ここと→*/$post_ID, /*ここ→*/$post) {

アクションフックの中にはこのように、関数の呼び出しとともに、さらに詳細な情報を引数で渡してくれるアクションもあります。 publish_post では、どの記事が公開されたかという情報を引数の $post_ID(記事ID) と $post(記事オブジェクト) で得ることができます。

このような引数をどのように調査すればいいかといえば、ソースコードをみるのが早くて正確です。ここまで読んでいただいた方ならお察しの通り、do_action( ‘publish_post’ で WordPress 内のファイルを検索すればでてくるってのが正解!なのですが、publish_post には若干のトリックがあります。

wp-includes/post.php

function wp_publish_post( $post ) {
    // 略
    wp_transition_post_status( 'publish', $old_status, $post );
    // 略
}

function wp_transition_post_status($new_status, $old_status, $post) {
    do_action('transition_post_status', $new_status, $old_status, $post);
    do_action("{$old_status}_to_{$new_status}", $post);
    do_action("{$new_status}_{$post->post_type}", $post->ID, $post);
}

do_action は、一番下の行「do_action(“{$new_status}_{$post->post_type}”, $post->ID, $post)」が該当します。残念ながら、変数で動的にアクション名をつくっているので、検索ででてこないのでした。

do_action("{$new_status}_{$post->post_type}", $post->ID, $post);

呼び出し元 wp_publish_post 関数。

wp_transition_post_status( 'publish', $old_status, $post );

第一引数が publish で、記事の投稿タイプは post なので、wp_transition_post_status 関数内の do_action で結合して publish_post となります。後ろに「$post->ID, $post」が続き、フックに登録した関数で情報が取得できることが分かります。

do_action("{$new_status}_{$post->post_type}", $post->ID, $post);

このようにソースコードをみることで、ついでに publish_page や publish_[カスタム投稿タイプ名]や、publish -> draft などのステータスの変更などもフックで「引っかけられる」ことも学ぶことが出来ました。:)

do_action('transition_post_status', $new_status, $old_status, $post);
do_action("{$old_status}_to_{$new_status}", $post);

この辺は、WordPress を少しやってくれば経験的に「こうなっているんではないか」という予測がつくようになってきます。WordPress の恐るべきは、それが大抵予測通りなところです。IDE などの関数呼び出し元ジャンプ機能を使ってみていけば、すぐお望みのものが見つかるハズです。

てなわけで、再度プラグインのソースコードに戻ります。

function prepare($post_ID, $post) {
    die("test");
}

add_action('publish_post', 'prepare', 10, 2);

この処理は、PHP 5.3 の無名関数を使うと次のようにもかくことができます。関数名削除。

add_action('publish_post', function($post_ID, $post) {
    die("test");
}, 10, 2);

jQuery みたいで便利ですね。

関数名考えなくて済みますので、PHP 5.3 が使える環境の方は是非。プラグインや functions.php で定義する関数はグローバル領域となってしまいますので、関数名かぶりを考えないと意味でも楽ができます。 🙂

さて、ここまでいけば後はお天気 API に問い合わせをして、カスタムフィールドに格納する処理を関数に書けば良いです。

add_action('publish_post', function($post_ID, $post) {
    // "今日"のポストの時だけ天気を取得
    if(date("Y/m/d", strtotime($post->post_date)) != date("Y/m/d")) return;
    // LWWS(V1/JSON)より天気情報(札幌)を取得
    $json = file_get_contents(
        'http://weather.livedoor.com/forecast/webservice/json/v1?'
        . http_build_query(array('city' => '016010')));
    // 正常な JSON 形式であればカスタムフィールドに格納
    // 取得できなかった場合は次回の公開に期待
    if($json !== false && json_decode($json) !== null) {
        update_post_meta(
            $post_ID
            , 'otenki'
            , $json);
    }
}, 10, 2);

API からもらえるお天気情報は過去日が取得できず現在日以降になります。処理上のポイントは「”今日”のポストの時だけ天気を取得」の部分で、アクションフックからもらった $post 記事オブジェクトの情報を元に「今日」日付の公開記事のみを取得するようにしています。過去日の公開では天気は取得しません。

余談ですが WordPress の未来日の予約投稿は、実際にその日になった時に公開処理が行われます。これは WordPress に内蔵されるスケジューラの力によるものです。

さて、ここまでつくってみたものの、このままではプラグインの体をなしていません。札幌しかお天気とれないし、、サイト用にカスタムフィールドから HTML タグを出力する処理もありません。また、記事の公開と同じシーケンスで外部 API に対して通信取得処理を行っているため、若干公開処理が遅くなると言う弊害もあります。

というわけで、次回(明日予定)はこれらの対応と、プラグインの class 化によるクリーンナップや、管理画面をつけてみたりしたいと思います。 お楽しみに!

忘れていました。。すいません。。すいません、、こちらも次回に。 🙂


目次

本連載はファイルがない状態からソースコードを実装していく方式を採っています。プログラムの構成法も含めて解説していますので、最初から読んでいくとより理解しやすいでしょう。

アクションフックのプラグインをつくる編(この記事)
最初に、WordPress の拡張において重要なフックの考え方を、サンプルの動作のひとつである「記事公開時の処理追加」の実装を元に解説します。フックはこの後の画面出力、処理の非同期化や管理画面の付与でも使われていきます。
処理のクラス化
WordPress 拡張におけるモジュール設計として、PHP のクラスを用いた方法を紹介します。分かりやすく、他のモジュールとの競合を最小限に抑えられる手法です。
外部 API の呼び出し
LWWS などの REST API に対して WordPress からリクエストを送る解説です。また、ユーザによって設定が異なる値がある場合のモジュール構成も合わせて紹介します。
外部 API 呼び出しの非同期化
外的要因などで処理スピードの低下が懸念される場合の処理方法です。WP-Cron を用いたスケジュール化手法を解説しています。
テンプレートファイルからの情報出力
テンプレートファイルに処理を記述し出力する場合に、拡張モジュールと疎結合にする実装法を紹介します。アクションフックの活用例としても有効です。
管理画面をつける
管理画面に設定欄を設けユーザが拡張の機能をコントロールできるようにする手法の解説です。また、本連載中のソースコードに最終的なリファクタリングをかけ、他の開発者にこの拡張の意味を表現するプログラミングテクニックも解説します。
まとめ
本連載の概要とまとめ、そして目次です。

WordPress 徹底解析(アクションフックのプラグインをつくる編)」への3件のフィードバック

  1. ピンバック: Daily Digest for 2013/04/10

  2. ピンバック: レビュー待ちが投稿または更新された際にメール通知させる方法【wordpress】 | Heastea's Blog , loves Music & Movies

  3. ピンバック: レビュー待ちが投稿・更新された際にメール通知させる方法【wordpress】 | Heastea's Blog , loves Music & Movies

コメントを残す