WordPress 徹底解析(まとめ)

ブログにて 6日間に渡り、WordPress 拡張機能の実装例を紹介しながら、フックの動作や内部 API、そしてそれらを骨組みする方法を解説してきました。

このページは最後のまとめと目次となります。

連載はブログ記事としてひといきで書いたもので、本来であればここから沢山のリライトをしたいところですが、まずは一度パブリッシュしております。 書き足りない部分、不要なギャグ(!)、言葉やソースコードの揺れが見られますが、推敲は別な機会にということでどうかお許しください。


はじめに

WordPress を使ったサイト構築の手法の中で必要となってくる、フックや内部 API などを用いた機能の拡張方法と、そのプログラムの構成方法を解説します。

紹介しているサンプルソースコードは WordPress のプラグイン形式、もしくはテーマの functions.php の双方で使うことができます。つまり内容は、WordPress のプラグイン及びテーマ開発者にとって有効です。

記事には個別の実装法だけではなく、 サイト構築において一般的な拡張を行おうとした場合に取り得る WordPress の全体的な動作シーケンスの解説が含まれ、内容は汎用的に利用することができると考えています。

WordPress を使った開発で、この連載が少しでも役に立てば幸いです。


Otenki について

本連載でサンプルとしている「Otenki」は、気象情報を提供する LWWSLivedoor Weather Web Service) API から、記事投稿当日のお天気情報の取得・出力を行う WordPress の拡張プログラムです。

大きく次の機能を持っています。

  • 投稿時のお天気情報の自動取得
  • 取得したお天気のテーマへの出力
  • 取得対象となる地域の管理画面からの指定

otenki_post

otenki_admin

含まれる実装的要素は以下のようになります。

  • アクションフック
  • 非同期スケジュール登録
  • 外部 API への http アクセス
  • 管理画面

解説は個々の機能だけではなく、WordPress において拡張の実装を行うためのプログラム構成例や考え方も同じくらいのボリュームでしています。その他の拡張機能やテーマの実装を行う場合においても、知識のひとつとして使えるはずです。


ソースコード

最終形のソースコードは次のリンクから閲覧することができ、実際に WordPress サイトで動作させることができます。

otenki.php

本ファイルの内容を otenki.php として保存し、プラグインディレクトリに格納した上で有効化することで動作します。また、ファイルからプラグインヘッダを除くソースコードをコピーし、テーマの functions.php に貼り付けることでも動作させることができます。

拡張の動作開始後、お天気情報を取得する地域を指定するため、「管理画面->投稿設定->お天気情報取得サービス」から地域IDを指定し「変更を保存」ボタンを押下してください。

お天気情報のテーマへの出力は、テンプレートファイルのループ内の任意の位置で次のように do_action(‘the_otenki’); をすることで行うことができます。

<?php while(have_posts()) : the_post(); ?>
    <?php the_title(); ?>
    <?php do_action('the_otenki'); ?>
    <?php the_content(); ?>
<?php endwhile; ?>

この後、記事の投稿を行うとお天気情報が取得され、指定した位置にお天気情報が出力されます。

なお、サンプルに下位互換の機能を追加した正式版の WordPressプラグインアーカイブは次から取得できます。

WordPress Plugins/JSeries » wp-otenki


目次

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

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

まとめ

WordPress のサイト構築で必要となってくる知識は大きく3つあると考えます。

  1. WordPress API
  2. HTML/CSS や http・データベースなどシステムを構成する環境
  3. PHP プログラミング言語のボキャブラリ

この 3つの知識を揃えた時点でスタートラインとなるため、何も分からない状態であると取りかかるまでの時間が少しかかります。

本連載ではこれらをまんべんなく、既知であろう部分はギリギリまで省いて解説しました。恐らく最初に指針が欲しいのはソースコードの構成や動作シーケンスの組み立てだと考え、これらを中心において書き進めています。

Web を含むオープンシステムは、開発に必要とされる知識が広範囲にわたります。しかしながら WordPress 上での作業は、WordPress に備わる柔軟な API やフレームワーク的な動きが、多くの面倒な部分や難解な部分を隠してくれるため、集中して楽しむことができるのではないかと思っています。

ソフトウェアの開発は非常に面白いものです。WordPress を通じてプログラミングが初めての方が動く感動を知らせてくれた時は、自分のことのように嬉しいと感じます。実はそんな想いも込めて、共著しております「WordPress デザインワークブック」も書きました。もしこの連載がまだ少し難しいと感じた時は手に取ってみてください。

Code is Poetry.

WordPress の世界にようこそ。

WordPress 徹底解析(WordPressを拡張するプログラムの構成法 – 管理画面をつける)

作成した拡張の設定値を、ユーザが WordPress の管理画面から操作できるようにします。

WordPress には、既存の設定ページに項目を簡単に追加できる便利な Settings API があり、これを用いて地域IDを「投稿設定」画面から設定できるようにしてみましょう。

また、完成したプラグインを機能の明確化のためにリファクタリングを行い、最後の仕上げをしてみます。


Settings API による管理画面付与

前回までのお天気取得拡張の機能で取得するお天気の地域を変更するには、ソースコードを修正しコンストラクタ引数により地域IDを設定するしかありませんでした。

今回は、これをユーザが画面から設定できるようになる管理画面の追加を行ってみます。

このような簡単な項目追加の場合、既存の管理画面のページに設定フォームを追加する Settings API と呼ばれる管理画面用 API を用いると、実装も楽で、ユーザも分かりやすいでしょう。

使い方はいくつかの API 関数の呼び出しとフックの登録で実現できます。

登場するのは、

  • セクションの追加(add_settings_section)
  • 項目の追加(add_settings_field)
  • 項目登録(register_setting)

の3関数と、これらの呼び出しを実装したメソッドです。

この実装メソッドを admin_init アクションフックに登録するという手順を踏むことにより、管理画面への画面展開やオプション値の保存が自動的に行われるようになります。

実装メソッド名を setting とし、処理を抜粋した概念ソースコードは次のようになります。

class Otenki {
    /**
     * オプション値・キー.
     */
    const OPTION_KEY = 'otenki';

    /**
     * 管理画面の投稿設定に地域ID設定を追加.
     */
    function setting() {
        // 投稿設定画面
        $writing = 'writing';
        // 投稿設定にセクション追加
        add_settings_section(
            'otenki_setting_section'
            , 'お天気情報取得サービス'
            , array($this, 'echoSettingSection')
            , $writing);
        // セクションに地域ID指定テキストボックス追加
        add_settings_field(
            self::OPTION_KEY
            , '地域ID'
            , array($this, 'echoSettingField')
            , $writing
            , 'otenki_setting_section');
        // 設定に登録
        register_setting($writing, self::OPTION_KEY);
    }

    /**
     * セクション文字列出力.
     */
    function echoSettingSection() {
        echo '取得したい地域IDを指定してください。';
    }

    /**
     * 地域ID設定テキストボックス出力.
     */
    function echoSettingField() {
        echo '<input name="' . self::OPTION_KEY . '"'
        . ' id="' . self::OPTION_KEY . '"'
        . ' type="text"'
        . ' value="'
        . esc_attr(get_option(self::OPTION_KEY)) . '" />';
    }
}

$otenki = new Otenki();
add_action('admin_init', array($otenki, 'setting'));

管理画面「設定->投稿設定」に HTML が展開され結果は次のようになります。(赤部分)

otenki_admin

ソースコードを順に見ていきます。

まず、add_action により Otenki クラスの setting メソッドを登録します。

add_action('admin_init', array($otenki, 'setting'));

setting メソッド内で管理画面に対するセクション、項目の登録を行います。

function setting() {
    // 投稿設定画面
    $writing = 'writing';
    // 投稿設定にセクション追加
    add_settings_section(
        'otenki_setting_section'
        , 'お天気情報取得サービス'
        , array($this, 'echoSettingSection')
        , $writing);
    // セクションに地域ID指定テキストボックス追加
    add_settings_field(
        self::OPTION_KEY
        , '地域ID'
        , array($this, 'echoSettingField')
        , $writing
        , 'otenki_setting_section');
    // 設定に登録
    register_setting($writing, self::OPTION_KEY);
}

add_settings_section、add_settings_field 関数の引数はコールバック関数を持ち、それぞれの画面出力用に登録したメソッド(echoSettingSection、echoSettingField)から HTML を行います。

PHP 5.3 以降が使える環境であればメソッドを登録せず無名関数で次のようにも記述できます。利用できる環境であればこちらのほうが良いでしょう。(無名関数を用いない場合、コールバックに登録するメソッドは public にする必要があります)

// 投稿設定にセクション追加
add_settings_section(
    'otenki_setting_section'
    , 'お天気情報取得サービス'
    , function() {
        echo '取得したい地域IDを指定してください。';
    }
    , $writing);
add_settings_field(
    self::OPTION_KEY
    , '地域ID'
    , function() {
        echo '<input name="' . self::OPTION_KEY . '"'
        . ' id="' . self::OPTION_KEY . '"'
        . ' type="text"'
        . ' value="'
        . esc_attr(get_option(self::OPTION_KEY)) . '" />';
    }
    , $writing
    , 'otenki_setting_section');

echoSettingSection、echoSettingField、register_setting 各 API 関数で指定している ‘writing’ は投稿設定画面を指定する引数です。

指定できる設定ページには general(一般設定), reading(表示設定), writing(投稿設定), media(メディア)などが管理画面の「設定」ページと対応しており、今回は「投稿時」の地域IDの指定ということで、writing(投稿設定) を渡しています。

const OPTION_KEY で定義している ‘otenki’ がデータベースに格納される((wp_)options テーブル)のキーとなり、add_settings_field 関数の引数と、echoSettingField で出力している name、id 属性を一致させていることにも注目してください。この対応により、自前で管理画面の処理を実装することなく、画面が展開されデータベースに値が保存がされるようになります。

以上で設定が管理画面から保持できるようになりましたので、この地域IDを元に Otenki クラスを動作させるようにします。コンストラクタで一定となっている地域IDを、保持した設定値で上書きするように修正します。

/**
 * コンストラクタ・プラグインデフォルト値設定及び設定の取得.
 */
function __construct(
    $city = '016010', $css_class = 'otenki') {
    // LWWS URL 引数(地域別値)
    $this->city = $city;
    // 画像出力時の CSS クラス
    $this->css_class = $css_class;
    // 管理画面地域ID設定を取得し存在すれば上書き
    $value = get_option(self::OPTION_KEY);
    if(!$value) {
        $this->city = $value;
    }
}

get_option で設定値を取得し、あればその地域IDを用いる流れです。管理画面未設定時はデフォルトコンストラクタの引数で動作します。

この処理をもって、管理画面から地域IDを指定し、その値を使って拡張が動作するようになりました。

リファクタリングとシングルトン

