欢迎光临
我们一直在努力

WordPress 插件开发教程 Part 3 – 钩子( Hooks )

建站超值云服务器,限时71元/月

本文目录
[隐藏]

  • 1动作钩子 ( actions )
    • 1.1什么是动作?
    • 1.2动作钩子函数
      • 1.2.1do_action_ref_array
      • 1.2.2remove_action
      • 1.2.3remove_all_actions
      • 1.2.4has_action
      • 1.2.5did_action
      • 1.2.6register_activation_hook 和 register_deactivation_hook
    • 1.3常用的动作钩子
      • 1.3.1plugins_loaded
      • 1.3.2init
      • 1.3.3admin_menu
      • 1.3.4template_redirect
      • 1.3.5wp_head
  • 2过滤器 Filters
    • 2.1什么是过滤器?
      • 2.1.1过滤器钩子函数
      • 2.1.2apply_filters_ref_array
      • 2.1.3remove_filter
      • 2.1.4remove_all_filters
      • 2.1.5has_filter
      • 2.1.6current_filter
    • 2.2快速返回函数
    • 2.3常用的过滤器钩子
      • 2.3.1the_content
      • 2.3.2the_title
      • 2.3.3同样 comment_text 也很常用
      • 2.3.4template_include
    • 2.4使用一个类中的钩子
  • 3创建自定义钩子
    • 3.1创建自定义钩子的优点
    • 3.2自定义动作钩子实例
    • 3.3自定义过滤器钩子实例
  • 4上哪找钩子?
    • 4.1在内核中搜索钩子
    • 4.2变量钩子
    • 4.3钩子参考列表
  • 5总结

本文是《WordPress 插件开发教程》系列教程的第 3 部分,该系列共包含以下 4 个部分:

  1. WordPress 插件开发教程 Part 1 – WordPress 插件简介
  2. WordPress 插件开发教程 Part 2 – WordPress 插件基础
  3. WordPress 插件开发教程 Part 3 – 钩子( Hooks )
  4. WordPress 插件开发教程 Part 4 – 与WordPress整合

本节内容

  • 为动作钩子创建动作
  • 为过滤器钩子创建过滤器
  • 使用 PHP 类里面的钩子
  • 为插件添加自定义钩子
  • 寻找 WordPress 中的钩子

钩子是 WordPress 的精髓。他们允许插件开发人员钩进 WordPress 工作流程内部来改变它的工作,而不用直接修改核心代码。这就使得用户可以方便的升级到 WordPress 的新版本而不需要修改一行代码。

如果一个开发人员修改了核心代码,这些改动在 WordPress 下一次升级的时候就会消失。升级会覆盖这些改动。使用钩子让你能够在核心以外的目录中单独开发插件,这就在升级时保证了插件代码的安全。

没有钩子,插件就没办法改变 WordPress 的功能了。本节介绍的钩子系统会贯穿整个教程,同时也会在几乎每一个插件的开发中用到。在你学会了钩子的用法后,你就会明白为什么 WordPress 这个平台这么强大,并有上千个插件供几百万用户使用了。

WordPress 主要有两种类型的钩子:动作钩子和过滤器钩子(action hooks and filter hooks )。第一个使得你可以在一个特定时刻上执行一个函数,第二个使得你可以操作通过钩子的输出。

钩子不是仅仅针对插件的。WordPress 内部也使用钩子。如果你浏览核心代码,你就能见到很多 WordPress 自己钩自己的例子了。

动作钩子 ( actions )

动作钩子让你可以在 WordPress 加载过程中或者当某个事件发生的特定时刻触发一个函数。例如:你可能希望当 WordPress 第一次加载一个页面或者保存一篇文章时执行一个函数。

你需要理解 do_action() 函数。当钩进 WordPress 中时,你的插件不会直接调用这个函数;但是你的插件几乎都会间接的使用它。

1
2
3
<?php
  do_action( $tag, $arg = '' );
?>

<?php do_action( $tag, $arg = ” ); ?>

  • $tag — 动作钩子的名称
  • $arg — 传递给已注册的动作的值。它看起来像一个单独的参数,但通常都不是这样的。动作钩子可以传递任何个数的参数,或者根本不传参数。对特殊的钩子,你需要查看 WordPress 的源码,因为参数个数在每个钩子的基础改变。

下面是一个具有多参数的动作钩子的例子:

1
2
3
<?php
  do_action( $tag, $arg_1, $arg_2, $arg_3 );
?>

<?php do_action( $tag, $arg_1, $arg_2, $arg_3 ); ?>

下面看看一个叫做 wp_head 的钩子是怎么出现在 WordPress 中的。这个钩子出现在前台的 <head> 里面。 WordPress 和插件经常用这个钩子来添加 meta 信息,样式表,和 js 脚本。

1
2
3
<?php
  do_action('wp_head');
?>

<?php do_action(‘wp_head’); ?>

