稍有接觸過 WordPress 主題或插件制作修改的朋友,對 WordPress 的Hook機(jī)制應(yīng)該不陌生,但通常剛接觸WordPress Hook 的新手,對其運作原理可能會有點混亂或模糊。本文針對 WordPress Hook 運作大致做個簡單的說明,而預(yù)設(shè)讀者是理解基本的 PHP function 語法及運作,但對 WordPress Hook 機(jī)制不是很明白。

Hook機(jī)制里登場的角色
先從“登場角色”的個別說明開始:
WordPress核心
指的是 WordPress 內(nèi)建的程式碼架構(gòu),提供 WordPress 主要的基本功能。
Hook
也許你早已聽說,Hook 本身雖是鉤子的意思,但直譯又有點奇怪,所以一般通常都不直譯它,而是直接稱它 Hook 。WordPress 的 Hook 也可以想像成“鉤子”,這些“鉤子”會埋在 WordPress 網(wǎng)站中特定幾處的程式碼中,埋進(jìn)去時使用的語法,其“標(biāo)示位置”的意義比較大,沒有實質(zhì)運作的內(nèi)容。當(dāng)程式執(zhí)行到有埋 Hook 的地方時,它會找出所有對應(yīng)到自己的 Hook Function (也就是所有“鉤到”該 Hook 的 hook function),并一一執(zhí)行。
因此若沒有針對此 Hook 去“加入”要鉤上去的 Hook Function,執(zhí)行到此什么也不會做。因此,它等于是WordPress核心預(yù)留一個執(zhí)行的機(jī)會給未來想要加入定制功能的開發(fā)者。
Hook Function
Hook Function 里會有實質(zhì)運作的內(nèi)容,即是實作了一些定制功能,可能是存取 DB、增加 HTML code、執(zhí)行其他函式等等。我們在 Hook Function 里寫好所需的功能后,就可以利用“加入至對應(yīng) Hook”的語法,把 Hook Function自已鉤到該 Hook 上,使得該 Hook 被執(zhí)行到時,也會連帶執(zhí)行自己。
Hook機(jī)制是如何運作的?
舉個例子,我們拿 wp_head 及 wp_footer 這兩個內(nèi)建的hook來說明,wp_head 這個 hook 就是用來埋在負(fù)責(zé)輸出標(biāo)簽的程式碼中,而 wp_footer 就是用來埋在輸出頁尾的程式碼中 (定義于 wp-includes/general-template.php,用 wp_head() 及 wp_footer() 包裝起來)。這兩個 hook,主要都是在布景檔案中使用的,常見會出現(xiàn)在 header.php 及 footer.php 中。
請看下面的情境示例圖,我們把 wp_head 及 wp_footer 看成是”鉤子“,而別的 hook functions 就能來鈎住它:

我們馬上來寫一個簡單的例子。我們要寫一個 hook function,就叫它 print_sth(),然後把它鉤上 wp_head 這個hook。因為 wp_head() 的內(nèi)容實際上就只有 do_action( ‘wp_head’ ); 這一行內(nèi)容,而 wp_footer() 的內(nèi)容也只有 do_action(‘wp_footer’);所以我們直接把 do_action 的語法換到圖上去,比較容易做說明,因此示意圖變成:

如此,只要執(zhí)行到輸出 header.php 時,就會執(zhí)行到 wp_head(),就如同執(zhí)行到 do_action( ‘wp_head’ ); 此時WP核心會去找所有”鉤上”wp_head 這個 hook 的 hook function,於是就找到我們寫的 print_sth() ,然後就執(zhí)行它,所以結(jié)果它做的事就會出現(xiàn)在網(wǎng)站上,也完成了”定制”的動作:

簡單的說,Hook 機(jī)制就是:WP 核心或其他插件、主題 提供想定制功能的人一個置入定制程式碼(Hook Function)到特定的執(zhí)行時間點(Hook)的機(jī)會。
WordPress的Action Hook與Filter Hook
WordPress中的 Hook 有兩種,分別是”Action Hook“及”Filter Hook“,我們剛才舉例的 wp_head 及wp_footer 都是屬於 Action Hook。不過,一開始你可以先把這兩種 Hook 看成是一樣的東西,只是 Filter 多了一點點不同的特色,接著說明。
Action Hook
WP核心 (或主題、插件)在做它們該做的事時,如果執(zhí)行到有埋 action hook 的程式碼 (即是 do_action 語法) 時,會去找尋對應(yīng)到的 hook functions,進(jìn)而執(zhí)行這些 hook functions(即那些透過 add_action() 來加入的 hook functions),藉此完成定制功能。WP核心并不期待 Action Hook functions 會有回傳值,所以這里的 hook function 只被視為一個”獨立切出來運作的功能“。
WP核心做它該做的事,你做你想做的事,做完就各自結(jié)束。
Filter Hook
跟 Action Hook 一樣,WP核心 (或主題、插件)在做它們該做的事時,如果執(zhí)行到有埋 filter hook 的程式碼 (即是apply_filters語法) 時,就會去找尋對應(yīng)的 hook functions ,進(jìn)而執(zhí)行這些 hook functions(即那些透過add_filter() 來加入的 hook functions ),藉此完成定制功能。與 Action Hook 不同之處是,所有”鈎上“ Filter Hook 的 hook functions 通常都會接收到參數(shù),而WP核心會期待你拿到它提供的參數(shù),并做完你想做的事后,要回傳(return)一個值,讓W(xué)P核心再利用你回傳的值來接著完成它該做的事。
透過你的干涉,修改了WP核心丟給你的參數(shù),WP核心再接著拿你改過的參數(shù),繼續(xù)完成它該做的事,此動作就像”過濾“的動作,因而得名 filter。
比較Action Hook與Filter Hook的操作語法
比較一下兩種 Hook 在埋進(jìn)某處程式碼時所用的語法,假設(shè)我們在某處 (可能是在輸出頁首的程式碼處,或輸出文章標(biāo)題、文章內(nèi)容、側(cè)邊欄…等地方,要”出現(xiàn)定制效果“的地方)埋下這兩種 hook:
/*--------------- 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 等處,要”實現(xiàn)定制功能“的地方)添加對應(yīng)的 hook function:
/*--------------- Action Hook Function---------------*/
// 增加要鉤上'do_more'這個hook的hook function,
// 并為此hook function取名叫more_func。
// 第一個參數(shù)是hook名稱、第二個是hook function名稱
add_action('do_more', 'more_func');
// 添加more_func的內(nèi)容,不需回傳值
function more_func()
{
echo 'do more thing...';
}
/*--------------- Filter Hook Function ---------------*/
// 增加要鉤上'get_special' hook的hook function,
//并為此hook function取名叫special_func。
// 參數(shù)1是hook名稱、參數(shù)2是hook function名稱
// 參數(shù)3是Priority(優(yōu)先序)、參數(shù)4是hook function參數(shù)的數(shù)目
add_filter('get_special', 'special_func', 10, 2);
// 添加special_func的內(nèi)容,需要給它回傳值
function special_func($a, $b)
{
$c = $a.' & '.$b; //做一些事,例如把兩個參數(shù)連接起來
return $c; //回傳值
}
所以其實兩種 Hook 的運作方式幾乎一樣,只差在增加 Action Hook 函式不需回傳值,而增加 Filter Hook function時,你必須要回傳一個值。所以 Filter Hook 函式通常都有提供參數(shù),讓想定制的人可以取得它,處理后再回傳。
但如果有一個 Filter Hook,它沒有任何 hook function 有去鉤它,它該怎么取得回傳值?答案是,直接拿第一個它給的參數(shù),以上面的例子來說,它會直接拿 $a 丟進(jìn) $c 。另外,其實我們也可以把 filter 寫的跟 action 一樣,只要不回傳值就行,但 action hook 就沒辦法”模仿“ filter hook,因為無法取得回傳值。
Hook Function的優(yōu)先序(Priority)
如果有很多地方(主題或者插件的 functions.php)都 添加同一個 hook ,會怎么決定出現(xiàn)順序?答案很顯然是可以透過 Hook Function 的 Priority 參數(shù)來作優(yōu)先序的設(shè)定:
就像我們剛才說明的例子中,我們使用 add_filter 加入 special_func 時設(shè)定的優(yōu)先序是 10 ,這也是 Priority 參數(shù)的預(yù)設(shè)值。如果你希望它能優(yōu)先被執(zhí)行,就設(shè)定小于 10 的數(shù)字,反之,就設(shè)個 100、500 之類的,讓它延后被執(zhí)行。
但其實這里有個隱含的沖突問題。
以 wp_head 這個 hook 為例,如果我寫了一個插件,希望透過 wp_head 來輸出”增加 a.css 檔案“的 HTML 語法,而 a.css 會重新設(shè)定 body 元素的樣式,所以我希望它可以最后才被匯入,不要被其他 css 檔干擾,于是我將 Priority 設(shè)為 900,但我怎么知道 Priority 900 夠不夠大?若某個WP網(wǎng)站,它除了安裝我的插件,也安裝了其他插件,而其他插件剛好也重新設(shè)定body元素的樣式,然后把 Priority 設(shè)為 950,此時我寫的插件在處理 body 樣式時就出事了,于是就跟其他插件沖突了。
所以此時我們需要了解的是:我的WP網(wǎng)站可能裝了很多插件,我怎么知道同一個 Hook 被加了多少 Hook Function,而每個 Hook Function 的 Priority 被設(shè)定為多少?
答案是,我們可以透過 $wp_filters 這個 global 變數(shù)來取得所有 hook 的信息,像是如下的 function:
// 列出所有的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);
}
echo '<pre>';
foreach($hook as $tag => $priority)
{
echo "<br />>>>>>\t<strong>$tag</strong><br />";
ksort($priority);
foreach($priority as $priority => $function)
{
echo $priority;
foreach($function as $name => $properties) echo "\t$name<br />";
}
}
echo '</pre>';
return;
}
當(dāng)我們呼叫 list_hooked_functions(‘wp_head’); 時,就會列出 wp_head 這個 Hook 所鉤住的所有 hook function,可以看到 priority 10 之后有好幾個都沒有數(shù)字,因為它們都沒有特別指定 priority,所以都是 10 ,包括我們剛才寫的 print_sth 也在其中:
>>>>> 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
所以,沖突很難提早避免,但發(fā)生沖突時,可以預(yù)先思考有沒有可能是因為 priority 的設(shè)定,導(dǎo)致結(jié)果跟預(yù)期不符合。
原文出自:http://www.mrmu.com.tw/2011/10/10/wordpress-hook/ ,倡萌整編。





很受用!整整一天看了這么多hook的文章,終于在這里看懂了!
干貨啊,wp最核心的原理!
這是wordpress的精髓所在
樓主高手啊
謝謝你!非常受用,終于明白了!
代碼,25行:function special($a, $b)應(yīng)該是function special_func($a, $b)吧?
感謝糾正,已更新
謝謝,在您的網(wǎng)站逛了一下午,獲益不少。
樓主很用心整理啊,終于遇到一個國內(nèi)的wp高手了,以后常來交流!
高手算不上,還在學(xué)習(xí)中。歡迎常來交流
學(xué)習(xí)了,還能坐沙發(fā)真好