ここまででついに目標とするお天気情報取得の処理が完成しました。

ここからは実装上、気になる部分の修正をしていきましょう。このような動きを変えずに、ソースのクリーンナップや拡張を行うことをリファクタリングと呼びます。

まずはクラスと WordPress のインターフェースに注目した全体の概念ソースを示します。

class Otenki {

    /**
     * コンストラクタ・プラグインデフォルト値設定及び設定の取得.
     */
    private function __construct(
        $city = '016010', $css_class = 'otenki') {
        // 「オプション値・キー」を使った設定値取得
        // 「地域ID」設定
    }

    /**
     * 記事公開フックで天気情報取得スケジュールを作成する.
     */
    function prepare($post_ID, $post) {
    }

    /**
     * LWWS から天気情報を取得し記事のカスタムフィールドに格納する.
     */
    function extract($post_ID) {
        // 「地域ID設定」取得
        // 「カスタムフィールド・メタキー」を使ったカスタムフィールド値登録
    }

    /**
     * カスタムフィールドに取得したお天気アイコン用 HTML をアクションフックで出力.
     */
    function output() {
        // 「カスタムフィールド・メタキー」を使ったカスタムフィールド値表示
    }

    /**
     * 管理画面の投稿設定に地域ID設定を追加.
     */
    function setting() {
        // 「オプション値・キー」を使った設定
    }
}

$otenki = new Otenki();

add_action('publish_post', array($otenki, 'prepare'), 10, 2);
add_action('the_otenki', array($otenki, 'output'));
add_action('extract_otenki', array ($this, 'extract'));
add_action('admin_init', array($this, 'setting'));

ソース下部の記述により、Otenki クラスは「$otenki = new Otenki()」からインスタンスが作成され、各 add_action により WordPress にフックが登録されます。

ここで気になるのが new Otenki() のインスタンス作成部分です。このクラスは地域IDの異なる複数のインスタンスが作成できるにもかかわらず、次の理由で正しい処理が行えません。

    • 「カスタムフィールド・メタキー」が一定で、他のインスタンスから値が上書きされてしまう。
    • 管理画面「オプション値・キー」が一定で、複数の地域IDを指定することが出来ない。
    • 管理画面のテキストボックスの name や id が一定で、複数の地域IDの入力欄が出力できない。

これらを対応するためには、固定値となっている部分に地域IDを連結付与するなど地域をユニークにする実装が考えられます。

ただしいずれにしてもインスタンスを増やすにはソースコードを修正し

$sapporo = new Otenki();
$kurume = new Otenki('400040');

などとするしか方法がありません。

つまりクラスを用いて全ての実装を行うには、Otenki クラスのインスタンスの生成を束ねるコントローラとなるもうひとつのクラスが必要になります。

$otenki = new OtenkiController();

コントローラクラスは、管理画面の設定やそのオプション値をもって複数の Otenki クラスの生成を行う、WordPress 内で唯一のインスタンスとなります。

もし仮にこのクラスが別なモジュールから new され複数のインスタンスが作成された場合は、管理画面やオプション値がおかしなことになるでしょう。 WordPress はひとつなので、対話を行うインスタンスもひとつといった意味合いです。

このように、ただひとつだけインスタンスの存在を許すクラスのことをシングルトンクラスと呼び、実装のテクニックにより唯一のインスタンスを確保することができます。

その実装の一部は次のようになります。インスタンスの取得を行うために new をしていないところに注目してください。

$otenki = OtenkiController::getInstance();

さて、このような複数のインスタンスを束ねるコントローラクラスの存在ですが、このお天気取得プラグインにはいささかオーバスペックです。複数の地域IDを扱える可能性を持たせてクラスをつくってあるものの、実にまだそういった要件がないためです。

しかしながら完成している Otenki クラスに、シングルトン性を確保しておくことには意味があることでしょう。他の開発者が別なモジュールから Otenki インスタンスを作成する可能性はほぼゼロではありますが、シングルトン実装を行っておくことで動作をソースコード上で明示化できます。

ではここからは Otenki クラスのシングルトン化を含むリファクタリングを行い、自分を含む開発者にソースコードを持って意思を伝達するテクニックを紹介していきます。

まずは、ここまで解説した「このクラスのインスタンスは唯一ひとつしか存在できない」というメッセージを表現するために、シングルトンパターンと呼ばれる実装を Otenki クラスに加えていきます。

最初に、Otenki コンストラクタを public から private に落とします。これで外部からのインスタンスの作成(new)が言語構文上不可能になります。

private function __construct(
    $city = '016010', $css_class = 'otenki')

次に、static メンバ変数とメソッドを使い、Ontenki クラスの new を一度だけしか行えないようにブロックし、インスタンスをひとつに保証します。まとめると次のようになります。

class Otenki {
    /**
     * シングルトンインスタンス格納.
     */
    static private $INSTANCE = null;

    /**
     * WordPress 拡張処理用 Otenki インスタンスの取得.
     * 
     * @return Otenki
     */
    static function getInstance() {
        if(self::$INSTANCE == null) {
            self::$INSTANCE = new Otenki();
        }
        return self::$INSTANCE;
    }

    /**
     * コンストラクタ・プラグインデフォルト値設定及び設定の取得.
     */
    private function __construct(
        $city = '016010', $css_class = 'otenki') {
        /* 省略 */
    }
}

この実装により Oteki インスタンスを得るには Otenki::getInstance() の記述が唯一の方法となり、返却されるインスタンスは常に同じ、ひとつに保証されます。

ちなみに、本来はシングルトンインスタンスを作成・返却する部分を、

/**
 * シングルトンインスタンス格納.
 */
static private $INSTANCE = new Otenki();

/**
 * WordPress 拡張処理用 Otenki インスタンスの取得.
 * 
 * @return Otenki
 */
static function getInstance() {
    return self::$INSTANCE;
}

と実装したいところなのですが、PHP の場合、

static private $INSTANCE = new Otenki();

とスタティックイニシャライザが使えないため、前述の方法で実装しています。(この実装はマルチスレッド環境では不具合を起こしますが、PHP は言語仕様がシングルスレッドモデルなので問題ないでしょう)

シングルトン化された Otenki をフックに登録します。

リファクタリング前のソースは以下のようになっていました。new を行いインスタンスの生成をしています。

$otenki = new Otenki();

add_action('publish_post'
    , array($otenki, 'prepare'), 10, 2);
add_action('extract_otenki'
    , array ($otenki, 'extract'), 10);
add_action('the_otenki'
    , array($otenki, 'output'));
add_action('admin_init'
    , array($otenki, 'setting'));

リファクタリング後のソースは、new は行わずインスタンスは getInstance() メソッドから取得することになりましたので、次のようになります。

add_action('publish_post'
    , array(Otenki::getInstance(), 'prepare'), 10, 2);
add_action('extract_otenki'
    , array (Otenki::getInstance(), 'extract'), 10);
add_action('the_otenki'
    , array(Otenki::getInstance(), 'output'));
add_action('admin_init'
    , array(Otenki::getInstance(), 'setting'));

シングルトン化によりインスタンスはひとつしか存在できないと明示でき、副次的効果としてグローバル変数 $otenki を削除することができました。これで完全に WordPress と Otenki は 1:1 の関係となりました。

シングルトン化が完了しましたので、次にこの拡張がどのように WordPress インターフェースを使っているかをソースコード上に表現してみます。

ユーザ(開発者)が意識する WordPree とのインターフェース(フック)は、記事公開(publish_post)と、テンプレートタグにお天気を表示する(the_otenki)のふたつだけで、その他はインスタンス内部で使われているものです。

逆に言えば、スケジュール実行(extract_otenki)と管理画面(admin_init)は Otenki クラスとは内部構造上切っても切れない関係です。

このことを表現するため、開発者が意識するフックをクラスの外側に、切り離せないフックをコンストラクタで定義します。

class Otenki {
    /**
     * コンストラクタ・プラグインデフォルト値設定及び設定の取得.
     */
    private function __construct(
        $city = '016010', $css_class = 'otenki') {
        // 投稿スケジュール用アクションフック登録
        add_action('extract_otenki'
            , array ($this, 'extract'));
        // 管理画面アクションフック登録
        add_action('admin_init'
            , array($this, 'setting'));
    }
}

add_action('publish_post'
    , array(Otenki::getInstance(), 'prepare'), 10, 2);
add_action('the_otenki'
    , array(Otenki::getInstance(), 'output'));

このように明示的に記述を分けておくことで、開発者は publish_post と the_otenki は単純に外部からコントロールしてもよいと考えられるようになります。

たとえば、条件によって記事公開時のお天気情報の取得を行いたくない場合は、開発者は安心して自身のモジュールに次のような実装ができます。(plugins_loaded は全てのプラグインロード後に呼ばれるアクションフック、remove_action はアクションフックから特定のフックを削除する関数)

add_action('plugins_loaded', function() {
    if(/*なんとか条件 */ false) return;
    remove_action('publish_post'
        , array(Otenki::getInstance(), 'prepare')
        , 10)
});

また上記のソースからも分かるとおり、シングルトン化したことにより他のモジュールから拡張を扱う場合でも「Otenki::getInstance()」メソッドが利用できるようになり、$otenki などのグローバル変数を介した処理よりも、どのモジュールの処理なのかを明確にすることができます。

まとめ

管理画面の追加とリファクタリングが完了しました。

今回のポイントは、

  • 既存の設定ページに、簡単なオプション値の項目を追加するには Setting API が利用できる。
  • WordPress とのインターフェースを取り持つクラスでひとつしかインスタンスが存在できない実装は、シングルトンパターンを利用するとソースコードがメッセージ性をもち、他のモジュールからも利用しやすい。
  • フックを利用する場合は、そのインターフェースをソースコード上に表現し、他の開発者が操作しやすいようにしよう。

でした。

次回はいよいよシーズン2最終回。 総まとめ+目次です。 お楽しみに。 🙂


目次

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

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

WordPress 徹底解析(WordPressを拡張するプログラムの構成法 – テンプレートファイルからの情報出力)

テンプレートファイルに情報表示用の記述を追加し、カスタムフィールドに格納した値を、クラス化した処理から出力する手法を紹介します。 🙂


テンプレートファイル用のヘルパー関数

前回までで、記事公開時にカスタムフィールドへのお天気情報の登録と、そのテンプレートファイルへの出力を行うヘルパー関数の実装までができました。

今回は実際にカスタムフィールドの値をHTML出力する処理を記述してみます。

出力部分のみ抜き出した概念ソースは以下のようになります。

class Otenki {
    function output() {
        // お天気情報出力処理
    }
}

$otenki = new Otenki();

function the_otenki() {
    global $otenki;

    $otenki->output();
}

本処理を行った場合の、記事投稿時の処理シーケンスは以下のようになります。

