写WordPress代码时需要不停的与hooks(actions and filters)打交道,filter就像茶壶的过滤嘴,茶壶在哪它就在哪,顺序问题不那么重要。而action是一种行为,比如掀起壶盖和盖上壶盖之间 就可以放一个action,在这个action里可以放茶叶,不掀起壶盖是不可以放茶叶的,所以actions执行的顺序很重要。
钩子Hooks
钩子是让一段代码与另一段代码做交互的方法。它们是插件、主题与WordPress内核做交互的基础,当然WordPress内核里也广泛使用了。
钩子有两种:Actions和Filters。使用它们,你必须写一个回调函数,然后将它注册到WordPress关联到特定action或filter。
Filters让你可以在WordPress运行的时候修改一段数据的值。传入给回调函数的变量修改后被返回。它们是独立工作的,不会影响到函数外部的东西。
Actions则相反,允许你增加或修改WordPress的运行。回调函数会运行在WordPress运行到特定点的时候,可以做一些任务,比如:输出显示给用户、插入数据到数据库。
WordPress提供了许多钩子供你使用,你也可以自定义一些供其他开发者来修改你的插件或主题。
Actions
Actions钩子提供了函数一种方法可以让其他函数挂靠上来,然后额外的代码就可以在WordPress内核、插件、主题运行到特定点的时候执行了。
它们通过调用add_action()函数来工作,传入两个参数:你要挂靠的钩子名称、要运行的回调函数。比如:
<?php add_action( 'init', 'do_some_stuff' ); ?>
以上语句中,自定义do_some_stuff()函数会在WordPress运行init操作的时候被调用。
可以去代码参考的hook部分查看更多可用的action。如你经验丰富,对WordPress内核十分熟悉,可以直接去源代码里找合适的action。
示例
如果你想为Loop修改MySQL查询语句,你可以挂靠到pre_get_posts操作上。比如,你可以将指定CPT包含到搜索中:
<?php
function search_filter( $query ) {
if ( ! is_admin() && $query->is_main_query() ) {
if ( $query->is_search ) {
$query->set( 'post_type', array( 'post', 'movie' ) );
}
}
}
add_action( 'pre_get_posts', 'search_filter' );
?>
还有比如,你想添加标签到HTML的<head>
里,你可以挂靠到wp_head操作上。
<?php
function prevent_google_maps_resize() {
echo '<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />';
}
add_action( 'wp_head', 'prevent_google_maps_resize' );
?>
非常重要的一个 url地址, 关于wordpress的
https://github.com/66beta/plugin-handbook-chs/blob/master/4.Hooks.md
WordPress中的actions
actions可以理解为一组在系统加载到某一时刻要执行的functions集合,使用do_action()添加,例如我们经常用到的get_header()函数,是这样定义的
1 2 3 4 5 6 7 8 9 10 11 12 13 | function get_header( $name = null ) { do_action( 'get_header' , $name ); $templates = array (); if ( isset( $name ) ) $templates [] = "header-{$name}.php" ; $templates [] = 'header.php' ; // Backward compat code will be removed in a future release if ( '' == locate_template( $templates , true)) load_template( ABSPATH . WPINC . '/theme-compat/header.php' ); } |
函数第二行用do_action()注册了一个action,叫做get_header
1 | do_action( 'get_header' , $name ); |
如果我们在functions.php中或者插件中写
1 | add_action( 'get_header' , 'my_fun' ) |
my_fun()这个函数就会在do_action的位置执行,而不是在functions.php运行的位置执行。
Actions的执行顺序
了解WordPress中actions的执行顺序,可以知晓在这个action执行时,是否已经具备某些资源,例如登陆用户信心、例如插件API等。
要了解Actions的执行顺序,可以安装一个开发人员的插件WordPress Hook Sniffer,该插件不仅能告知actions的加载顺序,还能知道当前页面add_action操作有哪些,remove_action操作有哪些,还有filters信息。
这个simply-show-hooks插件也很好, 用来显示hook。
用这个插件查看了安装默认主题时action的执行顺序,捡了一些重要的记录下来,红色字体标记了一下比较重要的阶段。
muplugins_loaded (最先加载的action)
registered_taxonomy
registered_post_type
(加载所有激活的插件的文件,这是插件代码被执行的位置)
plugins_loaded
sanitize_comment_cookies
setup_theme
(载入当前主题的functions.php,functions.php中没有用add_filter或add_action添加的函数在这里被执行)
after_setup_theme (这个钩子看着眼熟吧,默认主题开头就有)
auth_cookie_malformed
auth_cookie_valid
set_current_user (这里执行了wp_set_current_user()函数,全局变量$current_user产生)
init
widgets_init
register_sidebar
wp_register_sidebar_widget
wp_default_scripts
wp_default_styles
admin_bar_init
add_admin_bar_menus
wp_loaded
parse_request
send_headers
parse_query
pre_get_posts
posts_selection
wp
template_redirect
加载激活的主题的模板(例如index.php、page.php等)
get_header
wp_head
wp_enqueue_scripts
wp_print_styles
wp_print_scripts
wp_print_scripts
get_footer
wp_footer
从上面的列表中可以看出一些问题:
- 插件文件比主题的functions.php加载更早
- 插件加载时,wp_set_current_user()尚未执行,因此在插件文件的body中无法直接获取用户信息
- init和after_setup_theme的区别是,后者执行时尚未调用wp_set_current_user(),没有授权用户信息
- 加 载主题模板文件发生在最后阶段,此阶段中不管是插件的代码还是functions.php中的代码都已执行,这样我们就不奇怪为什么在 single.php中调用query_posts()会增加查询次数,query_posts()大约在pre_get_posts的位置就执行完了, 等程序执行到single.php时,如果调用query_posts,只能推翻前面的结果重新查一遍。我们还能看出,避免这个问题的方法就是在 functions.php中使用filters函数(posts_join, posts_groupby等)更改query_posts的查询参数,因为functions.php早于query_posts执行,方法可以参考《自定义WordPress查询的4种方法》中的第三种方法。
理解万岁
与其枯燥的去记忆什么时候该用哪个action,不如理解一下WordPress的启动过程,了解actions加载的顺序,记忆几个比较重要的过程,例如哪些actions发生在插件代码执行以后,哪些actions发生在functions.php加载以后。