WordPress 工作原理

WordPress 所有的前端页面生成都要经过根目录下的 index.php 文件(不是主题根目录),这是通过 Web 服务器的 rewrite 规则实现的。然后通过 index.php 文件一步步引导 WordPress 环境启动,再分析请求 URL 返回相应数据所组成的前台页面。以下将一步步分析源码文件,以此来了解 WordPress 整体框架及工作原理。

1. 加载 index.php

该文件有效源码如下:

  1. <?php
  2. define('WP_USE_THEMES', true);
  3. /** Loads the WordPress Environment and Template */
  4. require('./wp-blog-header.php'); ---把/wp-blog-header.php包含进来
  5. ?>

define('WP_USE_THEMES', true);

定义是否加载主题文件,true为加载。

require('./wp-blog-header.php');

加载 wp-blog-header.php 文件,该文件用于启动 WordPress 环境及模板。

2. 加载 wp-blog-header.php

该文件有效源码如下:

  1. <?php
  2. if ( !isset($wp_did_header) ) {
  3. $wp_did_header = true;
  4. require_once( dirname(__FILE__) . '/wp-load.php' );
  5. wp();
  6. require_once( ABSPATH . WPINC . '/template-loader.php' );
  7. }
  8. ?>

$wp_did_header = true;

对 $wp_did_header 进行赋值,这样如果代码块已经执行过,判断就会失败,代码块就不会再执行。这种做法可以确保wp-blog-header.php文件只执行一次(重复执行的话会出现函数名冲突、变量重置等,WordPress 会精神分裂的!)

require_once( dirname(__FILE__) . '/wp-load.php' );

加载 WP 根目录下 wp-load.php 文件,执行初始化工 作,如初始化常量、环境、加载类库和核心代 码等完成 WordPress 环境启动工作,如加载 wp-includes 目录下 functions.php(函数库)、class-wp.php(类库)、plugin.php(插件)、pomo 目录(语言包)、query.php(数据请求)、theme.php(加载主题文件)、post- template.php(文章模板)、comment.php(评论模板)、rewrite.php(URL重写)等等。

wp();

执行wp()函数,执行内容处理工作,如根据用户的请求调用相关函数获取和处理数据,为前端展示准备数据;

require_once( ABSPATH . WPINC . '/template-loader.php' );

加载根目录绝对路径下 wp-includes 目录中 template-loader.php 文件,执行主题应用工作,如根据用户的请求加载主题模板。

3. 加载 wp-load.php(初始化)

该文件初始化常量(如:定义绝对路径、设定功能文件及内容文件路径等)并加载 wp-config.php 文件(本处不分析wp-config.php 文件不存在的情况),部分核心代码如下:

define( 'ABSPATH', dirname(__FILE__) . '/' );

定义常量ABSPATH为根目录绝对地址;

require_once( ABSPATH . 'wp-config.php' );

加载根目录下wp-config.php文件;

从代码看出,本文件的主要作用就是加载 wp-config.php 文件,故我们可以抽象的将之看作是 wp-load.php 初始化时的第一个小步骤,具体如下:

(1)加载 wp-config.php 文件

