文本是《WordPress 插件開發(fā)教程(共4篇)》專題的第 3 篇。閱讀本文前,建議先閱讀前面的文章:
本節(jié)內容
- 為動作鉤子創(chuàng)建動作
- 為過濾器鉤子創(chuàng)建過濾器
- 使用 PHP 類里面的鉤子
- 為插件添加自定義鉤子
- 尋找 WordPress 中的鉤子
鉤子是 WordPress 的精髓。他們允許插件開發(fā)人員鉤進 WordPress 工作流程內部來改變它的工作,而不用直接修改核心代碼。這就使得用戶可以方便的升級到 WordPress 的新版本而不需要修改一行代碼。
如果一個開發(fā)人員修改了核心代碼,這些改動在 WordPress 下一次升級的時候就會消失。升級會覆蓋這些改動。使用鉤子讓你能夠在核心以外的目錄中單獨開發(fā)插件,這就在升級時保證了插件代碼的安全。
沒有鉤子,插件就沒辦法改變 WordPress 的功能了。本節(jié)介紹的鉤子系統(tǒng)會貫穿整個教程,同時也會在幾乎每一個插件的開發(fā)中用到。在你學會了鉤子的用法后,你就會明白為什么 WordPress 這個平臺這么強大,并有上千個插件供幾百萬用戶使用了。
WordPress 主要有兩種類型的鉤子:動作鉤子和過濾器鉤子(action hooks and filter hooks )。第一個使得你可以在一個特定時刻上執(zhí)行一個函數(shù),第二個使得你可以操作通過鉤子的輸出。
鉤子不是僅僅針對插件的。WordPress 內部也使用鉤子。如果你瀏覽核心代碼,你就能見到很多 WordPress 自己鉤自己的例子了。
動作鉤子 ( actions )
動作鉤子讓你可以在 WordPress 加載過程中或者當某個事件發(fā)生的特定時刻觸發(fā)一個函數(shù)。例如:你可能希望當 WordPress 第一次加載一個頁面或者保存一篇文章時執(zhí)行一個函數(shù)。
你需要理解 do_action() 函數(shù)。當鉤進 WordPress 中時,你的插件不會直接調用這個函數(shù);但是你的插件幾乎都會間接的使用它。
<?php
do_action( $tag, $arg = '' );
?>
- $tag — 動作鉤子的名稱
- $arg — 傳遞給已注冊的動作的值。它看起來像一個單獨的參數(shù),但通常都不是這樣的。動作鉤子可以傳遞任何個數(shù)的參數(shù),或者根本不傳參數(shù)。對特殊的鉤子,你需要查看 WordPress 的源碼,因為參數(shù)個數(shù)在每個鉤子的基礎改變。
下面是一個具有多參數(shù)的動作鉤子的例子:
<?php
do_action( $tag, $arg_1, $arg_2, $arg_3 );
?>
下面看看一個叫做 wp_head 的鉤子是怎么出現(xiàn)在 WordPress 中的。這個鉤子出現(xiàn)在前臺的 <head> 里面。 WordPress 和插件經常用這個鉤子來添加 meta 信息,樣式表,和 js 腳本。
<?php
do_action('wp_head');
?>
當這段代碼在 WordPress 中執(zhí)行時,它會尋找任何為 wp_head 動作鉤子注冊的動作。然后按照特殊順序執(zhí)行它們。如你所見,它名叫 wp_head 但是沒有傳遞額外的參數(shù)。動作鉤子通常都這樣。下面是有兩個額外參數(shù)的動作鉤子的例子:
<?php
do_action( 'save_post', $post_ID, $post );
?>
這個 save_post 的鉤子傳遞兩個參數(shù),一個 $post_ID,一個 $post。
什么是動作?
從技術上講,一個動作就是一個 PHP 函數(shù)。一個函數(shù)要成為一個動作,它需要被注冊成一個動作鉤子。在上面的部分可以看到什么是動作鉤子,但是動作鉤子要用作任何目的,需要有個動作為它們注冊。這就是插件的來源。你開發(fā)的自定義的函數(shù)(動作)當動作鉤子出發(fā)之后就可以執(zhí)行一個特定任務了。要實現(xiàn)這個,就要使用 add_action() 函數(shù)。
<?php
add_action( $tag, $function, $priority, $accepted_args );
?>
- $tag – 你的函數(shù)執(zhí)行時代動作鉤子的名稱。
- $function – WordPress 要調用的函數(shù)名。
- $priority – 一個表示動作調用順序的整數(shù),默認是10。數(shù)字越小,這個函數(shù)越早被調用。
- $accepted_args – 動作鉤子要傳遞給你的函數(shù)的參數(shù)個數(shù)。默認只有一個參數(shù)。
動作鉤子并不局限于單個動作。你的插件可以將多個函數(shù)添加到一個動作鉤子上。其他插件,甚至是 WordPress 核心,經常將多個函數(shù)添加到同一個鉤子上。
現(xiàn)在是使用動作鉤子的時候了。一個常見的動作鉤子是 wp_footer 。它提供給前端用戶的 WordPress 模板使用。通常它剛好在</body>前調用。在下面的例子中,將要為 wp_footer 注冊一個動作并添加一條自定義信息到 footer。
<?php
add_action( 'wp_footer', 'boj_example_footer_message', 100 );
function boj_example_footer_message() {
echo "基于 <a href="https://wordpress.org" >WordPress </a>架設。;
}
?>
仔細看看上面的代碼中如何使用 add_action() 函數(shù)的。
<?php
add_action( 'wp_footer', 'boj_example_footer_message', 100 );
?>
第一個參數(shù)是鉤子的名字( wp_footer )。第二個參數(shù)是要回調的函數(shù) ( boj_example_footer_message )。第三個參數(shù)是優(yōu)先級 ( 100 )。這個函數(shù)比起其他鉤到 wp_footer 中的函數(shù),會在比較靠后的次序執(zhí)行。如果設置成1,就較先執(zhí)行。
要說明,鉤子可能會因為許多原因在 WordPress 執(zhí)行過程中多次觸發(fā)。任何添加到這個鉤子中的動作每當鉤子觸發(fā)時都會執(zhí)行一次。
動作鉤子函數(shù)
你已經知道了最基本的兩個動作鉤子函數(shù)怎么使用;do_action() 和 add_action()。WordPress 還有其他類型的與動作鉤子相關的函數(shù)用于插件開發(fā)。
do_action_ref_array
do_action_ref_array() 函數(shù)和 do_action() 函數(shù)完成同樣的工作,只不過參數(shù)傳遞的方式不同。它并不傳遞通過附加參數(shù)的值來確定的多個參數(shù),而是傳遞一個包含參數(shù)的數(shù)組。這個參數(shù)數(shù)組也是一個必須的參數(shù)。這個函數(shù)的目的是通過引用傳遞一個對象給添加到特定鉤子的動作(函數(shù))。這意味著這個函數(shù)可以不用返回就改變對象本身。
<?php
do_action_ref_arrray( $tag, $args );
?>
- $tag – 動作鉤子的名字。
- $args – 要傳遞給注冊到這個鉤子的函數(shù)的參數(shù)的數(shù)組。通常,這是一個動作可以改變的對象。
下面看一個 WordPress 如何調用 do_action_ref_array() 的實例。下面的代碼展示了 pre_get_posts 動作鉤子。WordPress 在從數(shù)據(jù)庫取得 posts 之前執(zhí)行這個鉤子,使得插件可以改變查詢 posts 的方式。
<?php
do_action_ref_array( 'pre_get_posts', array( & $this ) );
?>
第一個參數(shù) pre_get_posts 是鉤子的名字。第二個參數(shù)是從數(shù)據(jù)庫中查詢 posts 的參數(shù)的數(shù)組。這個鉤子使你可以執(zhí)行基于那個參數(shù)數(shù)組的代碼。
假如你想安裝隨機的順序來得到首頁的 blog,而不是默認的通過發(fā)布時間來得到。你就需要注冊一個動作到這個鉤子,并改變排序順序。
<?php
add_action( 'pre_get_posts', 'boj_randomly_order_blog_posts' );
function boj_randomly_order_blog_posts( $query ) {
if( $query -> is_home && empty( $query -> query_vars['suppress_filters']))
$query -> set( 'rderby', 'rand' );
}
?>
remove_action
remove_action() 可以刪除先前添加到一個鉤子的動作。代表性的,你可以刪除 WordPress 默認添加的動作。要刪除一個動作,這個動作必須已經用 add_action() 函數(shù)添加了。如果你在動作注冊之前執(zhí)行 remove_action(),那么動作并不會被從鉤子中刪除。
如果動作被成功刪除,則函數(shù)返回 true,否則返回 false。
<?php
remove_action( $tag, $function_to_remove, $priority, $accepted_args );
?>
參數(shù)類似于 do_action()。要成功的從一個鉤子中刪除一個動作, $tag, $function_to_remove, 和 $priority 必須完全的復合 do_action() 中使用的參數(shù)。否則動作不會被溢出,同時 remove_action() 返回 flase。
我們看一個叫做 rel_canonical 的 WordPress 默認動作。這個動作在 <head> 和 </head> 元素之間添加一個 canonical 鏈接。
<?php
add_action( 'wp_head', 'rel_canonical' );
?>
要刪除這個動作,就要使用 remove_action() 函數(shù)。你需要定義 $tag 和 $function_to_rmove 參數(shù)。這里你不用添加 $priority 因為先前定義動作的時候沒有明確指定優(yōu)先級。
<?php
remove_action( 'wp_head', 'rel_canonical' );
?>
WordPress、plugin 或者 theme 添加的任何動作都可以在插件中刪除。通常只刪除 WordPress 添加的動作。許多默認的動作都定義在 wp-includes/default-filters.php 文件中。通過瀏覽這個文件你就會明白 WordPress 是如何使用動作鉤子的。
remove_all_actions
在有些插件中,可能需要刪除所有特定 tag 或者 特定 tag + 特定優(yōu)先級的所有鉤子。使用 remove_all_actions() 可以一次刪除所有符合條件的動作。
<?php
remove_all_actions( $tag, $priority );
?>
$priority 參數(shù)是可選的,默認是 false。如果你設置了這個參數(shù),那么只有這個優(yōu)先級的動作會被刪除。下面的例子從 wp_head 動作鉤子中刪除不管任何優(yōu)先級的動作。
<?php
remove_all_actions( 'wp_head' );
?>
在使用這個函數(shù)的時候必須要小心。其他 plugin 或者 theme 可能添加了你不知道動作。這就可能破壞插件應有的功能。通常應該保持你的代碼盡可能的特殊。在大多數(shù)情況下,你應該使用 remove_action() 函數(shù)來代替。
has_action
有的時候需要確定一個鉤子是否包含一些動作,或者一個特定的動作是否已經添加到了鉤子里面。has_action() 提供了這些功能。
<?php
has_action( $tag, $function_to_check );
?>
has_action() 函數(shù)的返回值是 Boolean 或者 一個整型值。如果 $function_to_check 參數(shù)為空,那么如果有動作已經添加到了鉤子中就返回 true,反之,返回 false。而如果 $function_to_check 設置了,而且這個函數(shù)已經添加到了鉤子里面,則返回該動作的優(yōu)先級,否則返回 false。
下面的例子中,根據(jù) wp_footer 動作鉤子中是否有注冊的動作來確定顯示的信息。
<?php
if( has_action( 'wp_footer' ) )
echo '<p> footer 中已經注冊有動作了。</p>';
else
echo '<p> footer 中還沒有注冊動作。</p>';
?>
下面看一個 WordPress 核心添加到 wp_footer 中的動作。wp_print_footer_script() 默認注冊給這個鉤子。
<?php
add_action( 'wp_footer', 'wp_print_footer_scripts' );
?>
did_action
did_action() 使你的插件可以檢查一個動作鉤子是否已經被執(zhí)行,或者記錄執(zhí)行的次數(shù)。這也意味著這一次頁面的加載過程中有些動作被執(zhí)行了多次。
<?php
did_action( $tag );
?>
這個參數(shù)返回動作已經執(zhí)行的次數(shù),如果還未執(zhí)行,返回 false。這個函數(shù)的一般用途是判斷一個動作鉤子是否已經被觸發(fā),并執(zhí)行基于 did_action() 的返回值的代碼。
下面的例子中,如果 plugins_loaded 動作鉤子已經被觸發(fā),就定義一個 PHP 常量。
<?php
if ( did_action( 'plugins_loaded' ) )
define( 'BOJ_MYPLUGIN_READY' ,true );
?>
register_activation_hook 和 register_deactivation_hook
在第二章中已經介紹了這兩個函數(shù)。
常用的動作鉤子
WordPress 有許多動作鉤子,有一些是很常用的。
plugins_loaded
對插件開發(fā)者來說,plugins_loaded 動作鉤子也許是最重要的動作鉤子了。它在大多數(shù) WordPress 文件加載完成之后,并在pluggable 函數(shù)和 WordPress 開始執(zhí)行任何東西之前觸發(fā)。在大多數(shù)的插件中,在這個鉤子觸發(fā)之前,不應該執(zhí)行其他的代碼。plugins_loaded 在所有用戶啟用的插件都被 WordPress 加載之后執(zhí)行。這也是在加載過程中插件開發(fā)這最早能用到的鉤子。
WordPress 的插件應該在這個鉤子中執(zhí)行安裝。其他動作也應該添加到這個鉤子的回調函數(shù)中。
下面的例子中,使用前面部分創(chuàng)建的 boj_example_footer_message 動作。要把它添加到鉤到 plugins_loaded 鉤子中的安裝動作中,而不是單獨調用它。
<?php
add_action( 'plugins_loaded', 'boj_footer_message_plugin_setup' );
function boj_footer_message_plugin_setup() {
/* 添加 footer 信息動作 */
add_action( 'wp_footer', 'boj_example_footer_message', 100 );
}
function boj_example_footer_message() {
echo "基于 <a href="https://wordpress.org" >WordPress </a>架設。;
}
?>
創(chuàng)建一個安裝函數(shù)并把它鉤到 plugins_loaded 中。這樣做就可以確保不會由于特定的 WordPress 函數(shù)還沒有加載而觸發(fā)錯誤。
init
init 鉤子在大多數(shù)的 WordPress 都建立之后。WordPress 同樣添加許多內部的功能到這個鉤子中,例如 post types 和 taxonomies 的廚廁以及默認 widgets 的初始化。
因為這時幾乎 WordPress 中的所有內容都就緒了,當 WordPress 的所有信息都可用時,你的插件使用這個鉤子差不多可以做任何需要的事情了。
下面的例子中,為用戶添加了給 pages 寫摘要的功能。這應該在 init 中執(zhí)行,因為 “page” post type 在這時使用 add_post_type_support() 函數(shù)來創(chuàng)建。( 詳見 Part-11, “擴展 posts”)
<?php
add_action( 'init', 'boj_add_excerpts_to_pages' );
function boj_add_excerpts_to_pages() {
add_post_type_support( 'page', array( 'excerpt' ) );
}
?>
admin_menu
admin_menu 鉤子在管理員頁面加載的時候調用。無論何時你的插件直接在管理頁面下工作,你都要用這個鉤子來執(zhí)行你的代碼。
下面的例子添加了一個內容是 BOJ Settings 的 sub-menu 項到 WordPress 管理頁面的設置菜單。(詳見:Part-7,”插件設置”)
<?php
add_action( 'admin_menu', 'boj_admin_settings_page' );
function boj_admin_settings_page() {
add_options_page(
'BOJ Settings',
'BOJ Settings',
'manage_options',
'boj_admin_settings',
'boj_admin_settings_page'
);
}
?>
template_redirect
template_redirect 動作鉤子很有用,因為它是 WordPress 知道用戶正在瀏覽的頁面的關鍵。它在特定的頁面選擇 theme template 之前執(zhí)行。在只在網站的前端觸發(fā),并不在管理員頁面觸發(fā)。
當你需要為特定的頁面加載代碼的時候,這個鉤子很有用。
下面的例子中,僅僅為 singular post 加載一個樣式表文件。
<?php
add_action( 'template_redirect', 'boj_singular_post_css' );
function boj_singular_post_css() {
if( is_singular( 'post' ) ) {
wp_enqueue_style (
'boj-singular-post',
'boj-example.css',
false,
0.1,
'screen'
);
}
}
?>
wp_head
在網站的前端,WordPress 的模板調用 wp_head() 函數(shù),會觸發(fā) wp_head 鉤子。插件使用這個鉤子在 <head> 和 </head> 標簽之間添加 HTML。
下面的例子中在前端添加一個 meta description。
<?php
add_action( 'wp_head', 'boj_front_page_meta_description' );
function boj_front_page_meta_description() {
/* 得到站點描述 */
$description = esc_attr( get_bloginfo( 'description' ) );
/* 如果 description 設置了,顯示 meta 元素 */
if ( !empty( $description ) )
echo '<meta name="description" content="'. $description. '"/>';
}
?>
有些插件錯誤的使用了 wp_head 動作鉤子來添加 JavaScript 代碼,實際上應該使用 wp_enqueue_script() 函數(shù)的。( 詳見:Part-12,”JavaScript 和 AJAX “)。唯一一種使用這個鉤子來添加 JavaScript 的情形是當 JavaScript 代碼不在一個單獨的文件中時。
過濾器 Filters
過濾器鉤子和動作鉤子有很大的區(qū)別。它讓你可以控制代碼的輸出。動作鉤子是讓你插入代碼,而過濾器鉤子讓你重寫 WordPress 傳遞給鉤子的代碼。你的函數(shù)會對輸出進行”過濾”。
要掌握過濾器鉤子的概念,必須首先明白 WordPress 的 apply_filters() 函數(shù)是如何工作的。
<?php
apply_filters( $tag, $value );
?>
- $tag – 過濾器鉤子的名字。
- $value – 傳遞給任何添加到這個鉤子的過濾器的參數(shù)。這個函數(shù)可以添加任意個額外的 $value 參數(shù)傳遞給過濾器。
注意:在寫一個過濾器的時候 $value 必須返回給 WordPress。
下面是 WordPress 核心的一個過濾器鉤子的例子:
<?php
apply_filters( 'template_include', $template );
?>
這個例子中,template_include 是一個過濾器鉤子的名字,$template 是一個可以通過注冊到這個過濾器鉤子的過濾器改變的文件名。
什么是過濾器?
一個過濾器是一個注冊到過濾器鉤子的函數(shù)。這個函數(shù)最少接受一個參數(shù)并在執(zhí)行完代碼后返回那個參數(shù)。沒有過濾器的過濾器鉤子沒有任何作用。插件開發(fā)者可以通過過濾器鉤子改變不同的變量 – 從字符串到多位數(shù)組。
當一個過濾器鉤子被 apply_filters() 函數(shù)調用時,所有注冊到這個鉤子的過濾器都會被執(zhí)行。要添加一個過濾器,使用 add_filter() 函數(shù)。
<?php
add_filter( $tag, $function, $priority, $accepted_args );
?>
和動作鉤子添加動作類似。$accepted_args 是過濾器函數(shù) $function 接受的參數(shù)個數(shù),默認是1。你的函數(shù)必須至少接受一個參數(shù)并返回。
可以給一個過濾器鉤子添加多個過濾器。同樣其他 WordPress 插件也可以給這個鉤子添加過濾器。過濾器鉤子并不限制給一個鉤子。注意:因為每個過濾器都必須返回一個值供其他過濾器使用。如果你的函數(shù)沒有返回值,那就可能會破壞整個 WordPress 或者其他的插件。
下面看看 wp_title 過濾器鉤子,它是負責頁面的 <title> 元素的過濾器鉤子。
<?php
apply_filters( 'wp_title', $title, $sep, $seplocation );
?>
- wp_title – 鉤子名。
- $title – 一個字符串,要過濾并返回給 WordPress 的值。
- $sep – 說明 <title> 元素之間的分隔符是什么。
- $seplocation – 分隔符的位置。下一個例子中要用到。
現(xiàn)在寫一個函數(shù)來過濾 $title 的輸出 – 在頁面的 title 后面附加站點的名字:
<?php
add_filter( 'wp_title', 'boj_add_site_name_to_title', 10, 2 );
function boj_add_site_name_to_title( $title, $seq ) {
/* 得到網站名稱 */
$name = get_bloginfo( 'name' );
/* 附加到 $title 變量。 */
$title .= $sqp.' '.$name;
/* 返回 $title */
return $title;
}
?>
boj_add_site_name_to_title() 函數(shù)修改 $title 參數(shù)并返回給 WordPress。$sep 參數(shù)在函數(shù)中使用,但沒有返回。
過濾器鉤子函數(shù)
除了前面提到的 apply_filters() 和 add_filter() 函數(shù),WordPress 還提供其他的操作過濾器鉤子的函數(shù)。
apply_filters_ref_array
類似于動作鉤子里面的 do_action_ref_array() 函數(shù)。
<?php
apply_filters_ref_array( $tag, $args );
?>
假設你要執(zhí)行一個一般的 WordPress 沒有的復雜的數(shù)據(jù)庫查詢來加載首頁的 posts。WordPress 提供了一個叫做 posts_results 的過濾器鉤子使得你可以改變它。下面是 WordPress 核心中的代碼:
<?php
$this -> posts = apply_filters_ref_array(
'posts_results', array( $this -> posts, & $this )
);
?>
這個過濾器鉤子向所有注冊到它的過濾器傳遞一個 post 對象的數(shù)組。下面的例子,你完全重寫這個 post 對象的數(shù)組并用自定義的內容代替。默認情況下,WordPress 查詢 post 類型的 posts。下面改成查詢 page 類型的 psots 并顯示在首頁。
這段代碼使用了 wpdb 類,在 part-6 “插件安全” 中將詳細介紹。
<?php
add_filter( 'posts_result', 'boj_custom_home_page_posts' );
function boj_cumstom_home_page_posts( $result ) {
global $wpdb, $wp_query;
/* 檢查是否在首頁 */
if ( is_home() ) {
/* 每頁的 post 個數(shù) */
$per_page = get_option( 'posts_per_page' );
/* 得到當前頁 */
$paged = get_query_var( 'paged' );
/* 設置 $page 變量 */
$page = ( ( 0 == $paged || 1 == $paged ) ? 1 : absint( $paged ) );
/* 設置偏移的 posts 的個數(shù) */
$offset = ( $page - 1 ) * $per_page. ',';
/* 通過 $offset 和 要顯示的 posts 數(shù)量來設置 limit */
$limits = 'LIMIT'. $offset . $per_page;
/* 從數(shù)據(jù)庫查詢結果 */
$result = $wpdb -> get_results("
SELECT SQL_CALC_FOUND_ROWS $wpdb -> posts. *
FROM $wpdb -> posts
WHERE 1 = 1
AND post_type = 'page'
AND post_status = 'publish'
ORDER BY post_title ASC $limits "
);
}
return $result;
}
?>
remove_filter
<?php
remove_filter( $tag, $function_to_remove, $priority, $accepted_args );
?>
這和前面的 remove_action 類似。
下面看看 WordPress 定義在 wp-includes/default-filters.php 頁面中的默認的過濾器。其中一個有意思的過濾器叫做 wpautop(),它將雙換行轉換成 HTML 的 <p> </p>。這也就是我們在 HTML 發(fā)布內容時,直接回車便可在最終前端顯示的時候換行的原因。它在核心代碼中的幾個鉤子中都執(zhí)行。下面是其中一個實例:
<?php
add_filter( 'the_content', 'wpautop' );
?>
這將 wpautop() 過濾器應用到 post 的內容中,把每個換行都轉換成段落( <p> )。但是也許有的客戶不希望他的內容自動變成段落。那么你就可以使用 remove_filter() 函數(shù)從 the_content 鉤子中刪除這個過濾器。
<?php
remove_filter( 'the_content', 'wpautop' );
?>
因為在 add_filter 的時候沒有定義優(yōu)先級,所以這里也不需要。
remove_all_filters
和前面的remove_all_actions類似。
<?php
remove_all_filters( $tag, $priority );
?>
has_filter
和前面的 has_action 類似。
current_filter
同樣類似于 did_action。不過它不僅僅對過濾器鉤子有效,同樣對動作鉤子也有效,所以它返回的是當前的 action 或者 filter 鉤子。這個函數(shù)在你對多個鉤子使用單個函數(shù),但是需要依賴不同的鉤子執(zhí)行不同的內容的時候非常的有用。例如,客戶希望在 post 標題 和內容中限制一些內容,但是這兩個限制的minganci的集合是不同的。使用 current_filter() 函數(shù)來根據(jù)鉤子設置不同的minganci表就可以實現(xiàn)用一個函數(shù)同時過濾 the_content 和 the_title。使用下面的代碼,你可以把minganci替換成**。
<?php
add_filter( 'the_content', 'boj_replace_unwanted_words' );
add_filter( 'the_title', 'boj_replace_unwanted_words' );
function boj_replace_unwanted_words( $text ) {
/* 如果過濾器鉤子是 the_content */
if( 'the_content' == current_filter() )
$words = array( 'min', 'gan', 'ci' );
/* 如果鉤子是 the_title */
elseif( 'the_title' == current_filter() )
$words = array( 'zhen', 'de', 'hen', 'min', 'gan' );
/* 替換minganci */
$text = str_replace( $words, '**', $text );
return $text;
}
?>
快速返回函數(shù)
你經常需要寫一個函數(shù)返回一個常用的值給一個過濾器鉤子,例如 true,false,或者一個空數(shù)組。你甚至嘗試使用 PHP 的 create_function() 函數(shù)來快速返回一個值。
WordPress 提供幾個函數(shù)處理這種情況。
下面是例子禁用了 user contact 方法 – 在 WordPress 的個人用戶管理頁面中的一系列 <input>。要禁用這些表單項,你需要返回一個空數(shù)組。通常,你必須添加過濾器鉤子調用和函數(shù)。
<?php
add_filter( 'user_contactmethods', 'boj_return_empty_array' );
function boj_return_empty_array() {
return array();
}
?>
寫這樣的代碼一兩次并沒什么。但是寫一個返回空數(shù)組的函數(shù)太傻了。WordPress 使之簡單化了。因為要禁用這些表單項,你只需要使用 WordPress 的 __return_empty_array() 函數(shù)作為過濾器來快速返回一個空數(shù)組。如下:
add_filter( 'user_contactmethods', '__return_empty_array' );
還有幾個類似的快速返回函數(shù):
- __return_false
- __return_true
- __return_zero
如果上面的函數(shù)不符合你的要求,你還可以創(chuàng)建自己的快速返回函數(shù)。
常用的過濾器鉤子
WordPress 上百種過濾去鉤子提供給插件開發(fā)者。下面介紹一些常用的過濾器鉤子。
the_content
the_content 可以說是用的最多的過濾器鉤子了。沒有內容,一個網站一點用處也沒有。內容是一個網站上最重要的東子,插件使用這個鉤子為網站添加許多特色。
the_content 鉤子向所有注冊給它的過濾器傳遞一個 post 的內容。之后由過濾器來控制內容,通常添加一些格式化或者附加而外的一些信息。下面的例子根據(jù) post 分類,當用戶閱讀一篇 post 時顯示一個附加的相關 post 列表到 the_content。
<?php
add_filter( 'the_content', 'boj_add_related_posts_to_content' );
function boj_add_related_posts_to_content( $content ) {
/* 如果不是單篇文章,直接返回 content */
if ( !is_singular( 'post' ) )
return $content;
/* 得到當前 post 的分類 */
$terms = get_the_terms( get_the_ID(), 'category' );
/* 循環(huán)分類,并將它們的 ID 放到一個數(shù)組中 */
$categories = array();
foreach ( $terms as $term )
$categories[] = $term -> term_id;
/* 從數(shù)據(jù)庫查詢相同分類的 posts */
$loop = new WP_Query(
array(
'cat__in' => $categories,
'posts_per_page' => 5,
'post__not_in' => array( get_the_ID() ),
'orderby' => 'rand'
)
);
/* 是否有相關 posts 存在 */
if( $loop -> have_posts() ) {
/* 開始 ul */
$content .= '<ul class="related-posts">';
while( $loop -> have_posts() {
$loop -> the_post();
/* 添加 post 標題 */
$content .= the_title (
'<li><a href="'.get_permalink().'">',
'</a></li>',
false
);
}
/* 結束 ul */
$content .= '</ul>';
/* 重置 query */
wp_reset_query();
}
/* 返回 content */
$return $content;
}
?>
the_title
文章的標題幾乎和內容一樣重要,所以 the_title 也是一個常用的過濾器鉤子。你可以使用這個鉤子添加信息,或者直接重寫。
應用給 the_title 鉤子的一個有用的過濾器就是去除標題中 HTML 標簽的過濾器函數(shù)。用戶有時會添加一些標簽到標題中,這可能會破壞正常的格式。使用下面的代碼,你可以過濾掉所有用戶可能添加到標簽。
<?php
add_filter( 'the_title', 'boj_strip_tags_from_titles' );
function boj_strip_tags_from_title( $title ) {
$title = strip_tags( $title );
$return $title;
}
?>
同樣 comment_text 也很常用
下面的例子中,檢查一條評論是否是網站的注冊用戶發(fā)表的。如果是注冊用戶,你可以附加一段用戶信息的說明( 詳見:Part-8,”用戶” )
<?php
add_filter( 'comment_text', 'boj_add_role_to_comment_text' );
function boj_add_role_to_comment_text( $text ) {
global $comment;
/* 檢查是否是注冊用戶 */
if( $comment -> user_id > 0 ) {
/* 新建一個用戶對象 */
$user = new WP_User( $comment -> user_id );
/* 如果用戶有一個角色,添加到評論內容中 */
if( is_array( $user -> roles ) )
$text .= '<p>User Role: ' .$user -> roles[0]. '</p>';
}
return $text;
}
?>
template_include
template_include 是其他一些更特殊的過濾器鉤子的一類”雜物包”( catchall )過濾器鉤子。
- front_page_template
- home_template
- single_template
- page_template
- attachment_template
- archive_template
- category_template
- tag_template
- author_template
- date_template
- search_template
- 404_template
- index_template
它用在 theme template 過濾器后面,當當前頁被選中后。WordPress 根據(jù)讀者當前瀏覽的頁面來選擇一個模板。你可以為每一個獨立的過濾器鉤子添加一個過濾器,也可以在最后使用 template_include 鉤子一起過濾他們。
比如你想按照你的插件的標準構造一個模板層級結構,而不是使用 WordPress 默認的模板層級。template_include 和上面列表中的鉤子可以滿足你。
下面的例子中,根據(jù)分類判斷一個 posts 的模板是否存在。默認情況下,WordPress 先檢查 single.php,如果不存在,再檢查 index.php文件。而你的函數(shù)查找一個叫做 single-category-$slug.php ( $slug 是分類的別名 )的文件。所以如果用戶有一個叫 art 的分類,同時一個模板的名字叫做 single-category-art.php,那么這個文件會被用來代替 single.php。
<?php
add_filter( 'single_template', 'boj_single_template' );
function boj_single_template( $template ) {
global $wp_query;
/* 檢查是否在瀏覽單個 post */
if( is_singular( 'post' ) ) {
/* 獲得 post ID */
$post_id = $wp_query -> get_queried_object_id();
/* 獲得 post 的分類 */
$terms = get_the_terms( $post_id, 'category' );
/* 循環(huán)分類,添加別名作為文件名的一部分 */
$template = array();
foreach ( $terms as $term )
$templates[] = "single-category-{$term->slug}.php";
/* 檢查模板是否存在 */
$locate = locate_template( $templates );
/* 如果找到,讓它成為新模板 */
if( !empty ( $locate ) )
$template = $locate;
}
/* 換回模板文件名 */
return $template;
}
?>
使用一個類中的鉤子
前面已經講了許多通過 PHP 函數(shù)來使用動作鉤子和過濾器鉤子的例子。在類中添加一個方法作為一個動作或者過濾器的時候,格式和 add_action() 和 add_filter() 略微有些不同。
一般來說,插件使用函數(shù)而不是類中的方法作為動作或者過濾器。但是,可能有些時候使用類更適合,所以你要知道如何類在類中將方法注冊到鉤子。
前面已經提到的注冊函數(shù)到一個動作鉤子的方法:
<?php
add_action( $tag, $function_to_add );
?>
當在類中將方法作為 $function_to_add 參數(shù)時,你必須把 $function_to_add 變成一個數(shù)組,其中 & $this 作為第一個參數(shù),方法名作為第二個參數(shù):
<?php
add_action( $tag, array( & $this, $method_to_add ) );
?>
對于過濾器鉤子也是一樣。一般的將函數(shù)添加到一個過濾器鉤子類似于:
<?php
add_filter( $tag, $function_to_add );
?>
當使用類的方法的時候,要改成:
<?php
add_filter( $tag, array( & $this, $method_to_add ) );
?>
下面的例子中,創(chuàng)建了一個類,包含一個構造函數(shù),一個作為動作的方法,和一個作為過濾器的方法。add_filters() 方法檢查用戶是否在瀏覽單篇 post。如果是 content() 方法附加最后的修改時間到 post 的內容中。
<?php
class BOJ_My_Plugin_Loader {
/* 類的構造函數(shù) */
function BOJ_My_Plugin_Loader() {
/* 為 'template_redirect' 鉤子添加 'singular_check' 方法 */
add_action( 'template_redirect', array( & $this, 'singular_check' ) );
}
/* 作為動作的方法 */
function singular_check() {
/* 如果在看單個文章,過濾內容 */
if ( is_singular() )
add_filter( 'the_content', array( & $this, 'content' ) );
}
/* 作為過濾器的方法 */
function content( $content ) {
/* 得到 post 的最后修改時間 */
$date = get_the_modified_time( get_option( 'date_format' ) );
/* 附加修改時間到 content */
$content .= '<p> 最后修改于:' .$date. '</p>';
return $content;
}
}
$boj_myplugin_loader = new BOJ_My_Plugin_Loader();
?>
創(chuàng)建自定義鉤子
插件不但可以使用內核的內置鉤子,他們也可以創(chuàng)建自定義的鉤子供其他插件和模板使用。
插件可以使用4個可用函數(shù)中的一個來創(chuàng)建自定義鉤子。
- do_action()
- do_action_ref_array()
- apply_filters()
- apply_filters_ref_array()
前兩個創(chuàng)建自定義動作鉤子,后兩個創(chuàng)建自定義過濾器鉤子。
創(chuàng)建自定義鉤子的優(yōu)點
自定義鉤子使得你的插件更靈活,使其可以被其他插件擴展,讓你可以鉤到你的整個插件自己的其他執(zhí)行過程中。
使用自定義鉤子還可以防止用戶直接修改你的插件。這一點的重要性在于,當你更新你的插件時,用戶不會失去他們修改的內容。
自定義動作鉤子實例
在這個例子中,建立了一個插件安裝函數(shù)。這個函數(shù)定義了一個可以更換的常量。別的插件也可以在這個鉤子上執(zhí)行任何代碼。因為你在那一點上提供了鉤子。
<?php
add_action( 'plugins_loaded', 'boj_myplugin_setup' );
function boj_myplugin_setup() {
/* 允許動作最先觸發(fā) */
do_action( 'boj_myplugin_setup_pre' );
/* 檢查 root slug 是否定義 */
if( !defined( 'BOJ_MYPLUGIN_ROOT_SLUG' ) )
define( 'BOJ_MYPLUGIN_ROOT_SLUG', 'articles' );
}
?>
其他插件或者模板可以鉤到 boj_myplugin_setup_pre 來執(zhí)行任何函數(shù)。
比如你想把 BOJ_MYPLUGIN_ROOT_SLUG 常量從 ‘articles’ 改為 ‘papers’ ,你可以建立一個動作并添加到這個鉤子中:
<?php
add_action( 'boj_myplugin_setup_pre', 'boj_define_myplugin_constants' );
function boj_define_myplugin_constants() {
define( 'BOJ_MYPLUGIN_ROOT_SLUG', 'papers' );
}
?>
自定義過濾器鉤子實例
假設有一個函數(shù)顯示一個具有一個特定闡述的文章列表。你也許希望其他人能夠過濾那個參數(shù)或者過濾最終結果。
下面的例子中,寫一個函數(shù)根據(jù)收到的評論條數(shù)列取了前10的文章。這個函數(shù)讓用戶可以在從數(shù)據(jù)庫獲取數(shù)據(jù)前過濾這個參數(shù),并且可以過濾最終輸出的 HTML 列表。
<?php
function boj_posts_by_comments() {
/* 默認參數(shù) */
$args = array(
'post_type' => 'post',
'posts_per_page' => 10,
'order' => 'DESC',
'oerderby' => 'comment_count'
);
/* 應用過濾器 */
$args = apply_filters( 'boj_posts_by_comments_args', $args );
/* 設置輸出變量 */
$out = '';
/* 由給定的參數(shù)從數(shù)據(jù)庫查詢文章 */
$loop = new WP_Query( $args );
/* 檢查是否返回結果 */
if( $loop -> have_posts() ) {
$out .= '<ul class="posts-by-comments" >';
while( $loop -> have_posts() ) {
$loop -> the_post();
$out .= the_title( '<li>', '</li>', false );
}
$out .= '</ul>';
}
$out = apply_filters( 'boj_posts_by_comments', $out );
echo $out;
}
?>
要過濾參數(shù),給 boj_posts_by_comments_args 添加一個過濾器。比如你希望把數(shù)量從默認的10變成15,添加下面的過濾器:
<?php
add_filter( 'boj_posts_by_comments_args', 'boj_change_posts_by_comments_args' );
function boj_change_posts_by_comments_args( $args ) {
$args['posts_per_page'] = 15;
return $args;
}
?>
要過濾最后的 HTML 輸出,添加一個過濾器到 boj_posts_by_comments。比如你想把 ul 改成 ol。
<?php
add_filter( 'boj_posts_by_comments', 'boj_change_posts_by_comments' );
function boj_change_posts_by_comments( $out ) {
$out = str_replace( '<ul', '<ol', $out );
$out = str_replace( '</ul>', '</ol>', $out );
return $out;
}
?>
上哪找鉤子?
要給出 WordPress 中所有鉤子的列表幾乎是不可能的。前面我們討論了一些常用的動作和過濾器鉤子,這一節(jié)僅僅討論一小部分 WordPress 提供的鉤子。
WordPress 的新版本會加入新的鉤子。最終查看不同版本的內核可以讓你找到可以用在插件中的新鉤子。
在內核中搜索鉤子
作為一個插件開發(fā)這,你應該熟悉 WordPress 的內核。尋找鉤子能很好的幫助你熟悉 WordPress 內核是如何工作的。沒有更好的方法來搞明白 PHP 代碼在內核中是如何工作的了。
要尋找鉤子的一個簡單的方法是在編輯器中打開一個 WordPress 文件然后搜索下面的四個詞:
- do_action
- do_action_ref_array
- apply_filters
- apply_filters_ref_array
這四個函數(shù),每一個都創(chuàng)建一個鉤子。
變量鉤子
在 WordPress 的內核中找鉤子的時候,你會遇到變量鉤子。通常鉤子的名字是一個靜態(tài)的字符串。但是變量鉤子的名字跟著特定的變量而改變。
一個很好的例子就是 load-$pagenow 動作鉤子。變量 $pagenow 根據(jù) WordPress 中當前瀏覽的 admin 頁面而改變。這個鉤子如下:
<?php
do_action( "load-$pagenow" );
?>
變量 $pagenow 會變成當前訪問頁面的名字。例如 new post 頁面的鉤子是 load-post-new.php,而編輯頁面的是 load-post.php。這就使得插件僅僅對特定的 admin 頁面執(zhí)行代碼。WordPress 有幾個動作和過濾器鉤子的名稱里面是含有變量的。
通常,這些鉤子的名字變成給定的內容,使得插件開發(fā)者可以在特定的環(huán)境下執(zhí)行代碼。
鉤子參考列表
雖然在核心里面搜索鉤子有助于你增長經驗,但是有時你需要一些網上的參考列表。
WordPress 在 Codex 中有官方的鉤子參考列表。
- http://codex.wordpress.org/Plugin_API/Action_Reference
- http://codex.wordpress.org/Plugin_API/Filter_Reference
在 Part-18,開發(fā)人員工具箱,將介紹更多幫助插件開發(fā)者的工具。
總結
鉤子是創(chuàng)建 WordPress 插件的最重要的一環(huán)了。每次你開始創(chuàng)建一個插件,你都要把你的插件鉤到 WordPress 的動作鉤子和過濾器鉤子中。鉤子是插件開發(fā)中必不可少的工具。在了解了鉤子之后,就是時候開始創(chuàng)建插件了。
注:本文出自《Professional WordPress Plugin Development》一書,由 sixpoint.me 翻譯,倡萌整編。
您已閱讀完《WordPress 插件開發(fā)教程(共4篇)》專題的第 3 篇。請繼續(xù)閱讀該專題下面的文章:





可惜只有4課啊,如果能有完整的就好了
謝謝作者的翻譯!