WordPressに独自のURLを追加する。2014年版

WordPressサイトに独自のURLを追加するのに一番簡単な方法は、add_rewrite_endpoint()関数を使うことです。

この関数を使うと、たとえばhttp://example.com/に対してhttp://example.com/events/みたいなURLを追加するには以下のようにすればオッケーです。

/*
Plugin Name: おれおれイベントカレンダー
*/

register_activation_hook(__FILE__, 'my_activation_callback');

function my_activation_callback() {
    add_rewrite_endpoint('events', EP_ROOT);
    flush_rewrite_rules();
}

上記のソースをプラグインとして有効化してhttp://example.com/events/にアクセスすると、あら不思議。

WordPressがなにを血迷ったかトップページを表示した上に、レスポンスヘッダーも404じゃなく200を返してくれちゃうようになったことがお分かりいただけます。

/events/というURLでURLが表示された!
/events/というURLでコンテンツが表示された!

あとは、query_varsフックとtemplate_redirectフックを使った以下のようなコードを追加します。

add_filter('query_vars', 'my_query_vars');

function my_query_vars($vars) {
    $vars[] = 'events';
    return $vars;
}

add_action('template_redirect', 'my_template_redirect');

function my_template_redirect() {
    global $wp_query;
    if (isset($wp_query->query['events'])) {
        // ここでイベントカレンダー的なものを出力
        echo 'いべんとからんだーだおらおらー';
        exit; // WordPressの処理を止める!
    }
}

すると以下のような感じに。

いべんとかれんだーだおらおらー
いべんとかれんだーだおらおらー

まあ、イベントカレンダーにはほど遠いですが、ここまでやれば、いろいろ想像が膨らみますよね。

実際には、テーマの固定ページのテンプレートとか自動的に読み込みてえ!とか、http://example.com/events/hogehogeみたいなURLでもリアクションしちゃうのなんとかしてえ!とか、じょじょにややっこしくなってくるんですが、今回の記事の本題はちょっと違う問題についてです。

このまんまだとflush_rewrite_rules()の発火で消えちゃう!

上の例のプラグインは一見うまく動作してるように見えますが、大きな落とし穴があって、以下のどちらかの条件によって、http://example.com/events/のURLが404を吐くようになっちゃいます。

  • 管理者によってパーマリンク設定のページが開かれたためflush_rewrite_rules()が発火した。
  • 他のプラグインによってflush_rewrite_rules()が発火した。

まあ、ためしにパーマリンク設定のページを開いてから(開くだけ)、http://example.com/events/にアクセスしちゃって下さい。

404
404

この問題は、クライアントや管理者に「さわんないでね。ぜったいさわらないでね!」っていうのもありと思うかもしれませんが、関西ではこういうのは「さわれ」という振りなので通用しません。

したがって以下のようにするわけです。

flush_rewrite_rules()が発火しても大丈夫なようにする。

以上の問題を解決するにはどうするかというと、以下のようにinitあたりでadd_rewrite_endpoint()を追加すれば解決します。

add_action('init', 'my_init');

function my_init() {
    add_rewrite_endpoint('events', EP_ROOT);
}

以上のコードを追加すれば確かにパーマリンク設定のページにアクセスしたあとでもhttp://example.com/events/にちゃんとアクセスできます。

flush_rewrite_rules()を発火しちゃう系の他のプラグインを有効化しても大丈夫。

わーい!

でも残念。まだ問題が残ります。

じつは、このままでは、そのプラグインを無効化した後も、URLの設定が残っちゃうんです。

肝心のコンテンツは、プラグインが無効化されてるのでもう出ませんが、一番最初の例のようにトップページのコンテンツが出力されちゃうんですよね。

レスポンスヘッダーも200だし。。。

これは自社のサイトで使う限りでは問題ないかもしれませんが、公式ディレクトリに登録するプラグインだと、世界各国から阿鼻叫喚が届きそうです。

そんなわけで完璧版

実はこれらの問題については、とっくの昔にわかってたので、問題を解決する方法を過去にも紹介したことがあります。

WordBench Osakaのスライド「アプリケーションプラットホームとしてのWordPress」

ところが最近になってWordPressにwp-cliというブレークスルーが訪れました。

で、こいつがコマンドラインで、flush_rewrite_rules()を発火させたり、プラグインを無効化させたりできちゃうもんですから、いままでの方法だとダメになっちゃったんですよね。

そういうわけで、完璧版。

<?php

// プラグインの有効化時/無効化時の処理を登録
register_activation_hook( __FILE__ , 'my_activation_callback' );
register_deactivation_hook( __FILE__ , 'my_deactivation_callback' );

// 他のプラグインや管理者の操作によってflush_rewrite_rules()が発火した際にこのプラグイン用のrewrite ruleを再登録する
add_action( 'delete_option', 'my_delete_option', 10, 1 );

// 有効化時の処理
function my_activation_callback() {
    /*
     * プラグインが有効化されていることをオプションに保存する
     * この時点でis_plugin_active()の戻り値はfalse
     */
    update_option( 'my_plugin_activated', true );
    flush_rewrite_rules();
}

// 無効化時の処理
function my_deactivation_callback() {
    /*
     * プラグインが無効化された!
     * この時点でis_plugin_active()の戻り値はtrue
     */
    delete_option( 'my_plugin_activated' );
    flush_rewrite_rules();
}

// delete_optionフックのコールバック関数
function my_delete_option($option){
    /*
     * flush_rewrite_rules()が発火&プラグインが有効化されている場合に限りrewrite ruleを再登録
     * register_activation_hook()発火時にはまだis_plugin_active()の戻り値はtrueのままなのでget_option()の値で評価する必要がある。
     */
    if ( 'rewrite_rules' === $option && get_option('my_plugin_activated') ) { 
        add_rewrite_endpoint( 'json', EP_ROOT );
    }
}

この方法では無効化時にもflush_rewrite_rules()を発火させるんですが、その直前のアクションフックでadd_rewrite_endpoint()を再度実行するようにしています。

でもこれ、ちょうどいいあんばいのフックがなくてdelete_optionってフックを使ってるんですけど、flush_rewrite_rulesってフックがあったらいいのにってことでTracに突撃してみようかな。