写WordPress代码时需要不停的与hooks(actions and filters)打交道,filter就像茶壶的过滤嘴,茶壶在哪它就在哪,顺序问题不那么重要。而action是一种行为,比如掀起壶盖和盖上壶盖之间就可以放一个action,在这个action里可以放茶叶,不掀起壶盖是不可以放茶叶的,所以actions执行的顺序很重要。

WordPress中的actions

actions可以理解为一组在系统加载到某一时刻要执行的functions集合,使用do_action()添加,例如我们经常用到的get_header()函数,是这样定义的

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

do_action( 'get_header', $name );

如果我们在functions.php中或者插件中写

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信息。

用这个插件查看了安装默认主题时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加载以后。

3条留言

  1. […] 是因为$current_user这个全部变量到init action执行时才完成赋值,既然要读它的内容,至少要等到它的内容准备好后再读取。functions.php的代码先与init action执行,所以在functions.php中直接写global $current_user是无法获取用户信息的。详细信息可以参考《WordPress Actions加载顺序》。 […]

  2. […] 从文章第一节所述就知道,插件和functions.php的代码区别真的不大,只不过插件需要自己的声明,需要一个数据库字段记录哪些插件激活,插件代码执行的位置与functions.php不同。把插件代码拷贝到functions.php中真的能让网站提速很多?还是由此损失掉的可移植性更令人惋惜? […]

评论功能已关闭