过滤器 Filters
- 过滤器钩子和动作钩子有很大的区别。它让你可以控制代码的输出。
apply_filters() 函数
- $tag – 过滤器钩子的名字。
- $value – 传递给任何添加到这个钩子的过滤器的参数。这个函数可以添加任意个额外的 $value 参数传递给过滤器。
- 注意:在写一个过滤器的时候 $value 必须返回给 WordPress。
过滤器钩子的例子:
template_include 是一个过滤器钩子的名字,
$template 是一个可以通过注册到这个过滤器钩子的过滤器改变的文件名。
什么是过滤器?
一个过滤器是一个注册到过滤器钩子的函数。这个函数最少接受一个参数并在执行完代码后返回那个参数。没有过滤器的过滤器钩子没有任何作用。插件开发者可以通过过滤器钩子改变不同的变量 – 从字符串到多位数组。
当一个过滤器钩子被 apply_filters() 函数调用时,所有注册到这个钩子的过滤器都会被执行。要添加一个过滤器,使用
add_filter() 函数。
下面看看 wp_title 过滤器钩子,它是负责页面的
wp_title – 钩子名。
$title – 一个字符串,要过滤并返回给 WordPress 的值。
$sep – 说明
$seplocation – 分隔符的位置。下一个例子中要用到。
现在写一个函数来过滤 $title 的输出 – 在页面的 title 后面附加站点的名字:
boj_add_site_name_to_title() 函数修改 $title 参数并返回给 WordPress。$sep 参数在函数中使用,但没有返回。
过滤器钩子函数
apply_filters_ref_array
类似于动作钩子里面的do_action_ref_array) 函数。
假设你要执行一个一般的 WordPress 没有的复杂的数据库查询来加载首页的 posts。WordPress 提供了一个叫做 posts_results 的过滤器钩子使得你可以改变它。下面是 WordPress 核心中的代码:
posts = apply_filters_ref_array(
'posts_results', array( $this -> posts, & $this )
);
?>
这个过滤器钩子向所有注册到它的过滤器传递一个 post 对象的数组。下面的例子,你完全重写这个 post 对象的数组并用自定义的内容代替。默认情况下,WordPress 查询 post 类型的 posts。下面改成查询 page 类型的 psots 并显示在首页。
这段代码使用了 wpdb 类,在 part-6 “插件安全” 中将详细介绍。
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
这和前面的 remove_action 类似。
下面看看 WordPress 定义在 wp-includes/default-filters.php 页面中的默认的过滤器。其中一个有意思的过滤器叫做 wpautop(),它将双换行转换成 HTML 的
</p>。这也就是我们在 HTML 发布内容时,直接回车便可在最终前端显示的时候换行的原因。它在核心代码中的几个钩子中都执行。下面是其中一个实例:
这将 wpautop() 过滤器应用到 post 的内容中,把每个换行都转换成段落(
)。但是也许有的客户不希望他的内容自动变成段落。那么你就可以使用 remove_filter() 函数从 the_content 钩子中删除这个过滤器。
因为在 add_filter 的时候没有定义优先级,所以这里也不需要。
remove_all_filters
和前面的remove_all_actions类似。
has_filter
和前面的 has_action 类似。
current_filter
同样类似于 did_action。不过它不仅仅对过滤器钩子有效,同样对动作钩子也有效,所以它返回的是当前的 action 或者 filter 钩子。这个函数在你对多个钩子使用单个函数,但是需要依赖不同的钩子执行不同的内容的时候非常的有用。例如,客户希望在 post 标题 和内容中限制一些内容,但是这两个限制的minganci的集合是不同的。使用 current_filter() 函数来根据钩子设置不同的minganci表就可以实现用一个函数同时过滤 the_content 和 the_title。使用下面的代码,你可以把minganci替换成**。
快速返回函数
你经常需要写一个函数返回一个常用的值给一个过滤器钩子,例如 true,false,或者一个空数组。你甚至尝试使用 PHP 的 create_function() 函数来快速返回一个值。
WordPress 提供几个函数处理这种情况。
下面是例子禁用了 user contact 方法 – 在 WordPress 的个人用户管理页面中的一系列 <input>。要禁用这些表单项,你需要返回一个空数组。通常,你必须添加过滤器钩子调用和函数。
写这样的代码一两次并没什么。但是写一个返回空数组的函数太傻了。WordPress 使之简单化了。因为要禁用这些表单项,你只需要使用 WordPress 的 __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。
term_id;
/* 从数据库查询相同分类的 posts */
$loop=newWP_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.='
';
/* 重置 query */
wp_reset_query();
}
/* 返回 content */
$return $content;
}
?>
the_title
文章的标题几乎和内容一样重要,所以 the_title 也是一个常用的过滤器钩子。你可以使用这个钩子添加信息,或者直接重写。
应用给 the_title 钩子的一个有用的过滤器就是去除标题中 HTML 标签的过滤器函数。用户有时会添加一些标签到标题中,这可能会破坏正常的格式。使用下面的代码,你可以过滤掉所有用户可能添加到标签。
同样 comment_text 也很常用。
下面的例子中,检查一条评论是否是网站的注册用户发表的。如果是注册用户,你可以附加一段用户信息的说明( 详见:Part-8,”用户” )
user_id > 0 ) {
/* 新建一个用户对象 */
$user=newWP_User( $comment-> user_id );
/* 如果用户有一个角色,添加到评论内容中 */
if(is_array($user-> roles ) )
$text.='User Role: '.$user-> roles[0]. '
';
}
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。
get_queried_object_id();
/* 获得 post 的分类 */
$terms= get_the_terms($post_id,'category');
/* 循环分类,添加别名作为文件名的一部分 */
$template=array();
foreach($termsas$term )
$templates[] ="single-category-{$term->slug}.php";
/* 检查模板是否存在 */
$locate= locate_template($templates);
/* 如果找到,让它成为新模板 */
if( !empty($locate) )
$template=$locate;
}
/* 换回模板文件名 */
return$template;
}
?>
使用一个类中的钩子
前面已经讲了许多通过 PHP 函数来使用动作钩子和过滤器钩子的例子。在类中添加一个方法作为一个动作或者过滤器的时候,格式和 add_action() 和 add_filter() 略微有些不同。
一般来说,插件使用函数而不是类中的方法作为动作或者过滤器。但是,可能有些时候使用类更适合,所以你要知道如何类在类中将方法注册到钩子。
前面已经提到的注册函数到一个动作钩子的方法:
当在类中将方法作为 $function_to_add 参数时,你必须把 $function_to_add 变成一个数组,其中 & $this 作为第一个参数,方法名作为第二个参数:
对于过滤器钩子也是一样。一般的将函数添加到一个过滤器钩子类似于:
当使用类的方法的时候,要改成:
下面的例子中,创建了一个类,包含一个构造函数,一个作为动作的方法,和一个作为过滤器的方法。add_filters() 方法检查用户是否在浏览单篇 post。如果是 content() 方法附加最后的修改时间到 post 的内容中
最后修改于:'.$date.'';
return$content;
}
}
$boj_myplugin_loader=newBOJ_My_Plugin_Loader();
?>
创建自定义钩子
插件不但可以使用内核的内置钩子,他们也可以创建自定义的钩子供其他插件和模板使用。
插件可以使用4个可用函数中的一个来创建自定义钩子。
- do_action()
- do_action_ref_array()
- apply_filters()
- apply_filters_ref_array()
前两个创建自定义动作钩子,后两个创建自定义过滤器钩子。
创建自定义钩子的优点
自定义钩子使得你的插件更灵活,使其可以被其他插件扩展,让你可以钩到你的整个插件自己的其他执行过程中。
使用自定义钩子还可以防止用户直接修改你的插件。这一点的重要性在于,当你更新你的插件时,用户不会失去他们修改的内容。
自定义动作钩子实例
在这个例子中,建立了一个插件安装函数。这个函数定义了一个可以更换的常量。别的插件也可以在这个钩子上执行任何代码。因为你在那一点上提供了钩子。
其他插件或者模板可以钩到 boj_myplugin_setup_pre 来执行任何函数。
比如你想把 BOJ_MYPLUGIN_ROOT_SLUG 常量从 ‘articles’ 改为 ‘papers’ ,你可以建立一个动作并添加到这个钩子中:
自定义过滤器钩子实例
假设有一个函数显示一个具有一个特定阐述的文章列表。你也许希望其他人能够过滤那个参数或者过滤最终结果。
下面的例子中,写一个函数根据收到的评论条数列取了前10的文章。这个函数让用户可以在从数据库获取数据前过滤这个参数,并且可以过滤最终输出的 HTML 列表
'post',
'posts_per_page'=> 10,
'order'=>'DESC',
'oerderby'=>'comment_count'
);
/* 应用过滤器 */
$args= apply_filters('boj_posts_by_comments_args',$args);
/* 设置输出变量 */
$out='';
/* 由给定的参数从数据库查询文章 */
$loop = newWP_Query( $args );
/* 检查是否返回结果 */
if($loop-> have_posts() ) {
$out.='';
while($loop-> have_posts() ) {
$loop-> the_post();
$out.= the_title('- ','
', false );
}
$out.='
';
}
$out= apply_filters('boj_posts_by_comments',$out);
echo $out;
}
?>
要过滤参数,给 boj_posts_by_comments_args 添加一个过滤器。比如你希望把数量从默认的10变成15,添加下面的过滤器:
要过滤最后的 HTML 输出,添加一个过滤器到 boj_posts_by_comments。比如你想把 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 页面而改变。这个钩子如下:
变量 $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 的动作钩子和过滤器钩子中。钩子是插件开发中必不可少的工具。在了解了钩子之后,就是时候开始创建插件了。