otenki_seq4

テーマ・テンプレートファイルのお天気情報を出力したい部分で、the_otenki 関数の呼び出しを記述することで出力を行います。

お天気情報は記事ごとの情報になりますので、WordPress ループ内に実装します。

<?php while(have_posts()) : the_post(); ?>
    <?php the_title(); ?>
    <?php the_otenki(); ?>
    <?php the_content(); ?>
<?php endwhile; ?>

ヘルパー関数 the_otenki を介して実際に HTML 出力処理を行う output メソッドでは、カスタムフィールドを特定するために、ループ中の記事IDが必要になります。

このような場合、通常のテンプレートファイルと同様、拡張の処理中でも記事IDを取得する get_the_ID() テンプレート関数を使うことが可能です。 これはシーケンス図から見て取れるように、処理が WordPress ループ内で呼び出されるためです。

カスタムフィールド取得や正当性確認などの処理を入れると、シーケンスは次のようになるでしょう。

otenki_seq5

実際の output メソッドの実装は以下のようになります。

function output() {
    // ループ中の記事IDを取得
    $post_ID = get_the_ID();
    // カスタムフォールドから天気情報取得
    $otenki = get_post_meta($post_ID, Otenki::META_KEY, true);
    // カスタムフィールドが正しい形式でなければ何もしない
    if(!(isset($otenki[Otenki::LWWS_V1_JSON])
        && $this->isOtenki($otenki[Otenki::LWWS_V1_JSON]))) return;
    // お天気情報を元に HTML を構築して出力
    $today = $otenki[Otenki::LWWS_V1_JSON]['forecasts'][0];
    $image = $today['image'];
    $telop = $today['telop'];
    echo "<img src=\"${image['url']}\""
        . " width=\"${image['width']}\" height=\"${image['height']}\""
        . " alt =\"${telop}\""
        . " class=\"$this->css_class\" />\n";
}

get_the_ID 関数でループ中の記事IDを取得し、get_post_meta 関数でそのカスタムフィールド値を取得し正当性確認後、HTML に出力するのが大きな流れです。

出力処理のアクションフック化

今回のように WordPress のテンプレートファイルに対して出力用のテンプレートタグを増やす拡張では、テンプレートファイルと拡張がかかれたモジュールの関係を維持する必要があります。

具体的に言えば、この拡張をプラグインとして定義した場合に、the_oteki テンプレートタグがかかれたテーマとプラグインはセットで扱う必要があります。つまり、プラグインを無効化すると the_otenki 関数が定義されなくなるため、PHP がエラーとなりサイトがダウンしてしまいます。

(functions.php に実装された場合は、必ずテーマとセットで有効になりますのでこの問題は発生しません。)

この問題に対処するために拡張をプラグイン化した場合には、その存在をテンプレートファイルで確認する必要があります。

グローバル変数 $otenki の存在チェック。

<?php if(isset($otenki)) the_otenki() ?>

グローバル関数 the_otenki の存在チェック。

<?php
if(function_exists('the_otenki')) {
    the_otenki();
}
?>

以上のようにいくつか方法がありますが、いずれもテンプレートファイルの記述は冗長になります。存在確認の処理はテンプレートファイルでは意識したくないところです。

そこで、WordPress のアクションフックの仕組みを利用して確認処理を削除するテクニックを用います。

テンプレートファイルに記述する関数呼び出しを次のようにアクションフックの呼び出しに変更します。

<?php while(have_posts()) : the_post(); ?>
    <?php the_title(); ?>
    <?php do_action('the_otenki'); /* ←ここ */ ?>
    <?php the_content(); ?>
<?php endwhile; ?>

そして処理を行う output メソッドを、新設した the_otenki アクションに登録します。

$otenki = new Otenki();

add_action(
    'publish_post', array($otenki, 'prepare'), 10, 2);
add_action(
    'extract_otenki', array ($otenki, 'extract'), 10);
add_action(
    'the_otenki', array($otenki, 'output')); /* ←ここ */

アクションフックを実行する do_action 関数は、登録された関数がないと何も処理を行いません。

つまり output メソッドがない(=プラグインが有効化されていない)場合にもエラーを起こさずに素通してくれるようになるため、冗長な存在確認の実装を削除することができます。

このようにするとプラグイン無効時の考慮だけでなく、合わせて the_otenki が呼び出されたタイミングを他のからもフックできるようになります。

ここからさらにアクションフックを使ったプログラミング手法を用いて、たとえば、お天気を出したくないときの条件制御を、他の拡張から動的に行うなどという実装も行えることでしょう。

まとめ

地域ID指定の管理画面化を除き全実装が完了しました。

今回のポイントは、

  • 拡張内でも WordPress のテンプレートタグ・関数が用いることが出来る。その場合は WordPress の動作シーケンスを意識しよう。
  • 拡張を functions.php ではなくプラグインとして実装し、テーマと関連する関数がある場合は呼び出し元で存在チェックをしよう。
  • 存在チェックが冗長になる場合はアクションフックの仕組みを用い、新設したアクションを用いて処理関数を登録すると処理削除とともに拡張性も向上する。

でした。

WordPress のフック処理は強力です。なんとバージョン 1.2 から備わる由緒正しい機能で、WordPress のプログラム構成上の最大の特徴といっても過言ではありません。

フックを使いこなすことにより、横断的関心事に対して様々な処理を柔軟に拡張していくことができます。自身で拡張を実装する場合も、フックを積極的に使うことで WordPress 的なプログラミングができるようになるでしょう。

というわけで、次回は地域IDを指定する管理画面を付与してみます。 お楽しみに。 🙂

ソース

最後にここまでの、全てのソースコードを記載します。

コンストラクタで地域IDを指定する形ですが、プラグインとしては全機能が動く状態です。プラグインファイルもしくは functions.php にコピーすることで動作します。

今までの解説が全て実装されていますので、プログラムの流れを考えながら是非読んでみてください。

<?php
/******************************************************************************
 * Otenki Class
 * 
 * @author     hiromasa
 * @version    1.00
 *****************************************************************************/
class Otenki {

    /**
     * カスタムフィールド・メタキー.
     */
    const META_KEY = 'otenki';

    /**
     * APIバージョン情報(V1/JSON).
     */
    const LWWS_V1_JSON = 'LWWS_V1_JSON';

    /**
     * LWWS URL 引数(地域別値).
     * 
     * @see http://weather.livedoor.com/forecast/rss/primary_area.xml
     */
    var $city;

    /**
     * 画像出力時の CSS クラス.
     */
    var $css_class;

    /**
     * コンストラクタ・プラグインデフォルト値設定.
     */
    function __construct($city = '016010', $css_class = 'otenki') {
        // LWWS URL 引数(地域別値)
        $this->city = $city;
        // 画像出力時の CSS クラス
        $this->css_class = $css_class;
    }

    /**
     * 記事公開フックで天気情報取得スケジュールを作成する.
     * 
     * @param $post_ID
     * @param $post
     * @return none
     */
    function prepare($post_ID, $post) {
        // "今日"のポストの時だけ天気を取得
        if(date("Y/m/d", strtotime($post->post_date))
            != date("Y/m/d")) return;
        // 天気取得の非同期スケジュール作成
        wp_schedule_single_event(
            time()
            , 'extract_otenki'
            , array($post_ID));
    }

    /**
     * LWWS から天気情報を取得し記事のカスタムフィールドに格納する.
     * 
     * @param $post_ID
     * @return none
     */
    function extract($post_ID) {
        // LWWS(V1/JSON)より天気情報を取得
        $json = wp_remote_get(
            'http://weather.livedoor.com/forecast/webservice/json/v1?'
            . http_build_query(array('city' => $this->city)));
        // 取得できなかった場合は次回の公開に期待
        if(is_wp_error($json)) return;
        // リクエストから JSON データ取得し PHP 連想配列に変換
        $otenki = json_decode($json['body'], true);
        // 正常な JSON 形式ではなければ次回の公開に期待
        if(!$this->isOtenki($otenki)) return;
        // カスタムフィールドにバージョン情報付きで格納
        update_post_meta(
            $post_ID
            , Otenki::META_KEY
            , array(Otenki::LWWS_V1_JSON => $otenki));
    }

    /**
     * カスタムフィールドに取得したお天気アイコン用 HTML をアクションフックで出力.
     * 
     * @param none
     * @return none
     */
    function output() {
        // ループ中の記事IDを取得
        $post_ID = get_the_ID();
        // カスタムフォールドから天気情報取得
        $otenki = get_post_meta($post_ID, Otenki::META_KEY, true);
        // カスタムフィールドが正しい形式でなければ何もしない
        if(!(isset($otenki[Otenki::LWWS_V1_JSON])
            && $this->isOtenki($otenki[Otenki::LWWS_V1_JSON]))) return;
        // お天気情報を元に HTML を構築して出力
        $today = $otenki[Otenki::LWWS_V1_JSON]['forecasts'][0];
        $image = $today['image'];
        $telop = $today['telop'];
        echo "<img src=\"${image['url']}\""
            . " width=\"${image['width']}\" height=\"${image['height']}\""
            . " alt =\"${telop}\""
            . " class=\"$this->css_class\" />\n";
    }

    /**
     * お天気情報バリデーション.
     * 
     * @param unknown $otenki
     * @return boolean
     */
    function isOtenki($otenki) {
        if(!(is_array($otenki)
            && isset($otenki['forecasts'])
            && is_array($otenki['forecasts'])
            && isset($otenki['forecasts'][0]['telop'])
            && isset($otenki['forecasts'][0]['image']))) {
            return false;
        }
        return true;
    }
}

/******************************************************************************
 * Otenki - WordPress Interface Define
 *****************************************************************************/

$otenki = new Otenki();

add_action('publish_post', array($otenki, 'prepare'), 10, 2);
add_action('extract_otenki', array ($otenki, 'extract'), 10);
add_action('the_otenki', array($otenki, 'output'));
?>

目次

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

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

WordPress 徹底解析(WordPressを拡張するプログラムの構成法 – 外部 API 呼び出しの非同期化)

外部APIの呼び出しなど時間がかかる処理で、投稿などの動作が遅くならないよう、WordPress に備わる WP-Cron という仕組みを利用して、実行を非同期化するテクニックを紹介します。 🙂


お天気API呼び出しの非同期化

前回は、アクションフックを用いて投稿時にお天気APIに問い合わせを行い、カスタムフィールドにお天気情報を格納する部分の実装を行いました。

主要部分の概念ソースコードは次のようになります。

class Otenki {
    function prepare($post_ID, $post) {
        // "今日"のポストの時だけ天気を取得判定
        // wp_remote_get 関数による http リクエスト
        // カスタムフィールドへの情報格納
    }
}

$otenki = new Otenki();
add_action(
    'publish_post', array($otenki, 'prepare'), 10, 2);