当这段代码在 WordPress 中执行时,它会寻找任何为 wp_head 动作钩子注册的动作。然后按照特殊顺序执行它们。如你所见,它名叫 wp_head 但是没有传递额外的参数。动作钩子通常都这样。下面是有两个额外参数的动作钩子的例子:

1
2
3
<?php
  do_action( 'save_post', $post_ID, $post );
?>

<?php do_action( ‘save_post’, $post_ID, $post ); ?>

这个 save_post 的钩子传递两个参数,一个 $post_ID,一个 $post。

什么是动作?

从技术上讲,一个动作就是一个 PHP 函数。一个函数要成为一个动作,它需要被注册成一个动作钩子。在上面的部分可以看到什么是动作钩子,但是动作钩子要用作任何目的,需要有个动作为它们注册。这就是插件的来源。你开发的自定义的函数(动作)当动作钩子出发之后就可以执行一个特定任务了。要实现这个,就要使用 add_action() 函数。

1
2
3
<?php
add_action( $tag, $function, $priority, $accepted_args );
?>

<?php add_action( $tag, $function, $priority, $accepted_args ); ?>

  • $tag – 你的函数执行时代动作钩子的名称。
  • $function – WordPress 要调用的函数名。
  • $priority – 一个表示动作调用顺序的整数,默认是10。数字越小,这个函数越早被调用。
  • $accepted_args – 动作钩子要传递给你的函数的参数个数。默认只有一个参数。

动作钩子并不局限于单个动作。你的插件可以将多个函数添加到一个动作钩子上。其他插件,甚至是 WordPress 核心,经常将多个函数添加到同一个钩子上。

现在是使用动作钩子的时候了。一个常见的动作钩子是 wp_footer 。它提供给前端用户的 WordPress 模板使用。通常它刚好在</body>前调用。在下面的例子中,将要为 wp_footer 注册一个动作并添加一条自定义信息到 footer。

1
2
3
4
5
6
<?php
add_action( 'wp_footer', 'boj_example_footer_message', 100 );
function boj_example_footer_message() {
    echo "基于 <a href="http://wordpress.org" >WordPress </a>架设。;
}
?>

<?php add_action( ‘wp_footer’, ‘boj_example_footer_message’, 100 ); function boj_example_footer_message() { echo “基于 <a href=”http://wordpress.org” >WordPress </a>架设。; } ?>

仔细看看上面的代码中如何使用 add_action() 函数的。

1
2
3
<?php
add_action( 'wp_footer', 'boj_example_footer_message', 100 );
?>

<?php add_action( ‘wp_footer’, ‘boj_example_footer_message’, 100 ); ?>

第一个参数是钩子的名字( wp_footer )。第二个参数是要回调的函数 ( boj_example_footer_message )。第三个参数是优先级 ( 100 )。这个函数比起其他钩到 wp_footer 中的函数,会在比较靠后的次序执行。如果设置成1,就较先执行。

要说明,钩子可能会因为许多原因在 WordPress 执行过程中多次触发。任何添加到这个钩子中的动作每当钩子触发时都会执行一次。

动作钩子函数

你已经知道了最基本的两个动作钩子函数怎么使用;do_action() 和 add_action()。WordPress 还有其他类型的与动作钩子相关的函数用于插件开发。

do_action_ref_array

do_action_ref_array() 函数和 do_action() 函数完成同样的工作,只不过参数传递的方式不同。它并不传递通过附加参数的值来确定的多个参数,而是传递一个包含参数的数组。这个参数数组也是一个必须的参数。这个函数的目的是通过引用传递一个对象给添加到特定钩子的动作(函数)。这意味着这个函数可以不用返回就改变对象本身。

1
2
3
<?php
do_action_ref_arrray( $tag, $args );
?>

<?php do_action_ref_arrray( $tag, $args ); ?>

  • $tag – 动作钩子的名字。
  • $args – 要传递给注册到这个钩子的函数的参数的数组。通常,这是一个动作可以改变的对象。

下面看一个 WordPress 如何调用 do_action_ref_array() 的实例。下面的代码展示了 pre_get_posts 动作钩子。WordPress 在从数据库取得 posts 之前执行这个钩子,使得插件可以改变查询 posts 的方式。

1
2
3
<?php
do_action_ref_array( 'pre_get_posts', array( & $this ) );
?>

<?php do_action_ref_array( ‘pre_get_posts’, array( & $this ) ); ?>

第一个参数 pre_get_posts 是钩子的名字。第二个参数是从数据库中查询 posts 的参数的数组。这个钩子使你可以执行基于那个参数数组的代码。

假如你想安装随机的顺序来得到首页的 blog,而不是默认的通过发布时间来得到。你就需要注册一个动作到这个钩子,并改变排序顺序。

1
2
3
4
5
6
7
<?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' );
}
?>

<?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() 函数添加了。如果你在动作注册之前执行 remove_action(),那么动作并不会被从钩子中删除。

如果动作被成功删除,则函数返回 true,否则返回 false。

