WordPress Hook 其實是筆者在 Day 4 提到的 PHP 設計模式 - 觀察者模式 (Observer)。不過只是不同的函式名稱命名,且分為兩種:action 及 filter。簡單的說,WordPress 的 Hook 可以歸納如下:
以下為詳細解說。
而整個 WordPress 的架構中,核心檔案依序載入。每到一個重點功能開始前的段落,會預留 action 這種 Hook,能讓外掛能在這個段落開始時,注入函式到這個 Hook 執行需要的程式碼。
| WordPress 函式 | 說明 | 
|---|---|
| has_action | 檢查是否有此名稱的 action | 
| add_action | 把函式註冊進 action | 
| do_action | 執行註冊在 action 裡的函式 | 
| do_actions_ref_array | 執行註冊在 action 裡的函式,參數為陣列 | 
| current_action | 取得目前的 action 名稱 | 
| remove_action | 移除註冊在指定的 action 的指定的函式。 | 
| remove_all_actions | 移除註冊在指定的 action 的所有函式。 | 
| doing_action | 檢查指定的 action 是否正在執行。 | 
例如在 WordPress 根目錄下的 wp-settings.php 這個檔案。

我們實際打開這個檔案來看看。

(圖:wp-settings.php 檔案的部分程式碼區塊截圖)
從檔案尾端就可以看到三個 action 類型的 Hook。

(圖:Action Hook 的生命週期圖表)
可以看到,載入外掛的 Hook 在很早期,在名為 plugins_loaded 這個區段,因此外掛可以預先把自己的函式注入到接下來會執行的 action 及 filter 來實現該外掛的功能。
還記得筆者在 Day 27 提到的「建立選單入口」這個段落的範例嗎?程式碼是注入到 admin_menu 這個 Hook,待程式執行到這個 Hook 時,才建立選單入口。
Filter 是 WordPress 用來過濾、修改變數的值。它的位置只會出現在函式 (function) 的區塊及類別的方法 (method) 裡。提供給外掛 (plugin) 來客製化修改核心功能輸出的能力。
| WordPress 函式 | 說明 | 
|---|---|
| has_filter | 檢查是否有此名稱的 filter | 
| add_filter | 把函式註冊進 filter | 
| apply_filters | 執行註冊在 filter 裡的函式 | 
| apply_filters_ref_array | 執行註冊在 filter 裡的函式,參數為陣列 | 
| current_filter | 取得目前的 filter 名稱 | 
| remove_filter | 移除註冊在指定的 filter 的指定的函式。 | 
| remove_all_filters | 移除註冊在指定的 filter 的所有函式。 | 
| doing_filter | 檢查指定的 filter 是否正在執行。 | 
直接舉個例子會比較容易瞭解。
/wp-includes/post-template.php 有個函式名稱為 the_content。而它的程式碼區塊如下圖。

(圖:the_content 函式的程式碼區塊截圖)
第 19 行:
$content = apply_filters( 'the_content', $content );
當執行到這一行時,會執行掛在名為 the_content 的這個 filter,如果有多支外掛同時有掛入函式要來過濾這個值,則會依序過濾,最後的值再分配給 $content 變數。
這很常見用來過濾文章內容中有不雅字詞。
function no_bad_words( $content ) {
	$content = str_replace( '白癡', '聰明', $content );
    
    return $content;
}
add_filter( 'the_content', 'no_bad_words' );
例如,以上的程式碼就會把文章中罵人的話「你真白癡」變成「你真聰明」。
光是 WordPress 官方的 Hook,總共就有二千多個(絕大多數是 filter)。除了可以在官方的開發手冊網站查詢以外,可以裝 Query Monitor 這個外掛,它是屬於開發者工具類外掛,會幫忙把所有目前在瀏覽的頁面所呼叫的 Hook 都列出來,有它的幫助,我們的開發過程會輕鬆許多。
接著明天要介紹的是,寫這支示範外掛的實作說明。鐵人賽終於剩最後二天了,我們明天見。