本処理を行った場合の、記事投稿時の処理シーケンスは以下のようになります。

otenki_seq3

今回注目するのは、上記黄色メモ部分「お天気APIの取得処理」と後続の処理の関係です。

APIの呼び出しには外的要因が多く、時間がかかったり予想外のデータが戻ってきたりする場合があります。自分の扱い知らぬところで動作するため、慎重な実装が必要です。

たとえば、ネットワークや対向 API サーバの都合で API 呼び出し処理に 60 秒かかってしまうとすると、後続がそれだけ遅れていきます。簡単なところでは投稿後、管理画面の再描画までにプラス 60 秒かかって可能性があるということです。これはちょっといただけません。

また、前回外部APIから取得した情報の処理にはしっかり正当性確認をしようという部分がありましたが、外的要因如何よっては PHP が Fatal Error で終了してしまうという不安もぬぐいきれません。 WordPress にはトランザクションロールバックの処理がありませんので、後続処理によってはデータが不整合してしまう可能性も出てきます。

もちろん、処理をなんとかひとつのトランザクションとして扱いたい場合はやむを得ないところなのですが、WordPress の場合主たる処理は記事の投稿です。今回のお天気のような付加情報は、また次回がんばろうという考え方ができます。

以上のような理由から、お天気APIの取得を記事投稿とは切り離した処理にしてしまおうというのが今回のお話です。このような実行の一部で、結果を待つこととなく処理を進めることを非同期処理と呼びます。

WP-Cron によるスケジュール

WordPress には決められた時間を元に、一定の処理を行う WP-Cron と呼ばれるスケジューラのが組み込まれています。たとえば、未来日に設定した投稿したが、その時間で公開されるのはこの機能の働きです。

また、WP-Cron で行う処理は、サイトや管理画面の表示とは別な流れで行われるようになっており、本筋の動作に影響がでません。これは本筋の処理から、バックグラウンドで動くスケジュール処理用の別な WordPress を起動する特殊な実装で動いているためで、次のように自分自身で WordPress に http リクエストを行い実行するようになっています。

/**
 * Send request to run cron through HTTP request that doesn't halt page loading.
 *
 * @since 2.1.0
 *
 * @return null Cron could not be spawned, because it is not needed to run.
 */
function spawn_cron( $gmt_time = 0 ) {

    /* 省略 */

    $cron_request = apply_filters( 'cron_request', array(
        'url' => site_url( 'wp-cron.php?doing_wp_cron=' . $doing_wp_cron ),
        'key' => $doing_wp_cron,
        'args' => array( 'timeout' => 0.01, 'blocking' => false, 'sslverify' => apply_filters( 'https_local_ssl_verify', true ) )
    ) );

    wp_remote_post( $cron_request['url'], $cron_request['args'] );

}

このような WP-Cron の特徴を生かすと、外部 API 取得処理のような時間のかかる可能性がある処理を非同期化することができます。WordPress 本体内でも更新 ping の送信や Akismet のスパムチェックなどで同様のテクニックが使われています。

では実際に WP-Cron を使ってお天気 API 取得処理の非同期化を行ってみます。

API がよくできていますので、WordPress でスケジュールをつくるのは難しくありません。ここではひとつだけスケジュールを作成する wp_schedule_single_event 関数を使います。

wp_schedule_single_event 関数は引数で指定した時刻以降に、指定したアクションフックを呼び出してくれます。

アクションフックは任意のものが指定できますので、お天気 API の取得を行うフックを “extract_otenki” と命名し、その処理がかかれた execute メソッドを登録(add_action)してあげます。

前回つくった prepare の取得処理部分だけが execute メソッドに移り、代わりに wp_schedule_single_event でスケジュール登録する形になります。

最初の概念ソースコードとの差分は次のようになります。

publish_post アクションフックだけで処理していた最初のソース。

class Otenki {
    function prepare($post_ID, $post) {
        // "今日"のポストの時だけ天気を取得判定
        // wp_remote_get 関数による http リクエスト
        // カスタムフィールドへの情報格納
    }
}

$otenki = new Otenki();
add_action(
    'publish_post', array($otenki, 'prepare'), 10, 2);

wp_schedule_single_event で処理を非同期化したソース。

class Otenki {
    function prepare($post_ID, $post) {
        // "今日"のポストの時だけ天気を取得判定
        // 天気取得の非同期スケジュール作成
        wp_schedule_single_event(
            time()
            , 'extract_otenki'
            , array($post_ID));
    }

    function extract($post_ID) {
        // wp_remote_get 関数による http リクエスト
        // カスタムフィールドへの情報格納
    }
}

$otenki = new Otenki();

add_action('publish_post'
    , array($otenki, 'prepare'), 10, 2);
add_action('extract_otenki'
    , array ($otenki, 'extract'), 10);

prepare メソッドと extract メソッドで処理対象となる記事IDが、wp_schedule_single_event の引数を介して受け渡しできている部分にも注目してください。

ここまできてようやく、publish_post のアクションフックに登録している関数名が prepare(準備)になっていた謎が解明されたのでした。 🙂

まとめ

以上で、お天気 API 取得処理の非同期化ができました。

今回のポイントは、

  • 外部 API 呼び出しなど外的要因で速度の低下が見込まれる場合は非同期化しよう。
  • WP-Cron にスケジュールされた処理は、サイトの表示処理とは別な WordPress が起動して行われるので非同期化される。
  • wp_schedule_single_event は WP-Cron にスケジュールを行う関数で、実行時刻の登録とアクションフック名と、アクションフックへの引数を登録することが出来る

でした。

というわけで、また次回。 🙂


目次

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

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

WordPress 徹底解析(WordPressを拡張するプログラムの構成法 – 外部 API の呼び出し)

アクションフックを使った例を用いて、WordPress プログラミングを紐解くシリーズ。引き続きまして「WordPress 徹底解析(WordPressを拡張するプログラムの構成法 – 外部 API の呼び出し)」です。

今回は LWWS お天気APIを呼び出して、記事公開時に当日のお天気情報をカスタムフィールドに入れるところまで進めてみます。


コンストラクタを使って地域IDを指定する

前回まででできあがった骨組みソースは以下のようなものでした。

プログラムがクラス化され、記事の公開を表すアクションフック「publish_post」に Otenki クラスのメソッド「prepare」を定義し、記事公開時に処理が呼び出されるところまでできています。また、テーマ用のお天気出力テンプレートタグ the_otenki も用意しました。

class Otenki {

    /**
     * カスタムフィールド・メタキー.
     */
    const META_KEY = 'otenki';

    /**
     * APIバージョン情報(V1/JSON).
     */
    const LWWS_V1_JSON = 'LWWS_V1_JSON';

    /**
     * 記事公開フックで天気情報を取得をする.
     *
     * @param $post_ID
     * @param $post
     * @return none
     */
    function prepare($post_ID, $post) {
        // お天気 API を呼び出し
        // Otenki::META_KEY, Otenki::LWWS_V1_JSON を用いて
        // カスタムフィールドに登録
    }

   /**
    * カスタムフィールドに取得したお天気アイコンを出力.
    *
    * @param none
    * @return none
    */
    function output() {
        // Otenki::META_KEY, Otenki::LWWS_V1_JSON を用いて
        // カスタムフィールドを取得しアイコン HTML を出力
    }
}

$otenki = new Otenki();
add_action(
    'publish_post', array($otenki, 'prepare'), 10, 2);

/**
 * お天気情報を出力するテンプレートタグ.
 */
function the_otenki() {
    global $otenki;
 
    $otenki->output();
}

まず、今回利用する外部API(LWWS)の仕様です。

LWWS のお天気APIは、指定された地域IDで RESTのリクエストをなげると、JSON 形式で当日以降のお天気情報が返却されるという動作が行われます。

(例)「福岡県・久留米の天気」を取得する場合
下記URLにアクセスしてJSONデータを取得します。
基本URL + 久留米のID(400040)
http://weather.livedoor.com/forecast/webservice/json/v1?city=400040

ここで指定するお天気を取得する地域ID は使う人によって変わりますので、何かしらの方法で指定する実装が必要です。

いくつかやり方が考えられますが、ここではオブジェクトのインスタンス生成時に地域IDを指定し、コンストラクタからメンバ変数に値を格納することにします。合わせて、後続でつかいそうなお天気情報出力時の CSS クラス名も設定できるようにしました。

class Otenki {
    /* 略 */

    /**
     * LWWS URL 引数(地域別値).
     *
     * @see 
     */
    var $city;

    /**
     * 画像出力時の CSS クラス.
     */
    var $css_class;

    /**
     * コンストラクタ・プラグインデフォルト値設定.
     */
    function __construct(
        $city = '016010', $css_class = 'otenki') {
        // LWWS URL 引数(地域別値)
        $this->city = $city;
        // 画像出力時の CSS クラス
        $this->css_class = $css_class;
    }

    /* 略 */
}

$otenki = new Otenki();
add_action(
    'publish_post', array($otenki, 'prepare'), 10, 2);

function __cunstruct はインスタンス生成時に PHP によって呼び出される、コンストラクタと呼ばれる特殊なメソッドです。

次のようにクラスが new されたとき、__cunstruct メソッドは自動的にコールされ処理をそこに移します。

$otenki = new Otenki();

コンストラクタの引数には、オブジェクトの new をするときに指定した引数値が渡されます。

上記の new Otenki() の場合は引数の指定がないため、コンストラクタ(__construct)のデフォルト引き数値である、’016010′ と ‘otenki’ がメンバ変数 $city(地域ID) と $css_class (CSSクラス名)に格納され保持されます。

/**
 * コンストラクタ・プラグインデフォルト値設定.
 */
function __construct(
    $city = '016010', $css_class = 'otenki') {
    // LWWS URL 引数(地域別値)
    $this->city = $city;
    // 画像出力時の CSS クラス
    $this->css_class = $css_class;
}

たとえば、久留米を地域IDに指定したい場合は、LWWS オブジェクトの生成を次のように書き換えます。

$otenki = new Otenki('400040');

さて、ここから少し余談です。オブジェクト指向、いかがですか。

このようなクラスとインスタンスの性質を利用すると、複数の地域の処理も同一のクラス定義で実行できます。

$sapporo = new Otenki();
$kurume = new Otenki('400040');
add_action(
    'publish_post', array($sapporo, 'prepare'), 10, 2);
add_action(
    'publish_post', array($kurume, 'prepare'), 10, 2);

/**
 * お天気情報を出力するテンプレートタグ.
 */
function the_otenki() {
    global $sapporo;
    global $kurume;
 
    $sapporo->output();
    $kurume->output();
}

処理シーケンスのイメージ的には次のようになります。 初期化 otenki.php からの <<new>> 部分で 2つのインスタンスが生成されていること注目してください。

otenki_seq2