1
2
3
<?php
remove_action( $tag, $function_to_remove, $priority, $accepted_args );
?>

<?php remove_action( $tag, $function_to_remove, $priority, $accepted_args ); ?>

参数类似于 do_action()。要成功的从一个钩子中删除一个动作, $tag, $function_to_remove, 和 $priority 必须完全的复合 do_action() 中使用的参数。否则动作不会被溢出,同时 remove_action() 返回 flase。

我们看一个叫做 rel_canonical 的 WordPress 默认动作。这个动作在 <head> 和 </head> 元素之间添加一个 canonical 链接。

1
2
3
<?php
add_action( 'wp_head', 'rel_canonical' );
?>

<?php add_action( ‘wp_head’, ‘rel_canonical’ ); ?>

要删除这个动作,就要使用 remove_action() 函数。你需要定义 $tag 和 $function_to_rmove 参数。这里你不用添加 $priority 因为先前定义动作的时候没有明确指定优先级。

1
2
3
<?php
remove_action( 'wp_head', 'rel_canonical' );
?>

<?php remove_action( ‘wp_head’, ‘rel_canonical’ ); ?>

WordPress、plugin 或者 theme 添加的任何动作都可以在插件中删除。通常只删除 WordPress 添加的动作。许多默认的动作都定义在 wp-includes/default-filters.php 文件中。通过浏览这个文件你就会明白 WordPress 是如何使用动作钩子的。

remove_all_actions

在有些插件中,可能需要删除所有特定 tag 或者 特定 tag + 特定优先级的所有钩子。使用 remove_all_actions() 可以一次删除所有符合条件的动作。

1
2
3
<?php
remove_all_actions( $tag, $priority );
?>

<?php remove_all_actions( $tag, $priority ); ?>

$priority 参数是可选的,默认是 false。如果你设置了这个参数,那么只有这个优先级的动作会被删除。下面的例子从 wp_head 动作钩子中删除不管任何优先级的动作。

1
2
3
<?php
remove_all_actions( 'wp_head' );
?>

<?php remove_all_actions( ‘wp_head’ ); ?>

在使用这个函数的时候必须要小心。其他 plugin 或者 theme 可能添加了你不知道动作。这就可能破坏插件应有的功能。通常应该保持你的代码尽可能的特殊。在大多数情况下,你应该使用 remove_action() 函数来代替。

has_action

有的时候需要确定一个钩子是否包含一些动作,或者一个特定的动作是否已经添加到了钩子里面。has_action() 提供了这些功能。

1
2
3
<?php
has_action( $tag, $function_to_check );
?>

<?php has_action( $tag, $function_to_check ); ?>

has_action() 函数的返回值是 Boolean 或者 一个整型值。如果 $function_to_check 参数为空,那么如果有动作已经添加到了钩子中就返回 true,反之,返回 false。而如果 $function_to_check 设置了,而且这个函数已经添加到了钩子里面,则返回该动作的优先级,否则返回 false。

下面的例子中,根据 wp_footer 动作钩子中是否有注册的动作来确定显示的信息。

1
2
3
4
5
6
<?php
if( has_action( 'wp_footer' ) )
    echo '<p> footer 中已经注册有动作了。</p>';
else
    echo '<p> footer 中还没有注册动作。</p>';
?>

<?php if( has_action( ‘wp_footer’ ) ) echo ‘<p> footer 中已经注册有动作了。</p>’; else echo ‘<p> footer 中还没有注册动作。</p>’; ?>

下面看一个 WordPress 核心添加到 wp_footer 中的动作。wp_print_footer_script() 默认注册给这个钩子。

1
2
3
<?php
add_action( 'wp_footer', 'wp_print_footer_scripts' );
?>

<?php add_action( ‘wp_footer’, ‘wp_print_footer_scripts’ ); ?>

did_action

did_action() 使你的插件可以检查一个动作钩子是否已经被执行,或者记录执行的次数。这也意味着这一次页面的加载过程中有些动作被执行了多次。

1
2
3
<?php
did_action( $tag );
?>

<?php did_action( $tag ); ?>

这个参数返回动作已经执行的次数,如果还未执行,返回 false。这个函数的一般用途是判断一个动作钩子是否已经被触发,并执行基于 did_action() 的返回值的代码。

下面的例子中,如果 plugins_loaded 动作钩子已经被触发,就定义一个 PHP 常量。

1
2
3
4
<?php
if ( did_action( 'plugins_loaded' ) )
    define( 'BOJ_MYPLUGIN_READY' ,true );
?>

<?php if ( did_action( ‘plugins_loaded’ ) ) define( ‘BOJ_MYPLUGIN_READY’ ,true ); ?>

register_activation_hook 和 register_deactivation_hook

在第二章中已经介绍了这两个函数。

常用的动作钩子

WordPress 有许多动作钩子,有一些是很常用的。

plugins_loaded

