WordPressの基本動作でご紹介した通り、WordPressは、テンプレートファイルを読み込むより前に、種々の自動処理を内部で行なっています。
一般的なフレームワークだと、この自動処理を変更するためには、コアファイル(WordPressでいえば、wp-includeの中)に手を加えるしかありません。
しかし、保守性やアップデート対応の面から、コアファイルを触るのは避けるべきです。
そこで、WordPressでは、アクションフックとフィルターフックという、カスタマイズのための手段が提供されています。
フィルターフックとは
簡単にいうと、フィルターとは、WordPressが内部で処理を行う際に、「この変数に対して何かしたい人いませんか?」と、呼びかけてくれているものです。
WordPressの基本動作でご紹介したように、WordPressは、URLにアクセスされると、URLを解析して自動的に検索条件を作り、それをデータベースに投げます。
その検索条件を作った後、データベースに投げる前に、「この検索条件($query)に何か手を入れたい人いませんか?」と聞いてくれるのです。
それに対して「ちょっと待った!」と手を挙げるのが、フィルターフックです。
具体的には、例えば functions.php に、以下のように記載します。
1 2 3 4 5 6 7 |
add_action( 'pre_get_posts', 'news_query' ); function news_query( $query ) { if ( $query->is_archive('news') ) { $query->set( 'posts_per_page', '20' ); } } |
これによって、投稿タイプnewsの一覧ページであれば、1ページあたりの投稿数を、デフォルトの10ページから、20ページに変えることができます。
(pre_get_postsの詳細についてはまた後日。)
WordPressは、これによって書き換えられた検索条件($query)をもとにSQLを作って、後続の処理を実行していきます。
テンプレート内で新たにWP_Queryオブジェクトを作る場合や、query_posts() などを使う場合と根本的に異なり、フィルターフックは、WordPressが自動で行う基本動作自体を変更していることがポイントです。
フィルターは、WordPressの自動処理の中だけでなく、テンプレートタグの中にも用意されています。
例えば、以下はおなじみの the_content() の定義です。
1 2 3 4 5 6 |
function the_content( $more_link_text = null, $strip_teaser = false) { $content = get_the_content( $more_link_text, $strip_teaser ); $content = apply_filters( 'the_content', $content ); $content = str_replace( ']]>', ']]>', $content ); echo $content; } |
apply_filters() という関数が実行されていることに注目してください。
これが、「$content に対して、何かしたい人いませんか?」と呼びかけてくれているのです。
これに対して、例えば functions.php に以下のように書きます。
1 2 3 4 5 6 7 8 |
add_filter( 'the_content','markup_url' ); function markup_url($content) { //URL文字列に自動でaタグをつける $pattern = '(https?://[-_.!~*\'()a-zA-Z0-9;/?:@&=+$,%#]+)'; $replace = '<a href="\1">\1</a>'; $content = mb_ereg_replace($pattern, $replace, $content); } |
ポイントは、add_filter() です。
上記の呼びかけに対して、「ちょっと待った! markup_url() をやらせて!」と手を挙げるための関数です。
これによって、テンプレートタグに対しても、デフォルトの仕様に横槍を入れることができます。
※注意
フィルターフックは、そのフィルターが呼び出される全てのタイミングで実行されるため、必要な時だけ処理が行われるように、細かく条件を書く必要があります。
例えば、先のpre_get_postsフィルターは、管理画面の表示時や、get_postsで投稿を取得する際にも呼び出されるため、上記のコードでは、管理画面内のnewsの一覧ページでも、サイドバーでget_postsを使って新着ニュースを表示させる時にも、20件になってしまいます。
アクションフックとは
フィルターは「これ(変数)に対して何かしたい人いませんか?」と呼びかけるのに対して、アクションは、「いま(このタイミングで)何かしたい人いませんか?」と呼びかけてくれるものです。
WordPressは、数々の自動処理を行っていく中で、都度、例えば「テーマの読み込み終わったよ。いま何かしたい人いない?」というように呼びかけてくれています。
具体的には、WordPressのコアのプログラムの中に、
1 |
do_action('init'); |
のような記述がいたるところにあります。
それに対して「ちょっと待った!この処理させて!」と横槍を入れるのが、アクションフックです。
同じく functions.php 等に、
1 |
add_action('init', 'register_my_post_type') |
のように記述することで、その処理が行われるタイミングで、任意の処理を実行することができます。
WordPressの基本動作(あるいは変数)とは無関係に、独自の処理を差し込みたい場合に活用できます。
フィルターフックとアクションフックの使用方法
テンプレートファイルが読み込まれる時には、既に多くのフィルターやアクションが実行済みなので、
フィルターフックやアクションフックは、WordPressの処理の初めの方で読み込まれるファイルに記述する必要があります。
具体的には、functions.php か、プラグインファイルです。
WordPressのプラグインは(ほぼ)全て、このアクションフックやフィルターフックを利用して実装されています。
アクションフックやフィルターフックの機能が「プラグインAPI」と呼ばれている所以です。
余談1:pre_get_posts はアクションフックか、フィルターフックか
フィルターフックの解説の中で、pre_get_posts がフィルターの代表格であるような言い方をしました。
しかし、厳密には、pre_get_posts はアクションです。
サンプルコードをよく見ると、add_action(‘pre_get_posts’) となっていますよね。
フィルターは変数に対して処理を加えるもの。
アクションはそのタイミングに処理を挟むもの。
この理解に照らせば、$query に処理を行う pre_get_posts はフィルターフックに近いですが、
WP_Queryクラスの定義を見ると、
add_filters(‘pre_get_posts’, $query)
ではなく、
do_action_ref_array( ‘pre_get_posts’, array( &$this ) );
となっています。アクションにも引数を渡せるようになっているようです。なんじゃそりゃ。
恐らく、WordPressにまだフィルターフックという概念がない頃(アクションフックしかない頃)に、
この関数が作られ、WP_Queryクラスに使用されて、
フィルターフックが開発された後も、互換性のために変えるに変えられず、残っているという、歴史的な経緯ではないかと思います(想像です)。
なので、pre_get_posts は「実質的にはフィルター(フック)」と言って良いと思います(異論は認めます)。
余談2:WordPressの設計思想
フィルターフックもアクションフックも、WordPressをカスタマイズする我々が、コアファイルに手を加えずとも自在にカスタマイズできるように、WordPressの開発者たちが発明してくれた仕組みです。
ただでさえ高機能なCMSなのに、カスタマイズ側への配慮の高さには、敬意を表さざるを得ません。
この仕組みを実現しているのは、do_action() や apply_filter() など、普通はwp-includeの中でしかお目にかからない関数なのですが、
これをfunctions.php やプラグインファイルに書いておけば、独自のアクションやフィルターを追加でき、当然それに対してアクションフックやフィルターフックを行うことができます。
プラグインの中でも、気の利いたプラグインは、アクションやフィルターを絶妙な場所に用意してくれているので、その動作を、さらにカスタマイズすることができます(プラグインの「アドオン」は、これを利用した仕組みです)。