単純な上位部への処理追加でいくつでも対象地域を増やせるようになっています。 🙂

ただし実装上、これからまだ考えるべきことが残っていることもシーケンスから見てとれるかもしれません。

DB へのデータ保存時のキー設計(カスタムフィールドを地域別にする?)や、ソースを修正することなく設定で動的に地域の増減を行いたい場合は、生成したインスタンスを管理する実装などが該当します。

これらの処置はそれほど難しくないものの、今回のサンプルソースとしては若干意味がぼやけてしまいそうなので、つくり込みは省略し他で解説することにしましょう。

オブジェクト指向の基本的な使い方のひとつを紹介したく、若干脱線してみました。普段オブジェクト指向が分からないと感じている方は、まずはシーケンスの確認を。異なる値を持った対象が new によってつくりだされ、それぞれを区別して処理を呼べる感覚が分かれば出来たも同然です。

ではここからは単純に、コンストラクタで地域IDが指定できるようになったことに注目していきます。 コンストラクタを用いることで処理に必要な変数の初期化のタイミングを得ることができます。

以上で地域IDの可変化を行う準備が整いました。まだ、ソースコードファイルを修正しないとユーザが地域IDが設定できませんが、これは後の管理画面までのお楽しみとしておきましょう。 🙂

外部 API からのお天気情報取得

外部 API を呼び出しお天気情報を取得します。

コンストラクタで設定した、地域ID($city) とアクションフックから渡された投稿記事情報 $post_ID, $post を元に、記事の公開時に WordPress によって呼び出される prepare メソッドを実装します。

/**
 * 記事公開フックで天気情報を取得をする.
 *
 * @param $post_ID
 * @param $post
 * @return none
 */
function prepare($post_ID, $post) {
    // "今日"のポストの時だけ天気を取得
    if(date("Y/m/d", strtotime($post->post_date))
        != date("Y/m/d")) return;
    // LWWS(V1/JSON)より天気情報を取得
    $json = wp_remote_get(
        'http://weather.livedoor.com/forecast/webservice/json/v1?'
        . http_build_query(array('city' => $this->city)));
    // 取得できなかった場合は次回の公開に期待
    if(is_wp_error($json)) return;
    // リクエストから JSON データ取得し PHP 連想配列に変換
    $otenki = json_decode($json['body'], true);
    // 正常な JSON 形式ではなければ次回の公開に期待
    if(!$this->isOtenki($otenki)) return;
    // カスタムフィールドにバージョン情報付きで格納
    update_post_meta(
        $post_ID
        , Otenki::META_KEY
        , array(Otenki::LWWS_V1_JSON => $otenki));
}

ソースを上から順に追っていきます。

まず最初に、公開された記事が当日かを判定します。これはお天気APIが”本日”の情報を返却するためです。過去日の記事の公開に対して取得を行うと”本日”の情報で上書きされてしまうため、これを抑止します。

// "今日"のポストの時だけ天気を取得
if(date("Y/m/d", strtotime($post->post_date))
    != date("Y/m/d")) return;

公開対象記事の情報を知るにはアクションフックから渡されてきた $post の情報を用いています。(var_dump($post) すると分かりやすいでしょう)

次にいよいよお天気 API に対して REST リクエストをなげます。 WordPress にはこのような http での外部情報取得のために wp_remote_get 関数が用意されてます。

実はこの処理に、古いソースでは PHP 標準関数の file_get_content を忘れて使ってしまっていたのでした。(まがりんさんご指摘ありがとうございます)

WordPress に準備された wp_remote_get 関数は、条件により適切な PHP の通信関数を選択がされ、詳細な通信ステータスの取得ができる機能を持っています。また、内部処理にフックがあちこちに仕掛けられており外部からの制御も可能ですので、WordPress で外部通信を行う場合はこちらを用いましょう。

使い方は簡単で、引数に API などのエンドポイント URL を渡し、is_wp_error で通信エラーハンドリングします。

URL に、コンストラクタで設定したメンバ変数($this->city)から地域IDを引数として付与していることにも注目してください。

// LWWS(V1/JSON)より天気情報を取得
$json = wp_remote_get(
    'http://weather.livedoor.com/forecast/webservice/json/v1?'
    . http_build_query(array('city' => $this->city)));
// 取得できなかった場合は次回の公開に期待
if(is_wp_error($json)) return;

wp_remote_get で取得したデータ本体は、$json[‘body’] に格納されます。

取得した JSON 形式を PHP の連想配列に変換し、正しいデータ形式かの正当性確認(バリデーション)を行います。

// リクエストから JSON データ取得し PHP 連想配列に変換
$otenki = json_decode($json['body'], true);
// 正常な JSON 形式ではなければ次回の公開に期待
if(!$this->isOtenki($otenki)) return;

外部 API から取得したデータのバリデーションチェックは非常に重要です。期待値が返ってくればよいものの、異常データが戻ってきた場合、特に連想配列で処理している局面で PHP が警告を発砲し不具合になる場合があります。

また、お天気の HTML の出力ではここで登録されたカスタムフィールド値の取得処理を行いますが、こちらも同様に、ユーザが手で不正な値を登録されたり変更されると不具合になる可能性がありますので、念のためバリデーションしておいたほうがよいでしょう。

このように2つのメソッドで使われる同じバリデーションになりますので、ここでは isOtenki メソッドとして外だしにしています。

連想配列を、LWWS の仕様を元に”本日のお天気”部までツリーをたどり、存在するかを確認しています。

/**
 * お天気情報バリデーション.
 *
 * @param unknown $otenki
 * @return boolean
 */
function isOtenki($otenki) {
    if(!(is_array($otenki)
        && isset($otenki['forecasts'])
        && is_array($otenki['forecasts'])
        && isset($otenki['forecasts'][0]['image']))) {
        return false;
    }
    return true;
}

PHP の配列仕様に、string 値に添え字をつけるとその位置の1文字が返ってくるというちょっと悲しい下位互換機能があるため、少ししつこい判定になっています。

バリデーションを抜け正しいお天気情報だと確認できたら、いよいよカスタムフィールドに登録です。

アクションフックからもらえる記事ID($post_ID)と、カスタムフィールド用のキーをもって、カスタムフィールドに連想配列を全て登録します。API のバージョンアップを鑑みて API バージョン情報も独自に付与しています。

// カスタムフィールドにバージョン情報付きで格納
update_post_meta(
    $post_ID
    , Otenki::META_KEY
    , array(Otenki::LWWS_V1_JSON => $otenki));

格納する情報は、お天気出力処理に必要な”今日のお天気”だけでよいのですが、予報の文言なども面白いためそのまま全部入れています。(この辺は趣味です)

というわけで、ようやくAPI の呼び出しと、カスタムフィールドへの登録まで完成しました。 🙂

今回のポイントは、

  • クラス化した処理の、メンバ変数の初期化や設定はコンストラクタで行える。
  • 複数のインスタンスを使い処理を簡略化するオブジェクト指向の技がある。
  • 外部 API への http リクエストには wp_remote_get を使う。
  • 外部 API からの返却値はバリデーションをしっかり行う。

でした。

WordPress と PHP プログラミングテクニックの組み合わせの実例ということで、それぞれ単独でみるよりも面白いんじゃないかと思って書いてみましたが、いかがでしょうか。。

では、また次回。 🙂


目次

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

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

WordPress 徹底解析(WordPressを拡張するプログラムの構成法 – 処理のクラス化)

「WordPress 徹底解析(WordPressを拡張するプログラムの構成法 – 処理のクラス化)」いきます。 🙂

前回は、アクションフックの動きと簡単な処理の実装まで行ってみました。

実はこの記事、”プラグインをつくる”としてしまったのですが、WordPress のフックの機能はテーマの functions.php でも同様に使える手法です。

テンプレートファイルの functions.php は、プラグインファイルと読み込み順番が若干違うだけでプラグインと同様な実装ができます。テーマと同時に有効になるプラグインファイルと考えても差し支えないでしょう。というわけで、テーマを主につくる方も読み進めていただければと思います。紹介しているソースも functions.php にかけばそのまま動くはずです。

さて、今回は以下のような WordPress を拡張する上でのプログラム構成のノウハウを、引き続きアクションフックのプラグインのソースを元に紹介していきます。

  1. 処理のクラス化
  2. 外部 API の呼び出し
  3. テンプレートファイルに情報を出力する方法
  4. 外部 API 呼び出しの並列化
  5. 管理画面をつくる

まずは「1. 処理のクラス化」より。


処理のクラス化

前回紹介した、アクションフックを使う基本コードは以下のようなものでした。

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

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

これをベースに以下の処理を実装していきます。

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

大きく2つの機能があり、双方ともカスタムフィールドの値の処理(設定と取得)で関連しています。これらの処理には関数が2個以上は必要そうです。

プラグインやテンプレートファイルの functions.php に定義する関数や変数はグローバルスコープになります。WordPress 本体や他のプラグインと共通的な空間に定義されますので、複数が大きくなり関数が増えるとお互いの干渉が気になってきます。大きなところでは、変数や関数名称の重複です。

このような場合、PHP のクラスの機能を名前空間として使い処理をまとめると、グローバルに対する干渉を最小限に抑えることが出来ます。(またオブジェクト指向をつかったトリックも使えるようになります)

先のグローバル関数をひとつ使ったアクションフックの定義をクラスを使ったものに変更すると次のようになります。

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

$otenki = new Otenki();
add_action('publish_post', array($otenki, 'prepare'), 10, 2);

クラスを定義(class Otenki {})し、その下で $otenki 変数に Otenki オブジェクトのインスタンス内を格納します。

$otenki = new Otenki();

クラス内に移動したメソッド(function prepare() { } )を add_action するには、’prepare’ と関数名のみを指定していた引数を、 array($otenki, ‘prepare’) と変更しインスタンスを特定できるようにします。

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

以上の変更でクラス Otenki が名前空間代わりになります。クラス内部に実装するメソッドや定数・変数は、他との名前の重複など考えず自由に定義できます。

なお、クラス名 Otenki とそのインスタンスを格納する $otenki 変数は依然としてグローバルです。この部分だけ他との干渉を意識します。

(WordPress とのインターフェースになるクラスはいろいろな機能が実装されてしまうためクラス名には悩みますが、、SI業界的(?)には OtenkiPluginFaçade とかでしょうか。モジュール分割の規模によって決めると良いと思います。今回は処理が小さくひとつのクラスで済みそうなので単純に Otenki としました)

では「お天気情報の取得」と「テンプレートファイルからお天気情報を出力する」2つのメソッドを実装します。 まずはソースの骨組みのみ。

class Otenki {

    /**
     * カスタムフィールド・メタキー.
     */
    const META_KEY = 'otenki';

    /**
     * APIバージョン情報(V1/JSON).
     */
    const LWWS_V1_JSON = 'LWWS_V1_JSON';

    /**
     * 記事公開フックで天気情報を取得をする.
     *
     * @param $post_ID
     * @param $post
     * @return none
     */
    function prepare($post_ID, $post) {
        // お天気 API を呼び出し
        // Otenki::META_KEY, Otenki::LWWS_V1_JSON を用いて
        // カスタムフィールドに登録
    }

   /**
    * カスタムフィールドに取得したお天気アイコンを出力.
    *
    * @param none
    * @return none
    */
    function output() {
        // Otenki::META_KEY, Otenki::LWWS_V1_JSON を用いて
        // カスタムフィールドを取得しアイコン HTML を出力
    }
}

$otenki = new Otenki();
add_action('publish_post', array($otenki, 'prepare'), 10, 2);

class 内に prepare と output メソッドが実装され、共通するカスタムフィールドのキーなどが const(定数)で定義されていることが分かります。処理の class 化を行うと、このような共通項がグローバルへの影響無しに自由に実装できるようになります。

さて、アクションフックにより「お天気情報の取得」を担う prepare メソッドの呼び出しはうまくいきそうですが、テンプレートファイルにお天気情報を出力する output メソッドはどのように呼び出せばいいかという疑問がわいてきます。

これは、Otenki クラスのインスタンスが格納されている $otenki 変数がグローバルスコープであることを利用して、テーマのテンプレートファイル内で次のように呼び出すことができます。

$otenki->output();

ただ WordPress のテンプレートタグ的には、オブジェクト指向のメタファーが入ってしまうため、若干違和感があります。WordPress 本体では似たケースで次のような実装がされています。

テンプレートのループで使われる the_post() 関数。

wp-includes/query.php

/**
 * Iterate the post index in the loop.
 *
 * @see WP_Query::the_post()
 * @since 1.5.0
 * @uses $wp_query
 */
function the_post() {
    global $wp_query;

    $wp_query->the_post();
}

グローバルスコープののインスタンスが格納されている変数を関数に持ってきてメソッドを呼び出す、ヘルパー関数を準備しているわけです。

今回のケースも同様にヘルパー関数をつくってあげれば良さそうです。

/**
 * お天気情報を出力するテンプレートタグ.
 */
function the_otenki() {
    global $otenki;

    $otenki->output();
}

以上の、クラス定義、アクションフックの登録、ヘルパー関数がかかれたプラグインファイルが otenki.php とすると、動作シーケンスの概要は次のようになります。

otenki_seq

本来は記事の公開(do_action(‘publish_post’))とテーマ出力処理は同時には起きえませんが、この図では一緒に書いております。逆にこの動きを理解することで、1アクセス内で発生する順次処理の結果を、クラスのインスタンスに保持し、合成した結果を出すような処理もつくることができるでしょう。

というわけで、なかなかプラグインが完成しませんが、、また明日。 🙂


目次

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

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

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() テンプレートタグ。

欧米か!

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

ということで 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 を用いたスケジュール化手法を解説しています。
テンプレートファイルからの情報出力
テンプレートファイルに処理を記述し出力する場合に、拡張モジュールと疎結合にする実装法を紹介します。アクションフックの活用例としても有効です。
管理画面をつける
管理画面に設定欄を設けユーザが拡張の機能をコントロールできるようにする手法の解説です。また、本連載中のソースコードに最終的なリファクタリングをかけ、他の開発者にこの拡張の意味を表現するプログラミングテクニックも解説します。
まとめ
本連載の概要とまとめ、そして目次です。

canvas と Web 仕掛け絵本

明けましておめでとうございます。 🙂

昨年のご愛顧も込めまして、「WordPress デザインワークブック」共著のコモモさんこと高橋朋代さんと Web 仕掛け絵本のサイトをつくりました。とてもかわいい感じになりましたので、良ければ見てください!

バッタになりたい魔法使い – pararaehon.com

PC/Mac はモダンブラウザ(IE8 以下はごめんなさいです)に対応しています。

モバイル系は iOS は 6 以降の Mobile Safari、Android は 4 以降の Chrome が良い感じで見れます。Android 2 の方は Firefox 17 を使うとゆっくりですが動作します。3G 回線の場合は、ちょっと読み込みに時間かかりますので、白くなったりしたらリロードしてみてください。キャッシュするときれいに動きます。

iPhone/iPad は 4S 以降(Apple A5)でフルスペックで動くはずです。すごい性能。iPhone の場合は Retina ででます。絵本的になりますので iPad3 もおすすめです。ベンチマークにもどうぞ。

自分の本職はバックエンド側の処理、業務系アプリの処理やインフラなどなのですが、WordPress を含めこういった Web サイトをつくる場合には、芸術性のあるデザインやブラウザフロントエンド処理が不可欠です。

WordPress デザインワークブック” の執筆ではこの部分にコモモさんの全面支援をもらっていますが、”バッタになりたい魔法使い”では絵をコモモさんに、ひろましゃはお話と、なんと初 JavaScript をやっております。 動きも面白いですので良ければその辺もご覧ください。 🙂

さて、去年はこのブログで「WordPress 徹底解析」シリーズを週1でやろうと始めておりましたが、少し忙しくなってしまい止まってしまってごめんなさい。2月くらいからまた時間が出来ると思いますので再開したいと思います。

今年もどうぞよろしくお願いいたします!


WordPress ではないですが、せっかく Web 系のネタですのでこの仕掛け絵本の仕組みを解説したいと思います。 まずは動作見て頂ければ。スクロールを上下するとぴょこぴょこ動くのが分かると思います。 🙂

バッタになりたい魔法使い

Web 仕掛け絵本で実現したかったことは2つでした。

  1. 60FPS のフルフレームで動作させる
  2. スマートフォン・タブレットでも動かす

絵本という前提があるので、コンピュータ的なフレーム落ちしたガタガタした動作は物語の世界を壊すと考え、ブラウザに 1/60 秒フレーム描画(canvas & requestAnimationFrame)をさせています。また、スマートフォン、タブレットでみれたら絵本っぽいなというのも考えました。

このため、canvas と requestAnimationFrame(もしくはベンダープレフィックス)が使えないブラウザでは動作しません。IE で言えば IE9 以降に対応します。ちなみに IE9 は結構速かったです。スマートフォン系では iOS6 以降、Android 4(標準ブラウザもしくは Chrome、Firefox17)が動作します。iOS5 の方、ごめんなさい…

この仕組みにパララ絵本という名称をつけていて、いわゆるパララックス効果も入っているのですが、通常のパララックスとは異なります。というのも、普通のパララックスはブラウザの縦座標(scrollTop)に対して、描画オブジェクトの位置を決めますが、パララ絵本ではスクロール位置を動かすタイミングとしてだけ使って、あとはタイマー処理しています。これは 60 フレーム描画をするためと、スマートフォン(Mobile Safari)がスクロール中動作にイベントが発生しないことに対する仕様です。

実際の実装ですが、絵本のコマに対して canvas タグを対応させ、CSS では canvas を display: block; してブラウザの描画領域を全て埋め、横幅、余白を含め全ての描画制御を JavaScript から行っています。たとえば、背景画像横幅が足りなければ CSS で真ん中にもっていくのではなく、”白”を画像の左右に描画しているイメージです。

ブラウザにやらせるよりも自前で制御したほうが、必ず見せたい領域を中央にもってくる処理や、 devicePixelRate(retina)の対応など、描いてもらった絵を忠実に再現させるのが楽だったというのが理由です。(このためちょっとブラウザリサイズの処理は遅いです)

<body id="parara">
<canvas></canvas>
<canvas></canvas>
...
</body>
body {
    margin: 0;
    padding: 0;
}

canvas {
    display: block;
}

JavaScript のソースはオーソドックスな、いわゆるゲーム描きです。requestAnimationFrame(render) と通常タイマー(update)の二つをつかった 1/60 処理です。

/**
 * Event initialize.
 */
Scene.prototype.start = function() {
    this.hack();
    window.addEventListener('load', this.init.bind(this), false);
    window.addEventListener('resize', this.init.bind(this), false);
    window.addEventListener('orientationchange', this.init.bind(this), false);
    window.addEventListener('scroll', this.update.bind(this), false);
    window.setInterval(this.update.bind(this), 1000 / ANIMATION_FPS);
    this.render();
};

/**
 * render.
 */