对插件开发者来说,plugins_loaded 动作钩子也许是最重要的动作钩子了。它在大多数 WordPress 文件加载完成之后,并在pluggable 函数和 WordPress 开始执行任何东西之前触发。在大多数的插件中,在这个钩子触发之前,不应该执行其他的代码。plugins_loaded 在所有用户启用的插件都被 WordPress 加载之后执行。这也是在加载过程中插件开发这最早能用到的钩子。

WordPress 的插件应该在这个钩子中执行安装。其他动作也应该添加到这个钩子的回调函数中。

下面的例子中,使用前面部分创建的 boj_example_footer_message 动作。要把它添加到钩到 plugins_loaded 钩子中的安装动作中,而不是单独调用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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="http://wordpress.org" >WordPress </a>架设。;
 
}
 
?>

<?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=”http://wordpress.org” >WordPress </a>架设。; } ?>

创建一个安装函数并把它钩到 plugins_loaded 中。这样做就可以确保不会由于特定的 WordPress 函数还没有加载而触发错误。

init

init 钩子在大多数的 WordPress 都建立之后。WordPress 同样添加许多内部的功能到这个钩子中,例如 post types 和 taxonomies 的厨厕以及默认 widgets 的初始化。

因为这时几乎 WordPress 中的所有内容都就绪了,当 WordPress 的所有信息都可用时,你的插件使用这个钩子差不多可以做任何需要的事情了。

下面的例子中,为用户添加了给 pages 写摘要的功能。这应该在 init 中执行,因为 “page” post type 在这时使用 add_post_type_support() 函数来创建。( 详见 Part-11, “扩展 posts”)

1
2
3
4
5
6
7
8
9
10
11
<?php
 
add_action( 'init', 'boj_add_excerpts_to_pages' );
 
function boj_add_excerpts_to_pages() {
 
add_post_type_support( 'page', array( 'excerpt' ) );
 
}
 
?>

<?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 钩子在管理员页面加载的时候调用。无论何时你的插件直接在管理页面下工作,你都要用这个钩子来执行你的代码。

下面的例子添加了一个内容是 BOJ Settings 的 sub-menu 项到 WordPress 管理页面的设置菜单。(详见:Part-7,”插件设置”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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'
 
);
 
}
 
?>

<?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 之前执行。在只在网站的前端触发,并不在管理员页面触发。

当你需要为特定的页面加载代码的时候,这个钩子很有用。

下面的例子中,仅仅为 singular post 加载一个样式表文件。

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
<?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'
 
);
 
}
 
}
 
?>

<?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() 函数,会触发 wp_head 钩子。插件使用这个钩子在 <head> 和 </head> 标签之间添加 HTML。

下面的例子中在前端添加一个 meta description。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?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. '"/>';
 
}
 
?>

<?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() 函数的。( 详见:Part-12,”JavaScript 和 AJAX “)。唯一一种使用这个钩子来添加 JavaScript 的情形是当 JavaScript 代码不在一个单独的文件中时。

过滤器 Filters

过滤器钩子和动作钩子有很大的区别。它让你可以控制代码的输出。动作钩子是让你插入代码,而过滤器钩子让你重写 WordPress 传递给钩子的代码。你的函数会对输出进行”过滤”。

要掌握过滤器钩子的概念,必须首先明白 WordPress 的 apply_filters() 函数是如何工作的。

1
2
3
4
5
<?php
 
apply_filters( $tag, $value );
 
?>

<?php apply_filters( $tag, $value ); ?>

  • $tag – 过滤器钩子的名字。
  • $value – 传递给任何添加到这个钩子的过滤器的参数。这个函数可以添加任意个额外的 $value 参数传递给过滤器。

注意:在写一个过滤器的时候 $value 必须返回给 WordPress。

下面是 WordPress 核心的一个过滤器钩子的例子:

1
2
3
4
5
<?php
 
apply_filters( 'template_include', $template );
 
?>

<?php apply_filters( ‘template_include’, $template ); ?>

这个例子中,template_include 是一个过滤器钩子的名字,$template 是一个可以通过注册到这个过滤器钩子的过滤器改变的文件名。

什么是过滤器?

一个过滤器是一个注册到过滤器钩子的函数。这个函数最少接受一个参数并在执行完代码后返回那个参数。没有过滤器的过滤器钩子没有任何作用。插件开发者可以通过过滤器钩子改变不同的变量 – 从字符串到多位数组。

当一个过滤器钩子被 apply_filters() 函数调用时,所有注册到这个钩子的过滤器都会被执行。要添加一个过滤器,使用 add_filter() 函数。

1
2
3
4
5
<?php
 
add_filter( $tag, $function, $priority, $accepted_args );
 
?>

<?php add_filter( $tag, $function, $priority, $accepted_args ); ?>

和动作钩子添加动作类似。$accepted_args 是过滤器函数 $function 接受的参数个数,默认是1。你的函数必须至少接受一个参数并返回。

