• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏幻天博客吧
  • 欢迎来到铃音的私人博客! QQ群

[教程]WordPress的Hook機制與原理

兴趣 天空の铃音 5年前 (2014-04-19) 891次浏览 已收录 0个评论 扫描二维码
文章目录[隐藏]

简介

稍有接觸過 WordPress 佈景或外掛客製修改的朋友,對 WordPress 的 Hook 機制應該不陌生,但通常剛接觸 WordPress Hook 的新手,對其運作原理可能會有點混亂或糢糊。本文針對 WordPress Hook 運作大致做個簡單的說明,而預設讀者是理解基本的 PHP function 語法及運作,但對 WordPress Hook 機制不是很明白。

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:

/*--------------- 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:

/*--------------- 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:

// 列出所有的 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;
}

 
當我們呼叫 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 的設定,導致結果跟預期不符合。


转载请注明 - [教程]WordPress 的 Hook 機制與原理 - 幻想天空 - 魔王殿 - 铃音の小屋
喜欢 (0)
[不公开]
分享 (0)
关于作者:
美国服务器正在努力恢复中!目前系统升级完善,功能可以使用!现在来申请免费空间无需填表哟!
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址