WordPressのapply_filters、do_action、add_filter、add_actionの使い方とか違いとか

WordPressでHook(Filter)を使う際に、以下の4つの関数が用意されている。

  • apply_filters
  • do-action
  • add_filter
  • add_action

今回は、これら4つの関数の使い方や違いを紹介していく。

Hookとは何か?

そもそも、add_filterとかで使われているHookとは何なのか?

Hookとは、フレームワークやライブラリ、WordPress本体(テーマではなく、wp-includesとか)のソースコードを直接いじることなく、コードを改造できるようにした仕組みの事を言う。

例えば、WordPressでは投稿記事を表示するのにthe_content()(WordPress/post-template.php at master · WordPress/WordPress)の関数を使う。

もし、the_content()の挙動を変えたいと思った時に直接the_content()のコードをいじると、後で「やっぱり元に戻したい」と思ったり、他のプログラマがコードをいじるようになった場合に、すごく扱いにくいコードになってしまう。

そこでadd_filter('the_content', "hoo_func')のようにしてhoo_funcをHookに加えることによって、the_content()を直接いじることなく挙動を変更できる、というメリットがある。

add_actionとadd_filterの使い方とか違い

まずHookを追加するのに使うadd_actionadd_filterだが、これらの関数は全く同じ関数となっている。

特定のアクションに関数をフックします。 この関数は add_filter() のエイリアスです。 アクションフックの一覧についてはアクションフック一覧をご覧ください。アクションは(通常は)WordPress コアが do_action() を呼び出すときにトリガーされます。 関数リファレンス/add action - WordPress Codex 日本語版

実際にadd_actionのコードを見ていると、単にadd_filterをしているだけだと分かる。

<?php

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

参考:WordPress/plugin.php at master · WordPress/WordPress

で、add_filterはなにをしているかと言うと、以下のようなコードになっている。

<?php

function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
    global $wp_filter;
    if ( ! isset( $wp_filter[ $tag ] ) ) {
        $wp_filter[ $tag ] = new WP_Hook();
    }
    $wp_filter[ $tag ]->add_filter( $tag, $function_to_add, $priority, $accepted_args );
    return true;
}

上記のコードを見てわかるように、add_filterではグローバル変数のwp_filtersを使っている。このwp_filtersは連想配列となっており、キーにはadd_filter( $tag, $foo_func, )の第1引数$tagが使われて、Hookを識別するHook名の役割を持つ。

そして、new WP_Hook();でWP_Hookクラスを初期化したものを配列のvalueとして扱う。

参考:WordPress/plugin.php at master · WordPress/WordPress

<?php

    public function add_filter( $tag, $function_to_add, $priority, $accepted_args ) {
        $idx              = _wp_filter_build_unique_id( $tag, $function_to_add, $priority );
        $priority_existed = isset( $this->callbacks[ $priority ] );
        $this->callbacks[ $priority ][ $idx ] = array(
            'function'      => $function_to_add,
            'accepted_args' => $accepted_args,
        );
        // if we're adding a new priority to the list, put them back in sorted order
        if ( ! $priority_existed && count( $this->callbacks ) > 1 ) {
            ksort( $this->callbacks, SORT_NUMERIC );
        }
        if ( $this->nesting_level > 0 ) {
            $this->resort_active_iterations( $priority, $priority_existed );
        }
    }

参考:WordPress/class-wp-hook.php at master · WordPress/WordPress