该文件主要用于配置 MySQL 数据库通信信息、设定数据库表名前缀、设定密钥、设置语言及文件绝对路径等,部分核心代码如下(为省事就直接在代码后加#然后解释含义了):

define('DB_NAME', 'db_name');

定义数据库名db_name

define('DB_USER', 'db_username');

定义数据库用户名db_username

define('DB_PASSWORD', 'db_password');

定义数据库密码db_password

define('DB_HOST', 'db_host_location');

定义数据库主机地址,如localhost或其他IP

define('DB_CHARSET', 'utf8');

定义数据表默认文字编码,如utf8

$table_prefix = 'wp_';

定义数据库表前缀,一般默认为wp_

define('WPLANG', 'zh_CN');

定义WordPress语言,中文默认zh_CH,使用的汉化语言文件为/wp-content/languages 目录下的zh_CH.mo文件,该文件为二进制,查看具体中文可见 zh_CH.po 文件;

define('WP_DEBUG', false);

设置开发环境DEBUG,默认为false不开启;

require_once(ABSPATH . 'wp-settings.php');

加载根目录下 wp-settings.php 文件;

代码中定义的数据库常量主要用于数据请求时通信数据库,本文件还有个主要作用就是加载了 wp-settings.php 文件,而该文件相当于启动 WordPress 环境的总指挥,下面我们就将该文件作为初始化的第二步来分析。

(2)加载 wp-settings.php 文件

该 文件主要用于创建和定义常见变量、函数和类的库来为 WordPress 运行做准备,也就是说 WordPress 运行过程中使用的大多数变量、函数和类等核心代码都是在这个文件中定义的。这个文件相当于一个总控制器,很多常量定义、函数定义等都是在其他文件中完成, 而该文件的作用就是执行那些文件或执行在那些文件中已经定义好的函数。

该文件源码分析内容较多,详见 WordPress 核心文件 wp-setting.php 源码分析。

4. 执行wp()函数(内容处理)

在这一阶段,调用wp()函数对数据库内容进行查询,并将查询的内容赋值给一些全局变量,方便在模板中使用模板标签获取相应的数据并展示在前端。该函数源码如下:

  1. function wp( $query_vars = '' ) {
  2. global $wp, $wp_query, $wp_the_query;
  3. // 对变量$wp,$wp_query,$wp_the_query进行全局化
  4. $wp->main( $query_vars );
  5. if ( !isset($wp_the_query) )
  6. $wp_the_query = $wp_query;
  7. }

$wp->main()

即调用对象 $wp 的 main() 方法,该对象是 class-wp.php 文件中 WP 类实例化得到的,该类主要用于启动 WordPress 环境,main() 方法源码如下:

  1. function main($query_args = '') {
  2. $this->init();
  3. $this->parse_request($query_args);
  4. $this->send_headers();
  5. $this->query_posts();
  6. $this->handle_404();
  7. $this->register_globals();
  8. do_action_ref_array('wp', array(&$this));
  9. }

该方法主要用于执行该类定义的一些方法,以下我们以每行代码为一个步骤进行分析:

1. 执行init()方法,该方法用于设置当前用户,它调用wp_get_current_user()函数,即设置全局变量$current_user;

2. 执行parse_request()方法,该方法对查询进行解析,具体内容为:使用parse_str()函数将查询解析到数组中,加载rewrite解析URL,基于请求设置查询变量,同时也执行了一系列过滤器和动作用于进一步操作结果;

3. 执行send_headers()方法,该方法用于发送附加的HTTP头信息;

4. 执行query_posts()方法,该方法用于查询操作,具体内容为:基于查询设置循环 LOOP,调用该类中 build_query_string() 方法设置变量属性,执行 $wp_the_query 对象中 query() 方法,其中传入参数为 $this->query_vars,即将用户的请求URL解析到变量后传入 WP_Query 类中 query() 方法继而调用该类中 get_posts() 方法获取相应请求对应的数据内容;

5. 执行handle_404()方法,该方法用于处理404错误,如果程序根据查询URL并未查询到数据则返回404;

6. 执行register_globals()函数,该方法用于注册全局变量;

7. 调用do_action_ref_array()函数(在wp-includes/plugin.php文件中),设置本对象作为wp钩子上的函数的参数;

if ( !isset($wp_the_query) ) $wp_the_query = $wp_query;

判 断 $wp_the_query 是否设置,若未设置将其赋值为 $wp_query,该对象是 query.php 文件中 WP_Query 类实例化得到的,该类作用强大,几乎WP所需要的所有数据信息都是由该类得到的,所以内容的准备工作基本都是这段代码来完成的,该类的具体分析见 WP_Query。

至此,WP根据请求准备相应数据的工作也已经完成,下面就需要加载模板并把这些数据展现到前台去了。

5. 加载 template-loader.php 文件(主题应用)

该文件根据用户 URL 返回加载相应模板,其源码如下:

  1. if ( defined('WP_USE_THEMES') && WP_USE_THEMES )
  2. do_action('template_redirect'); //如果常量 WP_USE_THEMES 存在且值为真,则触发挂载点(动作钩子)template_redirect
  3. if ( is_robots() ) :
  4. do_action('do_robots');
  5. return;
  6. elseif ( is_feed() ) :
  7. do_feed();
  8. return;
  9. elseif ( is_trackback() ) :
  10. include( ABSPATH . 'wp-trackback.php' );
  11. return;
  12. endif;
  13. // 判断函数is_robots(), is_feed() 和 is_trackback()的返回结果,处理 feeds 和 trackbacks,即使没有使用任何主题
  14. if ( defined('WP_USE_THEMES') && WP_USE_THEMES ) :
  15. $template = false;
  16. if ( is_404() && $template = get_404_template() ) :
  17. elseif ( is_search() && $template = get_search_template() ) :
  18. elseif ( is_tax() && $template = get_taxonomy_template() ) :
  19. elseif ( is_front_page() && $template = get_front_page_template() ) :
  20. elseif ( is_home() && $template = get_home_template() ) :
  21. elseif ( is_attachment() && $template = get_attachment_template() ) :
  22. remove_filter('the_content', 'prepend_attachment');
  23. elseif ( is_single() && $template = get_single_template() ) :
  24. elseif ( is_page() && $template = get_page_template() ) :
  25. elseif ( is_category() && $template = get_category_template() ) :
  26. elseif ( is_tag() && $template = get_tag_template() ) :
  27. elseif ( is_author() && $template = get_author_template() ) :
  28. elseif ( is_date() && $template = get_date_template() ) :
  29. elseif ( is_archive() && $template = get_archive_template() ) :
  30. elseif ( is_comments_popup() && $template = get_comments_popup_template() ) :
  31. elseif ( is_paged() && $template = get_paged_template() ) :
  32. else :
  33. $template = get_index_template();
  34. endif;
  35. //见下面解析
  36. if ( $template = apply_filters( 'template_include', $template ) )
  37. include( $template );
  38. return;
  39. endif;
  40. //若 template_include 过滤钩子上有挂载函数,则对 $template 进行应用,最终将内容呈现给用户

解析: 如果常量 WP_USE_THEMES 存在且值为真,则判断页面类型同时给 $template 变量赋相应值;其中,判断页面类型的函数如 is_404() 位于 wp-includes 目录下 query.php 文件,该函数返回对象 $wp_query 中 is_404() 方法,若 is_404() 为 false 则继续往下判断是否是其他页面;若为 true 则给 $template 赋值为 get_404_template(),该函数位于 wp-includes 目录下template.php 文件,它返回 get_query_template(‘404’),而该函数将页面类型传入数组 $templates 并应用调用函数locate_template($templates) 且应用过滤器;locate_template() 函数根据传入数组在主题中查找到相应的文件然后交给load_template() 函数然后使用 require 加载,最终将用户需要的页面呈现出来。