可以给一个过滤器钩子添加多个过滤器。同样其他 WordPress 插件也可以给这个钩子添加过滤器。过滤器钩子并不限制给一个钩子。注意:因为每个过滤器都必须返回一个值供其他过滤器使用。如果你的函数没有返回值,那就可能会破坏整个 WordPress 或者其他的插件。

下面看看 wp_title 过滤器钩子,它是负责页面的 <title> 元素的过滤器钩子。

1
2
3
4
5
<?php
 
apply_filters( 'wp_title', $title, $sep, $seplocation );
 
?>

<?php apply_filters( ‘wp_title’, $title, $sep, $seplocation ); ?>

  • wp_title – 钩子名。
  • $title – 一个字符串,要过滤并返回给 WordPress 的值。
  • $sep – 说明 <title> 元素之间的分隔符是什么。
  • $seplocation – 分隔符的位置。下一个例子中要用到。

现在写一个函数来过滤 $title 的输出 – 在页面的 title 后面附加站点的名字:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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;
 
}
 
?>

<?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() 函数修改 $title 参数并返回给 WordPress。$sep 参数在函数中使用,但没有返回。

过滤器钩子函数

除了前面提到的 apply_filters() 和 add_filter() 函数,WordPress 还提供其他的操作过滤器钩子的函数。

apply_filters_ref_array

类似于动作钩子里面的 do_action_ref_array() 函数。

1
2
3
4
5
<?php
 
apply_filters_ref_array( $tag, $args );
 
?>

<?php apply_filters_ref_array( $tag, $args ); ?>

假设你要执行一个一般的 WordPress 没有的复杂的数据库查询来加载首页的 posts。WordPress 提供了一个叫做 posts_results 的过滤器钩子使得你可以改变它。下面是 WordPress 核心中的代码:

1
2
3
4
5
6
7
8
9
<?php
 
$this -> posts = apply_filters_ref_array(
 
'posts_results', array( $this -> posts, & $this )
 
);
 
?>

<?php $this -> posts = apply_filters_ref_array( ‘posts_results’, array( $this -> posts, & $this ) ); ?>

这个过滤器钩子向所有注册到它的过滤器传递一个 post 对象的数组。下面的例子,你完全重写这个 post 对象的数组并用自定义的内容代替。默认情况下,WordPress 查询 post 类型的 posts。下面改成查询 page 类型的 psots 并显示在首页。

这段代码使用了 wpdb 类,在 part-6 “插件安全” 中将详细介绍。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?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 个数 */
 
$per_page = get_option( 'posts_per_page' );
 
/* 得到当前页 */
 
$paged = get_query_var( 'paged' );
 
/* 设置 $page 变量 */
 
$page = ( ( 0 == $paged || 1 == $paged ) ? 1 : absint( $paged ) );
 
/* 设置偏移的 posts 的个数 */
 
$offset = ( $page - 1 ) * $per_page. ',';
 
/* 通过 $offset 和 要显示的 posts 数量来设置 limit */
 
$limits = 'LIMIT'. $offset . $per_page;
 
/* 从数据库查询结果 */
 
$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;
 
}
 
?>

<?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 个数 */ $per_page = get_option( ‘posts_per_page’ ); /* 得到当前页 */ $paged = get_query_var( ‘paged’ ); /* 设置 $page 变量 */ $page = ( ( 0 == $paged || 1 == $paged ) ? 1 : absint( $paged ) ); /* 设置偏移的 posts 的个数 */ $offset = ( $page – 1 ) * $per_page. ‘,’; /* 通过 $offset 和 要显示的 posts 数量来设置 limit */ $limits = ‘LIMIT’. $offset . $per_page; /* 从数据库查询结果 */ $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
1
2
3
4
5
<?php 
 
remove_filter( $tag, $function_to_remove, $priority, $accepted_args );
 
?>

<?php remove_filter( $tag, $function_to_remove, $priority, $accepted_args ); ?>

这和前面的 remove_action 类似。

下面看看 WordPress 定义在 wp-includes/default-filters.php 页面中的默认的过滤器。其中一个有意思的过滤器叫做 wpautop(),它将双换行转换成 HTML 的 <p> &lt/p>。这也就是我们在 HTML 发布内容时,直接回车便可在最终前端显示的时候换行的原因。它在核心代码中的几个钩子中都执行。下面是其中一个实例:

1
2
3
4
5
<?php
 
add_filter( 'the_content', 'wpautop' );
 
?>

<?php add_filter( ‘the_content’, ‘wpautop’ ); ?>

这将 wpautop() 过滤器应用到 post 的内容中,把每个换行都转换成段落( <p> )。但是也许有的客户不希望他的内容自动变成段落。那么你就可以使用 remove_filter() 函数从 the_content 钩子中删除这个过滤器。

1
2
3
4
5
<?php
 
remove_filter( 'the_content', 'wpautop' );
 
?>

<?php remove_filter( ‘the_content’, ‘wpautop’ ); ?>

