实例说明WordPress Action Hoot和Filter Hook的区别及使用函数add_filter()、add_filter()

WordPress教程 323

前面理解了 WordPress 钩子 Hoot 的概念和动作机制原理之后,接下来就是要知道怎么利用 Hoot 来实现自己需要的功能。WordPress 的 Hook 有两种,分别是「Action Hook」及「Filter Hook」,分别对应函数 add_filter()add_filter()。前文举例的 wp_head 及 wp_footer 都是属于 Action Hook,事实上两者可以看成是一样的东西,只是 Filter 多了一点点不同的特色。

Action Hook

WordPress 核心、主题或插件在做它们该做的事时,如果执行到埋有 action hook 的程式码 (即是 do_action 语法) 时,会去找寻对应到的 hook functions,进而执行这些 hook functions(即那些透过 add_action() 来加入的 hook functions),藉此完成定制功能。

WordPress 核心并不期待 Action Hook functions 会有回传值,所以这里的 hook function 只被视为一个”独立切出来运作的功能“。

WordPress 核心做它该做的事,你做你想做的事,做完就各自结束。

Filter Hook

跟 Action Hook 一样,WordPress 核心、主题或插件在做它们该做的事时,如果执行到埋有 filter hook 的程式码 (即是 apply_filters 语法) 时,就会去找寻对应的 hook functions ,进而执行这些 hook functions(即那些透过 add_filter() 来加入的 hook functions ),藉此完成定制功能。与 Action Hook 不同之处是,所有「钩上」 Filter Hook 的 hook functions 通常都会接收到参数,而 WordPress 核心会期待你拿到它提供的参数,并做完你想做的事后,要回传(return)一个值,让 WordPress 核心再利用你回传的值来接着完成它该做的事。

透过你的干涉,修改了WordPress 核心丢给你的参数,WordPress 核心再接着拿你改过的参数,继续完成它该做的事,此动作就像「过滤」的动作,因而得名 filter。

比较 Action Hook 与 Filter Hook 的操作语法

比较一下两种 Hook 在埋进某处程式码时所用的语法,假设我们在某处 (可能是在输出页首的程式码处,或输出文章标题、文章内容、侧边栏等地方,要「出现定制效果」的地方)埋下这两种 hook:

1
2
3
4
5
6
/*--------------- Action Hook ---------------*/
// 埋下一个名叫'do_more'的action hook
do_action('do_more'); 
/*--------------- Filter Hook ---------------*/
// 埋下一个名叫'get_special'的filter hook,注意它会有返回值
$c = apply_filters('get_special',$a, $b);

然后我们可以在某处 (可能是其他插件、functions.php 等处,要「实现定制功能」的地方)添加对应的 hook function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*--------------- Action Hook Function---------------*/
// 增加要钩上 'do_more' 这个 hook 的 hook function,
// 并为此 hook function 取名叫 more_func。
// 第一个参数是 hook 名称、第二个是 hook function 名称
add_action('do_more', 'more_func');
// 添加 more_func 的內容,不需回传值
function more_func(){
    echo 'do more thing...';
} 
/*--------------- Filter Hook Function ---------------*/
// 增加要钩上 'get_special' hook 的 hook function,
// 并为此 hook function 取名叫 special_func。
// 参数1是 hook 名称、参数2是 hook function 名称
// 参数3是 Priority(优先序)、参数4是 hook function 参数的数目
add_filter('get_special', 'special_func', 10, 2);
// 添加 special_func 的內容,需要給它回传值
function special_func($a, $b){
    $c = $a.' & '.$b; //做一些事,例如把两个参数连接起來
    return $c; //回传值
}

所以其实两种 Hook 的运作方式几乎一样,区别在于增加 Action Hook 函式不需回传值,而增加 Filter Hook function 时,则必须要回传一个值。所以 Filter Hook 函式通常都有提供参数,让想定制的人可以取得它,处理后再回传。

但如果有一个 Filter Hook,它没有任何 hook function 有去钩它,它该怎么取得回传值?答案是,直接拿第一个它给的参数,以上面的例子来说,它会直接拿 $a 丢进 $c 。另外,其实我们也可以把 filter 写得跟 action 一样,只要不回传值就行,但 action hook 就没办法「模仿」 filter hook,因为无法取得回传值。

Hook Function 的优先序(Priority)

如果有很多地方(插件或者主题 functions.php)都 add 同一个 hook ,会怎么决定出现顺序?答案很显然是可以通过 Hook Function 的 Priority 参数来作优先序的设定。

就像刚才说明的例子中,使用 add_filter 加入 special_func 时设定的优先序是 10 ,这也是 Priority 参数的预设值。如果希望它能优先被执行,就设定小于 10 的数字,反之,就设个 100、500 之类的,让它延后被执行。

但其实这里有个隐含的冲突问题。

以 wp_head 这个 hook 为例,如果我写了一个插件,希望通过 wp_head 来输出「增加 a.css 文件」的 HTML 语法,而 a.css 会重新设定 body 元素的样式,所以我希望它可以最后才被汇入,不要被其他 css 文件干扰,于是我将 Priority 设为 900,但我怎么知道 Priority 900 够不够大?若某个用 WordPress 搭建的网站,它除了安装我的插件,也安装了其他插件,而其他插件刚好也重新设定body元素的样式,然后把 Priority 设为 950,此时我写的插件在处理 body 样式时就出事了,于是就跟其他插件冲突了。

所以此时我们需要了解的是:我的 WordPress 网站可能装了很多插件,我怎么知道同一个 Hook 被加了多少 Hook Function,而每个 Hook Function 的 Priority 被设定为多少?

答案是,我们可以通过 $wp_filters 这个 global 变量来取得所有 hook 的资讯,像是如下的 function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 列出所有的hook function及其priority
function list_hooked_functions($tag=false){
    global $wp_filter;     
    if ($tag){
        $hook[$tag] = $wp_filter[$tag];
        if (!is_array($hook[$tag])){
            trigger_error("Nothing found for '$tag' hook", E_USER_WARNING);
            return;
        }
    }else{
        $hook = $wp_filter;
        ksort($hook);
    } 
    foreach($hook as $tag => $priority){
        echo ">>>>>\t<strong>$tag</strong><br />";
        ksort($priority);
        foreach($priority as $priority => $function){
            echo $priority;
            foreach($function as $name => $properties) echo "\t$name<br />";
        }
    }
    return;
}

当我们呼叫 list_hooked_functions('wp_head'); 时,就会列出 wp_head 这个 Hook 所钩住的所有 hook function,如下:

>>>>> wp_head

1 wp_enqueue_scripts

2 feed_links

3 feed_links_extra

8 wp_print_styles

9 wp_print_head_scripts

10 rsd_link

wlwmanifest_link

index_rel_link

parent_post_rel_link

start_post_rel_link

adjacent_posts_rel_link_wp_head

locale_stylesheet

wp_generator

rel_canonical

wp_shortlink_wp_head

print_sth

wp_admin_bar_header

_admin_bar_bump_cb

可以看到 priority 10 之后有好几个都没有数字,因为它们都没有特别指定 priority,所以都是 10 ,包括我们刚才写的 print_sth 也在其中。

所以,冲突很难提早避免,但发生冲突时,可以预先思考有没有可能是因为 priority 的设定,导致结果跟预期不符合。

内容由博客吧整理,原文地址:https://audilu.com/2011/10/10/wordpress-hook/

精品推荐: