WordPressのパーマリンクとRewriteAPIの取り扱い方

WordPressにおけるサイト内コンテンツのURLをパーマリンクと呼ぶのだが、デフォルトのパーマリンク設定以外に変更した際に時々遭遇するのが、パーマリンクが有効にならずにリンクできなくなったり、リンク先のコンテンツが404になったりする症状だ。

私は今、プラグインを開発しているのだが、昨日その症状に陥っていることに気づいた(今までデフォルトのパーマリンク設定でデバッグしていたので気づかなかったのだ)。
だいたい、そういう時はApacheのmod_rewriteが有効化されていなかったり、.htaccessの記述やパーミッション不良とかが原因なのだが、今回はつい先日までリライト型のパーマリンクで動いていたので、先日プラグインに追加したRewriteRuleが原因だろうと思って調べてみた。
開発中のプラグインを無効化して、パーマリンク設定を初期化(設定を変えずに「保存」だけ行う)してみたところ、パーマリンクが正常化したのでやはりビンゴだ。

──そうなると、大抵はプラグインのどこかでRewriteRuleを無用に初期化するflush_rewrite_rules()を呼んでいるハズなので、ソースを追ってみたところ、不具合の原因は下記の部分だった。

このソースのまずいところはフックポイントのwp_loadedだ。
WordPress Codexには、下記のように書いてあったので、initフックではないところにフックしたのだが、これが罠だった(笑)。

flush_rules関数はとても遅いので、実装するときにはすべてのページで実行されるinitフィルターで呼び出さないようにしてください。そのかわり、この関数はリライトルールが変更されるときにだけ呼び出すようにしてください。プラグインのregister_activation_hookだけで消去すれば十分でしょう。
WordPress Codex : 関数リファレンス/WP Rewrite

コアのソースを見てみると、wp_loadedのフックはinitフックの後続フックであるのだが、RewriteRuleの追加・変更はinit内のadd_permastruct()で行われている。なので、その後続フックでflush_rules()等でリライト状況を初期化してしまうと、せっかく変更されたリライトルールが打ち消されてしまい、正常なリライトが行われなくなってしまうという次第だ。

──と云うわけで、flush_rewrite_rules()initフックで行うのが正しいということになる。確かにWordPress Codexのサンプルソースでもそうなっているのだが、日本語の補足文が誤解を生みやすいんだよなぁ…。

まぁ、これで私のプラグインの不具合は解消したわけだが、せっかくWordPressのRewriteAPIについて色々とクセがわかったので、この際、リライト周りの処理の適正化な流れを備忘録としてまとめておこうかと思う。

プラグインでのRewriteAPIの取り扱い方・まとめ

1. 独自のリライトルールの追加はプラグイン有効化時に行う

原則的にリライトルールはプラグインの有効化時に一度だけ行っておくのが良い。プラグイン側で動的にリライトルールを生成するとかでない限り、定型的なリライトルールを定義しておいて、それをプラグインを有効化した時に追加するというのがパフォーマンス的にも有用だ。
実際には、register_activation_hook()でプラグインが有効化した時に処理を呼び出す。

──こんな感じだ。もし、Class内のメソッドで処理する場合は、下記のようになる。

2. プラグインが停止された時にはリライトルールを削除する

プラグイン開発時に一番気をつけたいのが、「立つ鳥跡を濁さず」の作法だ。プラグインが停止もしくはアンインストールされた時に、できる限りゴミを残さないようにしておくのは大切だと思う(予期せぬバグや不正終了などによって発生してしまうゴミはどうしようもないところだが、それ以外の制御できるリソースについては綺麗にクリーンアップするように心がけたいものだ)。
拡張したリライトルールも、初期化しないで残っていると、後々サイトの誤動作やセキュリティホールに繋がったりするので、プラグインが停止された時、もしくはアンインストールされる直前に初期化しておく。
これは、register_deactivation_hook()でプラグインが停止時、register_uninstall_hook()でアンインストール直前に処理を呼び出せる。

register_uninstall_hook()も同等の記述でOKだ。なお、Class内で呼び出す場合は、前項の記述が参考になるかと。

$my_plugin_disabled = true; のグローバル変数は、後述の 3. の処理に必要となる。Classならメンバ変数としてプラグインのステータスを管理できる変数を用意しておき、ここで書き換えると良い。

3. プラグイン利用中のリライトルール変更にも対応する

前述の 1.2. だけの対応だと、プラグイン利用中に他のプラグインによってリライトルールを変更されたり、パーマリンクを変更したりすると自分のリライトルールが失われてしまう。そこで、リライトルールが変更された場合には、改めて自分のルールを追加する処理を作っておく。

──ミソとしては、リライトルールの初期化は自分のルールが定義されていない時だけ行うというところと、リライトルールを追加する時に$my_plugin_disabledのグローバル変数を参照して、プラグインが停止状態かどうかを判定することだ。
前者は、処理コストの高いリライトルールの初期化を必要な時だけ行うことで、WordPressが常に呼び出すinitフックで利用してもパフォーマンスを低下させないようにしてある。後者は、プラグイン停止状態に突入したらリライトルールの追加を行わないようにするために必要になる。このプラグインの停止状態判定がないと、せっかくregister_deactivation_hook()でリライトルールを初期化してもその後にリライトルールが再度追加されてしまい、ゴミが残ってしまうのだ。

あと、query_varsフィルタは、リライトルールに沿ったURLからパラメータを抽出して、WP_queryオブジェクトに追加する処理だ。これによってURLを仲介して渡されたパラメータをPHP側の独自処理で簡単に利用できるようになる。

APPENDIX(おまけ)

RewriteRuleを玩(もてあそ)んでいると、現在のリライトルールがどのようになっているかが判りづらくて、実際に処理が正常に動いているのかを確認するのも面倒だ。PHP側で都度var_dump()しても良いが、結果が見づらい。
そんなときに重宝するプラグインがある。

その名も「Rewrite Rules Inspector」だ。

これをインストールしておけば、現在のリライトルールを一覧で見れるし、変更や追加、初期化もできる。リライト系の処理のデバッグ用プラグインとして最適である。

Leave a Reply

Your email address will not be published.