因为在 add_filter 的时候没有定义优先级,所以这里也不需要。

remove_all_filters

和前面的remove_all_actions类似。

1
2
3
4
5
<?php
 
remove_all_filters( $tag, $priority );
 
?>

<?php remove_all_filters( $tag, $priority ); ?>

has_filter

和前面的 has_action 类似。

current_filter

同样类似于 did_action。不过它不仅仅对过滤器钩子有效,同样对动作钩子也有效,所以它返回的是当前的 action 或者 filter 钩子。这个函数在你对多个钩子使用单个函数,但是需要依赖不同的钩子执行不同的内容的时候非常的有用。例如,客户希望在 post 标题 和内容中限制一些内容,但是这两个限制的minganci的集合是不同的。使用 current_filter() 函数来根据钩子设置不同的minganci表就可以实现用一个函数同时过滤 the_content 和 the_title。使用下面的代码,你可以把minganci替换成**。

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
<?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;
 
}
 
?>

<?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; } ?>

快速返回函数

你经常需要写一个函数返回一个常用的值给一个过滤器钩子,例如 true,false,或者一个空数组。你甚至尝试使用 PHP 的 create_function() 函数来快速返回一个值。

WordPress 提供几个函数处理这种情况。

下面是例子禁用了 user contact 方法 – 在 WordPress 的个人用户管理页面中的一系列 &ltinput>。要禁用这些表单项,你需要返回一个空数组。通常,你必须添加过滤器钩子调用和函数。

1
2
3
4
5
6
7
8
9
10
11
<?php
 
add_filter( 'user_contactmethods', 'boj_return_empty_array' );
 
function boj_return_empty_array() {
 
return array();
 
}
 
?>

<?php add_filter( ‘user_contactmethods’, ‘boj_return_empty_array’ ); function boj_return_empty_array() { return array(); } ?>

写这样的代码一两次并没什么。但是写一个返回空数组的函数太傻了。WordPress 使之简单化了。因为要禁用这些表单项,你只需要使用 WordPress 的 __return_empty_array() 函数作为过滤器来快速返回一个空数组。如下:

1
add_filter( 'user_contactmethods', '__return_empty_array' );

add_filter( ‘user_contactmethods’, ‘__return_empty_array’ );

还有几个类似的快速返回函数:

  • __return_false
  • __return_true
  • __return_zero

如果上面的函数不符合你的要求,你还可以创建自己的快速返回函数。

常用的过滤器钩子

WordPress 上百种过滤去钩子提供给插件开发者。下面介绍一些常用的过滤器钩子。

the_content

the_content 可以说是用的最多的过滤器钩子了。没有内容,一个网站一点用处也没有。内容是一个网站上最重要的东子,插件使用这个钩子为网站添加许多特色。

the_content 钩子向所有注册给它的过滤器传递一个 post 的内容。之后由过滤器来控制内容,通常添加一些格式化或者附加而外的一些信息。下面的例子根据 post 分类,当用户阅读一篇 post 时显示一个附加的相关 post 列表到 the_content。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<?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' );
 
/* 循环分类,并将它们的 ID 放到一个数组中 */
 
$categories = array();
 
foreach ( $terms as $term )
 
$categories[] = $term -> term_id;
 
/* 从数据库查询相同分类的 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;
 
}
 
?>

<?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’ ); /* 循环分类,并将它们的 ID 放到一个数组中 */ $categories = array(); foreach ( $terms as $term ) $categories[] = $term -> term_id; /* 从数据库查询相同分类的 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 标签的过滤器函数。用户有时会添加一些标签到标题中,这可能会破坏正常的格式。使用下面的代码,你可以过滤掉所有用户可能添加到标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
 
add_filter( 'the_title', 'boj_strip_tags_from_titles' );
 
function boj_strip_tags_from_title( $title ) {
 
$title = strip_tags( $title );
 
$return $title;
 
}
 
?>

<?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 也很常用

下面的例子中,检查一条评论是否是网站的注册用户发表的。如果是注册用户,你可以附加一段用户信息的说明( 详见:Part-8,”用户” )

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
<?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;
 
}
 
?>

<?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 根据读者当前浏览的页面来选择一个模板。你可以为每一个独立的过滤器钩子添加一个过滤器,也可以在最后使用 template_include 钩子一起过滤他们。

比如你想按照你的插件的标准构造一个模板层级结构,而不是使用 WordPress 默认的模板层级。template_include 和上面列表中的钩子可以满足你。

下面的例子中,根据分类判断一个 posts 的模板是否存在。默认情况下,WordPress 先检查 single.php,如果不存在,再检查 index.php文件。而你的函数查找一个叫做 single-category-$slug.php ( $slug 是分类的别名 )的文件。所以如果用户有一个叫 art 的分类,同时一个模板的名字叫做 single-category-art.php,那么这个文件会被用来代替 single.php。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
<?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' );
 
/* 循环分类,添加别名作为文件名的一部分 */
 
$template = array();
 
