wordpress code analysis
wordpress code analysis
概念
主题(themes)提供了一种页面显示的定制机制,是指WordPress对于主要的显示页面,可以让二次开发者通过主题去定制,主题可以包含任意数量的可定制页面,如果某个页面在当前用户主题中没有,就用会使用默认主题中的相应页面。
插件(plugins)提供了针对二次开发者的博客功能可扩充机制,是WordPress在处理流程的各个节点,定义了大量的标签,可以让二次开发者通过add_action调用的方式,对某些标签添加处理函数,从而在这些标签执行时会执行二次开发者嵌入的处理函数,完成插件功能。另外也可以通过调用add_submenu_page添加菜单项,在添加的菜单项被点击时触发相应的插件处理函数。
过滤器(filter)提供了针对特定标签的字符串过滤修改机制,通过add_filter添加指定标签的过滤规则,在需要使用的地方通过apply_filters来应用指定为标签注册的过滤函数,完成字符串的过滤修改。
WordPress使用了大量的全局变量,很多函数都有定义,因为php是单线程的,所以全局变量之间不用考虑互斥处理。
tinymce是WordPress使用的一个开源web在线编辑器,具有所见即所得的特点。它没有使用常用的execCommand来完成格式修改,而是采用获取选中的html、修改样式、替换掉选择的html的步骤方式来实现的。
WordPrees没有依赖apache自带的重写功能,而是自己实现了一套重写机制,对请求的网页参数按照配置的格式进行重写,主要在类WP_Rewrite进行处理。
目录结构
二、目录结构
我们先看一下WordPress的目录结构:
├─wp-admin //配置管理页面目录
│ ├─images //配置管理页面用到的图片目录
│ └─import //从其他不同站点导入博客文章的处理代码目录
├─wp-content //主题和插件目录
│ ├─plugins //各个插件目录
│ │ └─akismet
│ └─themes //各个主题目录
│ ├─classic
│ └─default
│ └─images
└─wp-includes //博客主要处理逻辑代码目录
├─images //表情图片目录
│ └─smilies
└─js //各种Javascript特效目录
└─tinymce //web在线编辑器
表结构
mysql> show tables;
+---------------------+
| Tables_in_wordpress |
+---------------------+
| wp_categories | //存储文章的分类信息
| wp_comments | //存储文章的回复信息
| wp_linkcategories | //存储链接的分类信息
| wp_links | //存储博客链接
| wp_options | //存储博客的配置选项
| wp_post2cat | //存储从文章到分类的对应关系
| wp_postmeta | //存储文章的特有数据
| wp_posts | //存储博客文章
| wp_usermeta | //存储用户的特有数据
| wp_users | //存储用户配置信息
+---------------------+
10 rows in set (0.00 sec)
处理流程
入口index.php引导WordPress环境启动,再分析请求URL返回相应数据所组成的前台页面
加载index.php文件
该文件有效源码如下:
define('WP_USE_THEMES', true);
# 定义是否加载主题文件,true为加载;
require('./wp-blog-header.php');
# 加载wp-blog-header.php文件,该文件用于启动WordPress环境及模板;
加载wp-blog-header.php文件
该文件有效源码如下:
if ( !isset($wp_did_header) ) {
# 判断$wp_did_header变量是否已经设置,如果未设置则执行代码块;
$wp_did_header = true;
# 见解析1;
require_once( dirname(__FILE__) . '/wp-load.php' );
# 见解析2;
wp();
# 见解析3;
require_once( ABSPATH . WPINC . '/template-loader.php' );
# 见解析4;
}
if ( !isset($wp_did_header) ) {
# 判断$wp_did_header变量是否已经设置,如果未设置则执行代码块;
$wp_did_header = true;
# 见解析1;
require_once( dirname(__FILE__) . '/wp-load.php' );
# 见解析2;
wp();
# 见解析3;
require_once( ABSPATH . WPINC . '/template-loader.php' );
# 见解析4;
}
解析1:对$wp_did_header进行赋值,这样如果代码块已经执行过,判断就会失败,代码块就不会再执行。这种做法可以确保wp-blog-header.php文件只执行一次(重复执行的话会出现函数名冲突、变量重置等,WordPress会精神分裂的!);
解析2:加载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重写)等等。
解析3:执行wp()函数,执行内容处理工作,如根据用户的请求调用相关函数获取和处理数据,为前端展示准备数据;
解析4:加载根目录绝对路径下wp-includes目录中template-loader.php文件,执行主题应用工作,如根据用户的请求加载主题模板。
WordPress之所以能将用户请求的页面生成出来,都是最后这三行核心代码起的作用。wp-load.php会完成页面生成所需要的所有环境、变量、API等,相当于做了好准备工作;wp()函数根据用户请求的URL从数据库中取出相应的数据内容备用;template-loader.php把已经准备好的内容用主题所设定的样式展现方式给拼接出来。这三项工作完成,就可以将用户请求的页面展现出来了。我们姑且将这三项工作也认定为三个大步骤,以下将重点分析。
加载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初始化时的第一个小步骤,具体如下:
加载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环境的总指挥,下面我们就将该文件作为初始化的第二步来分析。
加载wp-settings.php文件
该文件主要用于创建和定义常见变量、函数和类的库来为WordPress运行做准备,也就是说WordPress运行过程中使用的大多数变量、函数和类等核心代码都是在这个文件中定义的。这个文件相当于一个总控制器,很多常量定义、函数定义等都是在其他文件中完成,而该文件的作用就是执行那些文件或执行在那些文件中已经定义好的函数。
该文件源码分析内容较多
define( 'WPINC', 'wp-includes' );
# 定义常量WPINC,为后面的路径使用做准备;
require( ABSPATH . WPINC . '/load.php' );
# 加载wp-includes目录(以下不特殊说明则文件目录均为wp-includes)下load.php文件,该文件无执行代码,主要用于定义一些WP可能用到的一些函数
主要函数详解见WordPress初始化核心文件load.php源码分析;
require( ABSPATH . WPINC . '/default-constants.php' );
# 加载目录下default-constants.php文件,该文件无执行代码,主要用于定义一些WP可能用到的函数,这些函数定义常量如插件目录、主题目录、内存、COOKIE、SSL等;
主要函数详解见WordPress初始化核心文件default-constants.php源码分析
require( ABSPATH . WPINC . '/functions.php' );
# 加载functions.php文件,该文件无执行代码(除加载option.php文件外),定义WP主要的API(API是一组函数,通常以库的形式存在供用户调用)
主要函数详解见“WordPress初始化核心文件functions.php源码分析”
require( ABSPATH . WPINC . '/option.php' );
# 加载option.php文件,该文件提供跟option后台及默认选项相关的API,无执行代码
主要函数详解见“WordPress初始化核心文件option.php源码分析”
require( ABSPATH . WPINC . '/class-wp.php' );
# 定义WP类,无执行代码,定义类WP和WP_MatchesMapRegex,WP类中部分方法功能如设定查询列表、解析查询parse_request()、创建主循环query_posts()、处理404请求、定义main()方法等
其中,main()方法详解见“WordPress核心类WP内main()方法源码分析”
require( ABSPATH . WPINC . '/plugin.php' );
# 定义插件API,无执行代码,主要用于创建动作、过滤、挂载函数等
主要函数详解见“WordPress初始化核心文件plugin.php源码分析”
require( ABSPATH . WPINC . '/pomo/mo.php' );
# 加载本路径下文件translations.php和streams.php并定义语言处理类MO,无执行代码,MO相关文件均用于程序语言翻译本处不再详解;
require_wp_db();
# 执行在load.php中定义的该函数,加载数据库类文件;
wp_set_wpdb_vars()
# 执行在load.php中定义的该函数,设置数据库前缀等;
wp_start_object_cache()
# 执行在load.php中定义的该函数,启动WP缓存等;
require( ABSPATH . WPINC . '/default-filters.php' );
# 加载default-filters.php文件,WP所有的大多数过滤钩子和动作钩子都是通过本文件设置,即添加挂载函数;
require_once( ABSPATH . WPINC . '/l10n.php' );
# 加载l10n.php文件,定义语言翻译API,无执行代码;
require( ABSPATH . WPINC . '/formatting.php' );
# 加载formatting.php文件,定义WP大多数用于格式化输出的函数,无执行代码;
require( ABSPATH . WPINC . '/capabilities.php' );
# 加载capabilities.php文件,定义WP角色和权限相当类和函数,无执行代码;
require( ABSPATH . WPINC . '/query.php' );
# 加载query.php文件,定义WP查询请求API(如文章或页面请求内容、评论等),无执行代码,它定义一个类WP_Query,并定义可以将其实例化的函数query_posts,将其实例化为对象$wp_query,同时定义了很多可以从该对象中取值的API;该类功能强大,它可以用来判断当前页面类型(如文章页、标签页、作者页、首页等)、获取文章内容、获取最新的N篇文章等;该类相对较为重要
该类部分重要属性和方法详解见“WordPress初始化核心文件query.php源码分析”
require( ABSPATH . WPINC . '/theme.php' );
# 加载theme.php文件,定义主题、模板和样式表相关函数,无执行代码,如获取主题及样式表、获取主题路径及URL、更换主题、主题样式展示等;
require( ABSPATH . WPINC . '/class-wp-theme.php' );
# 加载class-wp-theme.php文件,定义主题类WP_Theme,无执行代码,该类主要功能如获取样式表、获取模板路径、获取样式表URL等;
require( ABSPATH . WPINC . '/template.php' );
# 定义各类型页面模板API,无执行代码,该文件大多函数名形如get_XXX_template(),该函数会将XXX作为参数传入get_query_template()并返回相应值,这些模板如:404页面、TAG页面、首页、时间分类页等;
require( ABSPATH . WPINC . '/user.php' );
# 定义跟用户相当API及类,如账户密码cookie用户文章数、删除用户等;
require( ABSPATH . WPINC . '/meta.php' );
# 定义一组元数据 API,这些 API 用于获取和操作 WP 中各种对象类型的元数据(Title、Keywords、Description),一个对象的元数据简单的表示为键值对,对象可能包含多个元数据实体,他们有相同的键但不同的值,无执行代码;
require( ABSPATH . WPINC . '/general-template.php' );
# 定义常用的模板标签,它们可以在模板中任意使用,无可执行代码,如get_header()、get_footer()、get_sidebar()、wp_title()等;
require( ABSPATH . WPINC . '/link-template.php' );
# 定义大多数WP使用中可能用到的URL,如文章链接、按时间分类页面链接、所有feed相当链接、所有edit时链接、下篇文章链接等等,无可执行代码;
require( ABSPATH . WPINC . '/author-template.php' );
# 定义一组模板中处理作者的函数,如获取文章作者、最后修改人、作者链接、作者所有文章等,无可执行代码;
require( ABSPATH . WPINC . '/post.php' );
# 定义可修改文章信息等的API,无可执行代码,如文章状态、增加文章meta、更换文章meta、删除文章等;
require( ABSPATH . WPINC . '/post-template.php' );
# 定义一组跟文章缩略图相关API;
require( ABSPATH . WPINC . '/category.php' );
# 定义一组 WP 的目录标签API,无可执行代码,如获取目录ID、获取目录名、获取标签;
require( ABSPATH . WPINC . '/category-template.php' );
# 定义一组目录的模板标签和 API,无可执行代码,如处理目录排序、标签云、目录结构等;
require( ABSPATH . WPINC . '/comment.php' );
# 定义一组跟WP的评论信息相关的 API,无可执行代码,如检查评论必选项、获取评论内容及状态、获取评论总数等;
require( ABSPATH . WPINC . '/comment-template.php' );
# 定义一组评论的模板标签,旨在 Loop 中有用,无可执行代码,如获取评论者及其评论内容、获取评论人email、URL、IP等;
require( ABSPATH . WPINC . '/rewrite.php' );
# 定义一组WP的URL重写的API和类,无执行代码,如标签页URL自定义、首页URL、目录页URL等;
require( ABSPATH . WPINC . '/feed.php' );
# 定义一组 WP 的 Feed API,其中大部分只在 Loop 中使用,无可执行代码;
require( ABSPATH . WPINC . '/bookmark.php' );
# 定义一组 WP 的友情链接/书签 API,无可执行代码;
require( ABSPATH . WPINC . '/cron.php' );
# 定义一组 WP 的 CRON(定时任务) API,用于进行事件调度,无可执行代码;
if ( ! defined( 'WP_INSTALLING' ) || 'wp-activate.php' === $pagenow ) {
if ( TEMPLATEPATH !== STYLESHEETPATH && file_exists( STYLESHEETPATH . '/functions.php' ) )
include( STYLESHEETPATH . '/functions.php' );
if ( file_exists( TEMPLATEPATH . '/functions.php' ) )
include( TEMPLATEPATH . '/functions.php' );
}
# 该段代码用于加载当前主题下functions.php文件,从代码顺序来看,主题环境加载是在WordPress所有核心文件加载完且初始内容准备完毕后加载;
执行wp()函数(内容处理)
在这一阶段,调用wp()函数对数据库内容进行查询,并将查询的内容赋值给一些全局变量,方便在模板中使用模板标签获取相应的数据并展示在前端。该函数源码如下:
function wp( $query_vars = '' ) {
global $wp, $wp_query, $wp_the_query;
# 对变量$wp,$wp_query,$wp_the_query进行全局化;
$wp->main( $query_vars );
# 见解析1;
if ( !isset($wp_the_query) )
$wp_the_query = $wp_query;
# 见解析2;
}
解析1:调用$wp->main(),即调用对象$wp的main()方法,该对象是class-wp.php文件中WP类实例化得到的,该类主要用于启动WordPress环境,main()方法源码分析详见“WordPress核心类WP内main()方法源码分析”;
解析2:判断$wp_the_query是否设置,若未设置将其赋值为$wp_query,该对象是query.php文件中WP_Query类实例化得到的,该类作用强大,几乎WP所需要的所有数据信息都是由该类得到的,所以内容的准备工作基本都是这段代码来完成的,该类的具体分析见“”;
至此,WP根据请求准备相应数据的工作也已经完成,下面就需要加载模板并把这些数据展现到前台去了。
加载template-loader.php文件(主题应用)
该文件根据用户URL返回加载相应模板,其源码如下:
if ( defined('WP_USE_THEMES') && WP_USE_THEMES )
do_action('template_redirect');
# 如果常量WP_USE_THEMES存在且值为真,则触发挂载点(动作钩子)template_redirect;
if ( is_robots() ) :
do_action('do_robots');
return;
elseif ( is_feed() ) :
do_feed();
return;
elseif ( is_trackback() ) :
include( ABSPATH . 'wp-trackback.php' );
return;
endif;
# 判断函数is_robots(), is_feed() 和 is_trackback()的返回结果,处理 feeds 和 trackbacks,即使没有使用任何主题;
if ( defined('WP_USE_THEMES') && WP_USE_THEMES ) :
$template = false;
if ( is_404() && $template = get_404_template() ) :
elseif ( is_search() && $template = get_search_template() ) :
elseif ( is_tax() && $template = get_taxonomy_template() ) :
elseif ( is_front_page() && $template = get_front_page_template() ) :
elseif ( is_home() && $template = get_home_template() ) :
elseif ( is_attachment() && $template = get_attachment_template() ) :
remove_filter('the_content', 'prepend_attachment');
elseif ( is_single() && $template = get_single_template() ) :
elseif ( is_page() && $template = get_page_template() ) :
elseif ( is_category() && $template = get_category_template() ) :
elseif ( is_tag() && $template = get_tag_template() ) :
elseif ( is_author() && $template = get_author_template() ) :
elseif ( is_date() && $template = get_date_template() ) :
elseif ( is_archive() && $template = get_archive_template() ) :
elseif ( is_comments_popup() && $template = get_comments_popup_template() ) :
elseif ( is_paged() && $template = get_paged_template() ) :
else :
$template = get_index_template();
endif;
# 见解析1;
if ( $template = apply_filters( 'template_include', $template ) )
include( $template );
return;
endif;
# 若template_include过滤钩子上有挂载函数,则对$template进行应用,最终将内容呈现给用户;
if ( defined('WP_USE_THEMES') && WP_USE_THEMES )
do_action('template_redirect');
# 如果常量WP_USE_THEMES存在且值为真,则触发挂载点(动作钩子)template_redirect;
if ( is_robots() ) :
do_action('do_robots');
return;
elseif ( is_feed() ) :
do_feed();
return;
elseif ( is_trackback() ) :
include( ABSPATH . 'wp-trackback.php' );
return;
endif;
# 判断函数is_robots(), is_feed() 和 is_trackback()的返回结果,处理 feeds 和 trackbacks,即使没有使用任何主题;
if ( defined('WP_USE_THEMES') && WP_USE_THEMES ) :
$template = false;
if ( is_404() && $template = get_404_template() ) :
elseif ( is_search() && $template = get_search_template() ) :
elseif ( is_tax() && $template = get_taxonomy_template() ) :
elseif ( is_front_page() && $template = get_front_page_template() ) :
elseif ( is_home() && $template = get_home_template() ) :
elseif ( is_attachment() && $template = get_attachment_template() ) :
remove_filter('the_content', 'prepend_attachment');
elseif ( is_single() && $template = get_single_template() ) :
elseif ( is_page() && $template = get_page_template() ) :
elseif ( is_category() && $template = get_category_template() ) :
elseif ( is_tag() && $template = get_tag_template() ) :
elseif ( is_author() && $template = get_author_template() ) :
elseif ( is_date() && $template = get_date_template() ) :
elseif ( is_archive() && $template = get_archive_template() ) :
elseif ( is_comments_popup() && $template = get_comments_popup_template() ) :
elseif ( is_paged() && $template = get_paged_template() ) :
else :
$template = get_index_template();
endif;
# 见解析1;
if ( $template = apply_filters( 'template_include', $template ) )
include( $template );
return;
endif;
# 若template_include过滤钩子上有挂载函数,则对$template进行应用,最终将内容呈现给用户;
- 解析1:如果常量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加载,最终将用户需要的页面呈现出来;