Scene.prototype.render = function() {
    requestAnimFrame(this.render.bind(this));
    // viewport range render.
    ...
    ...

...
/**
 * requestAnimationFrame wrapper.
 * context invalid?: Illegal operation on WrappedNative prototype object
 */
window.requestAnimFrame = (function() {
    return window.requestAnimationFrame
    || window.webkitRequestAnimationFrame
    || window.mozRequestAnimationFrame
    || window.oRequestAnimationFrame
    || window.msRequestAnimationFrame
    || function(/* function */callback, /* DOMElement */element) {
        window.setTimeout(callback, 1000 / ANIMATION_FPS);
    };
})();

オブジェクトの設計は、複数あるコマ(Scene)、コマ(Page)、コマ内の描画オブジェクト(Actor)と分割し、Scene ではブラウザのリサイズやスクロール位置処理と update render タイミングをとる処理、Page では Actor の実座標や動作タイマー(tick)制御、Actor は描画処理(canvas.drawImage)という感じになっています。

描画オブジェクトも全てビットマップ(Image)ですので、これらに対する DOM の操作はありません。全て canvas コンテキストに描いています。

Actor の座標はそれぞれに設定された動作開始スクロール位置からタイマーで +1 増加を始める tick 値によって決まり、逆スクロールでは時間(tick)を巻き戻すことで元の配置に戻るようにしています。つまり x += 1; とやるわけではなく x = tick とするイメージです。

隠しコマンドで「d」キーを押すとデバッグ表示になるようにしています。たぶん見るとすぐ分かると思います。

また、Actor の座標に関しては PararaEhon.js 初期化時にコンストラクタで定義できるようになっており、update や efect(drawImage する直前)をメソッドを処理時に差し込み、仕掛け絵本ライブラリとして使えるようにしています。

/**
 * バッタになりたい魔法使い.
 *
 * @author komomo and hiromasa
 * @version 1.0
 */
new PararaEhon.Scene({
    id : 'parara',
    width: 960,
    height: 600,
    book : [
        /**
         * ページ01
         */
        {
             bg: {
                 image: 'images/page01/01_bg.png',
                 fixed: true
             },
             wizard: {
                 image: 'images/page01/01_m.png',
                 x: 50,
                 y: 25
             },
             .....
        },
        /**
         * ページ02
         */
        {
             .....
             hitsuji01: {
                 image: 'images/page02/02_hitsuji01.png',
                 x: 15,
                 //y: 320,
                 start: 200,
                 update: function() {
                     this.y = 600 + (1 - Math.exp(-6 * (this.tick / 60))) * -280;
                     if(this.tick > 60) this.complete = true;
                 }
             },
             .....
/**
 * Constructor.
 */
Actor = function(context, object) {
    .......
    this.update = this.update ? object.update.bind(this) : null;
    this.effect = this.effect ? object.effect.bind(this) : null;
    .......
};

さて、キモとなる canvas.drawImage ですがブラウザによっていろいろ面白い動作がありました。Gecko や Webkit のソースまで見ていないので本当に見た目だけの判断です。間違っている可能性大きいです。

  1. Firefox17 と IE9 は スクロールで見えなくなっている canvas にもまじめに drawImage している?(見えているコマだけ render したら速くなりました)
  2. Firefox17 の Mac と Windows 版は描画が少し遅い。Linux 版(しかも性能の劣る機械)だとフルルフレームで描画される。(本当にこれはなんとなくなのですが Linux 以外は Image が GPU じゃなくてメインメモリから転送されているような。うまく VRAM に乗らなかった?気のせい?)
  3. Firefox では drawImage の引数で画像範囲外を誤って転送させても動くが、他のブラウザだと動作しない。(動かない動作は OpenGL で同様の間違いをしたときの動作に似ている)
  4. Mobile Safari に過激に複数の canvas をレンダリングさせると落ちる時がある
  5. Android Firefox 17 が結構がんばって canvas 描ける。(遅いですが)
  6. 速度の印象は Safari < Chrome < Firefox17 = IE9 という感じでした。がんばれ Firefox!(もうすぐでるであろう Firefox18 / IonMonkey が楽しみです)

なにぶんにもモダンな JavaScript をかくこともブラウザもほとんど初めてで、押し迫る期限の中座標処理でうひょ〜となってしまった部分もあり、まだまだ不出来ですがもし興味ありましたらソースを見てみてください。 🙂

PararaEhon.js

maho.js

というわけで。

みんなバッタになっちゃえ!

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

週一回で始まった、WordPress 徹底解析シリーズ。 いきなり1週お休みをいただくというさすがぼく的な展開を見せていますが(ちょっと忙しかったのです・・・)、なんとかがんばって続けていきましょう。 🙂

前回まではこちら。

WordPress 徹底解析(PHP Development Tools インストール編)

また、テーマ制作中に WordPress 本体のソースを追うことも容易です。WordPress 本体のソースは WordPress のテクニックや考え方の宝庫です。

WordPress のコア(本体)ファイルを見ながら、WordPress で使えるテクニックと、おまけに PHP も面白く理解していこうというのが本シリーズの意図です。前回は準備としてコアのファイルが簡単にみれる環境を構築してみました。

WordPress のようなプログラマブルなアプリケーションを使いこなすには、そのコアのソースコードをみるのが、早く、はまらず、実装の間違えも少なくなると経験的に思います。 ソースコードを見るのは素早く作業を進めるための手段です。そのために、極力その手間を減らすために開発環境を構築しました。

あとは”とっかかり”が分かれば、、ということで今回は WordPress のカスタマイズを構成する要素について解説していきます。

WordPress のカスタマイズとは

まずは、このシリーズでいうところの “WordPress のカスタマイズ”について定義しましょう。

  1. 既存のテンプレートタグの動作や出力を変更する。(HTML の class 名追加や、出力条件、項目、表現の変更等)
  2. 管理画面に新しい機能を追加する、何かしらのタイミングで自分で記述した新しい処理を追加する。(投稿画面に新しい入力項目を追加し保存する、ポストしたタイミングで twitter に URL を投稿する等)
  3. ショートコードやウィジェットの追加。
  4. WordPress が標準で行う処理をまったく違うものに置き換えてしまう。(ログイン認証をユーザパスワード認証から OpenID や LDAP に変更する等)
  5. DB接続エラー画面の制御やメンテナンス中の画面、カスタマイズされた DB アクセスやキャッシュ制御を追加する。

ここでは上記を WordPress のコア(本体)ファイルを書き換えずに実現することをカスタマイズと呼ぶことにします。 WordPress では、そのためにいくつかの API や仕組みが用意されています。

  • フィルター
  • アクション
  • ショートコード、ウィジェットAPI
  • オーバーライドできる関数の上書き
  • ドロップインファイルの配置

今回から、それぞれの特徴と、その後にどこにどのように記述すれば動き出すかという部分を解説をしていきたいと思います。まずは、フィルター・アクションの紹介よりいってみましょう。

なお上記以外にも、テーマテンプレートファイル内で既存のテンプレートタグ(関数)を組み合わせて標準ではできない表現をすることなどもカスタマイズに入ると思います。 こちらについても大切な要素ですので PHP の知識とともに、追々フォローしていきたいと思います。

フィルターとアクション

フィルターとアクションとは、WordPress の動作を、事前に定義しておいたタイミングで処理の変更・追加を行うための仕組みの名称です。このことをフックと呼びます。フィルターフック、アクションフック、です。

フィルターフックは WordPress から入力されてくる値の変更を行い、アクションフックはそのタイミングにおける処理の追加を行います。 これらは、ぼくたちが利用するだけでなく、WordPress の内部処理においても積極的に使われているものです。

フィルターがコアで使われている代表例は、記事内に書いた顔文字変換  :D ←これとか、改行を二つ入れると <p> タグに変換されるフォーマット変換などです。 これらは入力されてきた記事の内容を出力時に値の変更を行っているイメージです。もちろん、変更できる対象は記事の内容にとどまらず、タイトルや HTML タグの属性まで多岐にわたります。

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

フィルター・アクションの仕組みはぼくたちにも開放されていますので、同様の動作をテーマやプラグインから追加できます。また、コアの定義で不必要なフックは削除することもできます。

気になるのは WordPress ではどんなタイミングのフックが配置されているかという部分・・・。先に申しますと、だいたい考え得るありとあらゆるところにあります。 なので、こんなことできたらいいなが代表例のような値の変更や処理の追加だった場合がフィルター・アクションの出番と覚えて差し支えないでしょう。 次回以降、その探し方を解説していきます。

さて、よくフィルターとアクションの差がよく分からないという声を聞きます。実はそれもその通り。 アクションを定義するコアの add_action 関数のコードを見てみると・・・

wp-includes/plugin.php

function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
    return add_filter($tag, $function_to_add, $priority, $accepted_args);
}

add_action はさらにフィルターを定義する add_filter で実装されていたりします。タイミングに対する処理という意味ではどちらも同じものなので内部的にはフィルターを使っているわけです。 違いは値の変更をするか、処理の追加をするかということになります。

それぞれに定義されたフックを呼び出すコアのコードは次のようなものです。

フィルター。 wp-includes/plugin.php::apply_filters 関数抜粋。

do {
    foreach( (array) current($wp_filter[$tag]) as $the_ )
        if ( !is_null($the_['function']) ){
            $args[1] = $value;
            $value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
        }
    }
} while ( next($wp_filter[$tag]) !== false );

アクション。 wp-includes/plugin.php::do_action 関数抜粋。

do {
    foreach ( (array) current($wp_filter[$tag]) as $the_ )
        if ( !is_null($the_['function']) )
            call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
} while ( next($wp_filter[$tag]) !== false );

フィルター・アクションともに $wp_”filter” 変数を使うほとんど同じコードになっています。

異なるのはフィルターのほうには $value があり、なんとなくループで同じフィルター処理に対して数珠つなぎで $value が渡っていくのが見て取れます。 これが、たとえばコアで設定されている記事出力に対するフィルターに、顔文字フィルター、HTML フォーマットフィルターなどと複数の設定が可能で、最終的に合成された結果を返す秘密です。

さて、いくつかプログラムのソースコードと関係する関数名がでてきました。 add_filter、apply_filter と add_action、do_action。前から、フィルターの登録・適応とアクションの登録・適応するための関数になります。次回はこの名前をヒントに、フィルター・アクションの実際やそのタイミングの探し方を見ていくことにしましょう。

このシリーズはブログ記事なので、いきなり新しい言葉がでてきてちょっとよく分からなかった方もいるかもしれません。

ぼくも共著させていただいている書籍のほうでは、WordPress サイト制作の基本から応用まで言葉も含めて解説しておりますので良ければ手にとっていただければと思います。

さて、昔話になりますが、ぼくが小学生くらいだったときは、ソースコードとその考え方をおもしろく筆者の思考の流れとともに解説する月刊誌や技術書が沢山ありました。 きっと、同年代の方ではそのような雑誌で育った方も多いかと思います。

もしかすると、ぼくが書くブログや書籍はそんなみんなが忘れかけた、知らない方には新しい(?)表現が垣間見られてるんじゃないかな、、と最近思います。

いまだに大切にしている 1991 年の技術書があります。やっぱり今読んでも面白いのです。

ぼくはコンピュータにさわり始めたのが早いので、マイコン世代の方より少し若輩者ではありますが、本来楽しいはずであるソフトウェアの世界をお伝えすべく、、次回もお楽しみに。 🙂

WordPress 徹底解析(PHP Development Tools インストール編)

WordPress のテーマなど PHP を記述するアプリケーションを使う場合は、Eclipse などの IDE を使うと非常に便利です。

特に PHP と近い位置でテーマの制作をする WordPress では IDE が持つ PHP の解析機能の助けを借りると、テンプレートファイルへのテンプレートタグの記述や、エラーチェック、不具合が起きたときの解析、functions.php やプラグインの記述が非常に楽になります。

また、テーマ制作中に WordPress 本体のソースを追うことも容易です。WordPress 本体のソースは WordPress のテクニックや考え方の宝庫です。

WordPress デザインワークブック」でもいくつか WordPress コアのソースを紹介しています。た とえばカスタム分類の解説では、

“「投稿」や「固定ページ」も、本体でカスタム投稿と同様に register_post_type 関数で登録されている”

なんてことをコアのコードとともに紹介しています。じゃーもしかしてカテゴリやタグもカスタム分類と同じ register_taxnomy ? …正解です。

WordPress デザインワークブックもまた、 IDE で各テンプレートタグなどをコアから追いながら執筆しています。考え方ベースで手順も優しく書いていて面白い本と思いますので、よければ手に取って見てください。 🙂

さてというわけで、IDE はこういったソースコードの調査や記述を強力に支援してくれます。

IDE とは

IDE(統合開発環境)はテキストエディタと異なり、記述されたプログラム言語を常に解析しながら動きます。常にプロジェクト配下のソース構文木を意識しているため、シンタックスエラーチェックはもちろん、辞書を必要としないコード補完や、適切な関数への定義ジャンプ、リファクタリングと呼ばれる関数・変数名の変更などが行えます。

たまに横にファイルツリーがあるテキストエディタが統合開発環境として紹介されていますが、完全な言語解析機を持たないテキストエディタと IDE は本質的に異なるソフトウェアです。

たとえば、Eclipse の PHP Development Tools では次のような機能を持ちます。 NetBeans などでも同様のことが可能でしょう。

構文エラーチェック。

リアルタイムに構文エラーや変数初期化に関するワーニングなどが出力されます。 ちなみに、Eclipse では PHP のような動的型付けスクリプト言語の解析に、DLTK(Dynamic Language Tool Kit)という仕組みが用いられています。

コード補完機能。

事前生成タグやスニペット的なものを使っているわけではないので、常にプロジェクトのファイルの最新を追いかけて補完できます。

定義の関数がどこにあるか IDE は知っているので、その場に PHPDoc などが出力できます。

さらに、関数の上で F3 (Fn + F3) などとするとそこの関数にすっとんで関数を読んで解析ができます。ぼくはこれができないと始まりません 🙂

その他、変数間接参照からの定義ジャンプができるなど、動的型付け言語の上でもさすがと思わせる機能がたくさんついています。(動的型付け言語はプログラムの形が実行時に変わるためこういった処理がなかなか難しい)

てなかんじで、便利そうな IDE ですがちょっとヘビーで使ってない方もいるかもしれません。

別に必要ない時は使わなければいい!

ってわけで、Eclipse PHP Development Tools(PDT) は必要な時だけ使うようにもできています。普段は Dreamweaver やテキストエディタで制作を進めることもできます。

Eclipse ではファイルのかたまりをプロジェクトという単位で管理しますが、PDT ではプロジェクトの位置を任意の場所で指定できます。 つまり、製作中のファイル群に対して、必要になったときだけ Eclipse を起動して使うことができます。

ではそんな Eclipse のインストール方法を紹介します。 なんと Mac、Linux、Windows 対応のインストール手順。 Windows は若干手抜きで。 Solaris、AIX、HP-UX も入れますか?(笑)

Java の導入

Eclipse は Java プラットフォーム上で動作するアプリケーションです。 Java を入れる必要がありますが、Mac OS X (Lion / Mountain Lion)では Java はのちほど Eclipse 起動時に自動的に入るようにアナウンスされるので操作不要です。

Linux の方は Sun(Oracle) Java か OpenJDK かを入れてください。 Ubuntu の場合は端末から以下のようになります。

$ sudo apt-get install openjdk-7-jdk

Windows の方は適当にいれてください(笑)←すぐ分かりそうなので、、

ちなみに、Java は Web で使われる(使われてない気もする)アプレットでたまに脆弱性がでるので、ランタイムと一緒に導入されるブラウザのプラグインは切っておいた方がいいかもしれません。

Eclipse の導入

Eclipse はポータブルにできていますので、導入はファイルをダウンロードして適当な場所に展開すれば終わりです。 Mac でのスクリーンショットを元に紹介します。

まずは以下から Eclipse をダウンロードします。 実は PHP を扱う Eclipse ディストリビューションは用意されていませんので、最初から便利なプラグインがいろいろ入っている Eclipse IDE for Java EE Developers を選択し、後から PDT をプラグインの形でインストールします。

Eclipse Downloads

Eclipse IDE for Java EE Developers

Mac OS Xの場合は Cocoa 64bitを、Linux の場合は GTK 版お使いのカーネルビルドを、Windows の場合も同様に 32/64bit を選択します。上記の URL はブラウザのエージェントみているようですので、各 OS に合わせたダウンロードサイトがでてきます。

ちなみに、Eclipse は Java のアプリケーションですが、NetBeans などと異なり各 OS のネイティブで動作する SWT というライブラリの上で動作するため、プラットフォームツールキット(GUI)との親和性が高い綺麗な画面が表示されます。

ダウンロードしたアーカイブファイルを任意の場所で展開します。 展開後は「eclipse 」というフォルダが作成されますが、フォルダ名をアーカイブファイルと同じバージョン表記がある名前にしておくと便利です。 Eclipse は手軽にいくつでもインストールすることができます。

展開したフォルダを OS のアプリケーションフォルダなどに格納します。

Eclipse には1つのアプリケーションファイルをマルチユーザで使い、ユーザによりプラグインをわけるようなモードもあるのですが、プラグインによってはうまく動作しないこともあるようです。

とりあえずぼくは、Mac の場合は /Application 下に、Linux の場合は /opt に置きたいところですが ~/apps などの下に置いています。Windows も \Develop などを作成して、その下に置くといいでしょう。 レジストリなどに設定をかくはありませんので、そのままコピーすれば別環境にもっていくことができます。

Mac の /Application(アプリケーション)の下に、フォルダ「eclipse-jee-juno-SR1-macosx-cocoa-x86_64」を置いたの図。

Linux の ~/apps の下などに配置します。

次に、日本語ランゲージパックを導入します。 英語のままで良い方はとばしてOKです。

Eclipse 日本語化言語パック (サードパーティ版)

Eclipse IDE for Java EE Developers

NLpackja-eclipse-jee-juno-blancofw20120628.zip

上記からダウンロードして展開したファイルを、先ほどの Eclipse フォルダの下にある「dropins」以下に「nlpack」というフォルダを作成して配置します。

配置は dropins/nlpack/eclpse/plugins… のようになります。

これでインストールはおしまいです。

Eclipse の起動はフォルダの下にある、eclipse 実行ファイルを起動すればOKです。 Mac の場合は Eclipse.app、Linux は eclipse、Windows は eclipse.exe をダブルクリックしましょう。

Mac 及び Windows の場合は、許可のない実行ファイルということで警告がでます。

Windows の場合はダイアログから許可を行い、Mac の場合は control(command ではない)を押しながら右クリック「開く」します。

これで起動です。 🙂

最初にワークスペースの位置をきいてきますので、(実は PDT ではほとんど使わないのですが)そのまま OKします。「この選択をデフォルト~」にチェックいれておくと便利です。

Mac 版。 ようこそをばってんで閉じます。

Windows 版。(ばってんした後)

Linux 版。(既に動作中のスクリーンショット)

それぞれ OS ネイティブで画面が描かれているのが分かります。 これが SWT の威力! 😀

Eclipse の設定と PDT の導入

起動したらとりあえずフォントとエンコードの設定をします。

Mac の場合はメニューの Eclipse -> 環境設定。 Linux / Windows の場合は、ウインドウ->設定より各種設定ができます。Eclipse は設定が多いので検索がきくようになっています。

Windows と Mac はなぜかデフォルト文字エンコードが Sfhit_JIS になるので検索窓に encode といれて「テキストファイルのエンコード」を「UTF-8」に。

フォント設定は font と入れるとでてくる画面でいろいろ設定できます。

BASIC ツリーの下のテキストフォント、テキスト選択フォントを変更するとテキストエディタ部分に関しては、そこがデフォルトして使われ全体的に変わります。

次に PHP Development Tools プラグインの追加を行います。これを導入することにより、Eclipse で PHP が扱えるようになります。といってもメニューからできるので簡単です。

ここからは各 OS 共通です。 メニューのヘルプ->新規ソフトウェアのインストールを選択します。

作業対象をドロップダウンから「http://download.eclipse.org/releases/juno」とし、その下のテキストボックスに「PHP」と入力してエンターしてください。しばらくすると選択がでてきますので、PHP Development Tools(PDT) SDK Feature を選択して、あとは次へ次へで完了です。

このように Eclipse はネットワークを介してプラグインなどのパッケージを導入することができます。

同様に、ヘルプ->Eclipse マーケットプレースからサードパーティーのパッケージをインストールすることができます。 たとえば、Git(EGit)や Subversion(Subclipse)、JS/jQuery(JSDT jQuery)サポートなどなどいろいろあります。:)

PDT が入ったら、ウインドウ->パースペクティブを開く->その他から「PHP」を選択してください。 PHP の作業する上で便利な画面構成になります。 Eclipse では、画面上の各ペインをビューとよび、ビューの配置状況をパースペクティブとなり、いくつも記憶することができます。 PHP パースペクティブは、事前に PHP に便利なビューを集めたものになります。

PDT に PHP プロジェクトを追加する

Eclipse では、製作しているアプリケーションのファイル群をプロジェクトという単位で扱います。 ここでは、既に制作中の WordPress サイトがあるものとして、それを PDT のプロジェクトにする方法を紹介します。 この使い方をすることで、必要な時だけ Eclipse でファイルを操作することができます。

まず、PHP パースペクティブ左に配置された PHP Explorer ビューを右クリックして、New -> プロジェクトを選択します。

プロジェクト作成ウイザードが現れますので、PHP で検索して「PHP Project」を選択します。

ここからがキモです。

Project name: に任意の名前をつけて、Contents を「Create project at existing location (from existing source)」として「Browse…」で WordPress の置かれたフォルダを指定してあげます。 既にあるソースからプロジェクト作成、ですね。

おめでとうございます。制作中のソースがプロジェクトになりました。  🙂

Eclipse は賢いので起動しながら別のテキストエディタなどでファイルをいじっても、適切に追従してきます。

PHP Explorer ビューからは編集するファイルを選択ができ、問題ビューにはエラー表示がでます。

各ビューに配置されたボタンをいろいろいじってみるとだいたい分かると思います。また、PHP Explorer のファイル右クリックから様々なファイル操作ができます。ちょっと古いですが以下も見てみてください。各種、使い方を書いています。

Eclipse PDT + XAMPP で WordPress の開発環境をつくる (3)

ここからは通常のテキストエディタにない統合環境特有の編集機能をみていきます。 難しい操作はありませんので、ぜひ手になじませてください。

ソースの中へ

インストール後ぜひ、ご自分のつくったテンプレートファイルで使われているテンプレートタグ(PHP の関数)の上で、F3 を押してみてください。(Mac 標準や最近の PC ではファンクションキーがマルチ側にあたっている場合があるので、その場合は Fn + F3) 定義部分にとべます。

これを使うことでプログラムの中を自由にどんどん追っていけるのが分かると思います。

WordPress などの CMS のテーマファイルをつくる場合は、コアのプログラムがよめると圧倒的に有利で不具合解決も早いです。IDE は定義ジャンプや複雑な条件がつけられる検索機能などが、これらの手助けをしてくれます。

さて、WordPress でなにかの実現の仕方が分からなかった場合、さらっと似たようなテンプレートタグのソースをみてみましょう。操作はそのテンプレートタグの上で F3 を押すだけ。きっと答えはそこにあります。コアたってただの PHP。そして何も難しいことかいてません。 😀

というわけで、このブログで週に一回「WordPress 徹底解析」シリーズ開始します。WordPress や PHP の考え方を知り、何にでも対応できるようにできるようになりましょうぞ。

まずは、Eclipse でも NetBeans でも Sublime Text2 の ctag でもソースの中を「飛べる」ようにしておいていただければ。 必要なのです。 🙂

Happy Hacking!