WordPress 的 Hook 机制与原理

WordPress核心

指的是WordPress內建的程式碼架構,提供WordPress主要的基本功能。

Hook

也許你早已聽說,Hook本身雖是鈎子的意思,但直譯又有點奇怪,所以一般通常都不直譯它,而是直接稱它Hook。WordPress的Hook也可以想像成「鈎子」,這些「鈎子」會埋在WordPress網站中特定幾處的程式碼中,埋進去時使用的語法,其「標示位置」的意義比較大,沒有實質運作的內容。當程式執行到有埋Hook的地方時,它會找出所有對應到自己的Hook Function (也就是所有「鈎到」該Hook的hook function),並一一執行。

因此若沒有針對此Hook去「加入」要鈎上去的Hook Function,執行到此什麼也不會做。因此,它等於是WordPress核心預留一個執行的機會給未來想要加入客製功能的開發者。

Hook Function

Hook Function裡會有實質運作的內容,即是實作了一些客製功能,可能是存取DB、增加HTML code、執行其他函式…等。我們在Hook Function裡寫好所需的功能後,就可以利用「加入至對應Hook」的語法,把Hook Function自已鈎到該Hook上,使得該Hook被執行到時,也會連帶執行自己。

Hook機制是如何運作的?

舉個例子,我們拿wp_head及wp_footer這兩個內建的hook來說明,wp_head這個hook就是用來埋在負責輸出標籤的程式碼中,而wp_footer就是用來埋在輸出頁尾的程式碼中 (定義於wp-includes/general-template.php,用wp_head()及wp_footer()包裝起來)。這兩個hook,主要都是在佈景檔案中使用的,常見會出現在header.php及footer.php中。

請看下面的情境示例圖,我們把wp_head及wp_footer看成是「鈎子」,而別的hook functions就能來鈎住它:

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

如此,只要執行到輸出header.php時,就會執行到wp_head(),就如同執行到do_action(‘wp_head’),此時WP核心會去找所有「鈎上」wp_head這個hook的hook function,於是就找到我們寫的print_sth(),然後就執行它,所以結果它做的事就會出現在網站上,也完成了「客製」的動作:

簡單的說,Hook機制就是:WP核心或其他plugin、theme提供想客製功能的人一個置入客製程式碼(Hook Function)到特定的執行時間點(Hook)的機會。

WordPress的Action Hook與Filter Hook

WordPress中的Hook有兩種,分別是「Action Hook」及「Filter Hook」,我們剛才舉例的wp_head及wp_footer都是屬於Action Hook。不過,一開始你可以先把這兩種Hook看成是一樣的東西,只是Filter多了一點點不同的特色,接著說明。

Action Hook

WP核心 (或佈景、外掛)在做它們該做的事時,如果執行到有埋action hook的程式碼 (即是do_action語法) 時,會去找尋對應到的hook functions,進而執行這些hook functions(即那些透過add_action()來加入的hook functions),藉此完成客製功能。WP核心並不期待Action Hook functions會有回傳值,所以這裡的hook function只被視為一個「獨立切出來運作的功能」。

WP核心做它該做的事,你做你想做的事,做完就各自結束。

Filter Hook

跟Action Hook一樣,WP核心 (或佈景、外掛)在做它們該做的事時,如果執行到有埋filter hook的程式碼 (即是apply_filters語法) 時,就會去找尋對應的hook functions,進而執行這些hook functions(即那些透過add_filter()來加入的hook functions),藉此完成客製功能。與Action Hook不同之處是,所有「鈎上」Filter Hook的hook functions通常都會接收到參數,而WP核心會期待你拿到它提供的參數,並做完你想做的事後,要回傳(return)一個值,讓WP核心再利用你回傳的值來接著完成它該做的事。

透過你的干涉,修改了WP核心丟給你的參數,WP核心再接著拿你改過的參數,繼續完成它該做的事,此動作就像「過濾」的動作,因而得名filter。

比較Action Hook與Filter Hook的實作語法

比較一下兩種Hook在埋進某處程式碼時所用的語法,假設我們在某處 (可能是在輸出頁首的程式碼處,或輸出文章標題、文章內容、側邊欄…等地方,要「出現客製效果」的地方)埋下這兩種hook:

?
1
2
3
4
5
6
7
/*--------------- 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
21
22
23
/*--------------- 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( $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)

如果有很多地方(plugin或者佈景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夠不夠大?若某個WP網站,它除了安裝我的外掛,也安裝了其他外掛,而其他外掛剛好也重新設定body元素的樣式,然後把Priority設為950,此時我寫的外掛在處理body樣式時就出事了,於是就跟其他外掛衝突了。

所以此時我們需要了解的是:我的WP網站可能裝了很多外掛,我怎麼知道同一個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
24
25
26
27
28
29
30
31
32
33
34
// 列出所有的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 />&gt;&gt;&gt;&gt;&gt;\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 ;
}

當我們呼叫 list_hooked_functions(‘wp_head’); 時,就會列出wp_head這個Hook所鈎住的所有hook function,可以看到priority 10之後有好幾個都沒有數字,因為它們都沒有特別指定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 

所以,衝突很難提早避免,但發生衝突時,可以預先思考有沒有可能是因為priority的設定,導致結果跟預期不符合。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值