foreach ( $terms as $term )
 
$templates[] = "single-category-{$term->slug}.php";
 
/* 检查模板是否存在 */
 
$locate = locate_template( $templates );
 
/* 如果找到,让它成为新模板 */
 
if( !empty ( $locate ) )
 
$template = $locate;
 
}
 
/* 换回模板文件名 */
 
return $template;
 
}
 
?>

<?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’ ); /* 循环分类,添加别名作为文件名的一部分 */ $template = array(); foreach ( $terms as $term ) $templates[] = “single-category-{$term->slug}.php”; /* 检查模板是否存在 */ $locate = locate_template( $templates ); /* 如果找到,让它成为新模板 */ if( !empty ( $locate ) ) $template = $locate; } /* 换回模板文件名 */ return $template; } ?>

使用一个类中的钩子

前面已经讲了许多通过 PHP 函数来使用动作钩子和过滤器钩子的例子。在类中添加一个方法作为一个动作或者过滤器的时候,格式和 add_action() 和 add_filter() 略微有些不同。

一般来说,插件使用函数而不是类中的方法作为动作或者过滤器。但是,可能有些时候使用类更适合,所以你要知道如何类在类中将方法注册到钩子。

前面已经提到的注册函数到一个动作钩子的方法:

1
2
3
4
5
<?php
 
add_action( $tag, $function_to_add );
 
?>

<?php add_action( $tag, $function_to_add ); ?>

当在类中将方法作为 $function_to_add 参数时,你必须把 $function_to_add 变成一个数组,其中 & $this 作为第一个参数,方法名作为第二个参数:

1
2
3
4
5
<?php
 
add_action( $tag, array( & $this, $method_to_add ) );
 
?>

<?php add_action( $tag, array( & $this, $method_to_add ) ); ?>

对于过滤器钩子也是一样。一般的将函数添加到一个过滤器钩子类似于:

1
2
3
4
5
<?php
 
add_filter( $tag, $function_to_add );
 
?>

<?php add_filter( $tag, $function_to_add ); ?>

当使用类的方法的时候,要改成:

1
2
3
4
5
<?php
 
add_filter( $tag, array( & $this, $method_to_add ) );
 
?>

<?php add_filter( $tag, array( & $this, $method_to_add ) ); ?>

下面的例子中,创建了一个类,包含一个构造函数,一个作为动作的方法,和一个作为过滤器的方法。add_filters() 方法检查用户是否在浏览单篇 post。如果是 content() 方法附加最后的修改时间到 post 的内容中。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
 
class BOJ_My_Plugin_Loader {
 
/* 类的构造函数 */
 
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();
 
?>

<?php class BOJ_My_Plugin_Loader { /* 类的构造函数 */ 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(); ?>

创建自定义钩子

插件不但可以使用内核的内置钩子,他们也可以创建自定义的钩子供其他插件和模板使用。

插件可以使用4个可用函数中的一个来创建自定义钩子。

  • do_action()
  • do_action_ref_array()
  • apply_filters()
  • apply_filters_ref_array()

前两个创建自定义动作钩子,后两个创建自定义过滤器钩子。

创建自定义钩子的优点

自定义钩子使得你的插件更灵活,使其可以被其他插件扩展,让你可以钩到你的整个插件自己的其他执行过程中。

使用自定义钩子还可以防止用户直接修改你的插件。这一点的重要性在于,当你更新你的插件时,用户不会失去他们修改的内容。

自定义动作钩子实例

在这个例子中,建立了一个插件安装函数。这个函数定义了一个可以更换的常量。别的插件也可以在这个钩子上执行任何代码。因为你在那一点上提供了钩子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
 
add_action( 'plugins_loaded', 'boj_myplugin_setup' );
 
function boj_myplugin_setup() {
 
/* 允许动作最先触发 */
 
do_action( 'boj_myplugin_setup_pre' );
 
/* 检查 root slug 是否定义 */
 
if( !defined( 'BOJ_MYPLUGIN_ROOT_SLUG' ) )
 
define( 'BOJ_MYPLUGIN_ROOT_SLUG', 'articles' );
 
}
 
?>

<?php add_action( ‘plugins_loaded’, ‘boj_myplugin_setup’ ); function boj_myplugin_setup() { /* 允许动作最先触发 */ do_action( ‘boj_myplugin_setup_pre’ ); /* 检查 root slug 是否定义 */ if( !defined( ‘BOJ_MYPLUGIN_ROOT_SLUG’ ) ) define( ‘BOJ_MYPLUGIN_ROOT_SLUG’, ‘articles’ ); } ?>

其他插件或者模板可以钩到 boj_myplugin_setup_pre 来执行任何函数。

比如你想把 BOJ_MYPLUGIN_ROOT_SLUG 常量从 ‘articles’ 改为 ‘papers’ ,你可以建立一个动作并添加到这个钩子中:

1
2
3
4
5
6
7
8
9
10
11
<?php
 
add_action( 'boj_myplugin_setup_pre', 'boj_define_myplugin_constants' );
 
function boj_define_myplugin_constants() {
 
define( 'BOJ_MYPLUGIN_ROOT_SLUG', 'papers' );
 
}
 
?>

<?php add_action( ‘boj_myplugin_setup_pre’, ‘boj_define_myplugin_constants’ ); function boj_define_myplugin_constants() { define( ‘BOJ_MYPLUGIN_ROOT_SLUG’, ‘papers’ ); } ?>

自定义过滤器钩子实例

假设有一个函数显示一个具有一个特定阐述的文章列表。你也许希望其他人能够过滤那个参数或者过滤最终结果。

下面的例子中,写一个函数根据收到的评论条数列取了前10的文章。这个函数让用户可以在从数据库获取数据前过滤这个参数,并且可以过滤最终输出的 HTML 列表。

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
 
function boj_posts_by_comments() {
 
/* 默认参数 */
 
$args = array(
 
'post_type' => 'post',
 
'posts_per_page' => 10,
 
'order' => 'DESC',
 
'oerderby' => 'comment_count'
 
);
 
/* 应用过滤器 */
 
$args = apply_filters( 'boj_posts_by_comments_args', $args );
 
/* 设置输出变量 */
 
$out = '';
 
/* 由给定的参数从数据库查询文章 */
 
$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;
 
}
 
?>

<?php function boj_posts_by_comments() { /* 默认参数 */ $args = array( ‘post_type’ => ‘post’, ‘posts_per_page’ => 10, ‘order’ => ‘DESC’, ‘oerderby’ => ‘comment_count’ ); /* 应用过滤器 */ $args = apply_filters( ‘boj_posts_by_comments_args’, $args ); /* 设置输出变量 */ $out = ”; /* 由给定的参数从数据库查询文章 */ $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; } ?>

要过滤参数,给 boj_posts_by_comments_args 添加一个过滤器。比如你希望把数量从默认的10变成15,添加下面的过滤器:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?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;
 
}
 
?>

<?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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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;
 
}
 
?>

<?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 中所有钩子的列表几乎是不可能的。前面我们讨论了一些常用的动作和过滤器钩子,这一节仅仅讨论一小部分 WordPress 提供的钩子。

WordPress 的新版本会加入新的钩子。最终查看不同版本的内核可以让你找到可以用在插件中的新钩子。

在内核中搜索钩子

作为一个插件开发这,你应该熟悉 WordPress 的内核。寻找钩子能很好的帮助你熟悉 WordPress 内核是如何工作的。没有更好的方法来搞明白 PHP 代码在内核中是如何工作的了。

要寻找钩子的一个简单的方法是在编辑器中打开一个 WordPress 文件然后搜索下面的四个词:

  • do_action
  • do_action_ref_array
  • apply_filters
  • apply_filters_ref_array

这四个函数,每一个都创建一个钩子。

变量钩子

在 WordPress 的内核中找钩子的时候,你会遇到变量钩子。通常钩子的名字是一个静态的字符串。但是变量钩子的名字跟着特定的变量而改变。

一个很好的例子就是 load-$pagenow 动作钩子。变量 $pagenow 根据 WordPress 中当前浏览的 admin 页面而改变。这个钩子如下:

1
2
3
4
5
<?php
 
do_action( "load-$pagenow" );
 
?>

<?php do_action( “load-$pagenow” ); ?>

变量 $pagenow 会变成当前访问页面的名字。例如 new post 页面的钩子是 load-post-new.php,而编辑页面的是 load-post.php。这就使得插件仅仅对特定的 admin 页面执行代码。WordPress 有几个动作和过滤器钩子的名称里面是含有变量的。

通常,这些钩子的名字变成给定的内容,使得插件开发者可以在特定的环境下执行代码。

钩子参考列表

虽然在核心里面搜索钩子有助于你增长经验,但是有时你需要一些网上的参考列表。

WordPress 在 Codex 中有官方的钩子参考列表。

  • http://codex.wordpress.org/Plugin_API/Action_Reference
  • http://codex.wordpress.org/Plugin_API/Filter_Reference

在 Part-18,开发人员工具箱,将介绍更多帮助插件开发者的工具。

总结

钩子是创建 WordPress 插件的最重要的一环了。每次你开始创建一个插件,你都要把你的插件钩到 WordPress 的动作钩子和过滤器钩子中。钩子是插件开发中必不可少的工具。在了解了钩子之后,就是时候开始创建插件了。

阅读该系列的其他文章: 上一篇:WordPress 插件开发教程 Part 2 – WordPress 插件基础 下一篇:WordPress 插件开发教程 Part 4 – 与WordPress整合

赞(0)
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com 特别注意:本站所有转载文章言论不代表本站观点! 本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。未经允许不得转载:IDC资讯中心 » WordPress 插件开发教程 Part 3 – 钩子( Hooks )
分享到: 更多 (0)