WordPress 源码分析-主流程分析

WordPress 源码分析-主流程分析

背景

作为一个优秀的开源项目,wordpress 在全球有着极其广泛的应用。但是由于wordpress框架中,经常使用global进行赋值,导致初次使用它的程序员不能很快对它的整体架构有一个直观的认识。在一位特殊朋友进入一家使用wordpress的公司后,熟悉wordpress比较慢,帮忙做了一次整理;在此,我将整理的过程记录下来。

三个阶段

首先,我们先整体了解下wordpress的三个主要流程:初始化(资源加载)、准备数据和渲染。

我们都知道,wordpress的主入口文件是index.php。在 index.php 中 引入了文件 wp-blog-header.php 。

// wp-blog-header.phpif ( ! isset( $wp_did_header ) ) { $wp_did_header = true; // Load the WordPress library. require_once __DIR__ . '/wp-load.php'; // Set up the WordPress query. wp(); // Load the theme template. require_once ABSPATH . WPINC . '/template-loader.php';}

此文件中,即包含了wordpress加载的三个阶段:

资源加载阶段:

require_once __DIR__ . '/wp-load.php';

数据准备阶段:

// Set up the WordPress query.wp();

渲染阶段:

// Load the theme template. require_once ABSPATH . WPINC . '/template-loader.php';

三个阶段的加载流程图:

wordpress 加载流程

接下来,我们看看每个阶段所做的事情。

资源加载阶段

资源加载

资源加载的引用关系如上图,wp-load.php 引入wp-config.php, wp-config.php 引入 wp-settings.php,核心的加载逻辑在 wp-settings.php中。

资源加载阶段,主要是加载所有依赖的PHP库和文件,包括各种核心类,functions,插件 等等。

需要注意的一点是,在wordpress 未安装成功时,是没有wp-config.php这个文件的。

数据准备阶段

数据准备阶段的入口是 wp-blog-header.php 中的 wp() 函数;wp函数的定义是在functions.php中;最终会调用到 class-wp.php 中的 main() 方法。

main() 方法主要做三件事情:

parse_request(): 解析请求的参数query_posts(): 通过请求的参数查询文章register_globals(): 注册全局数据,以便渲染阶段可以获取数据

我们重点关注第2和第3这两个事情。

查询文章

query_posts() 会调用 class-wp-query.php 中的 query() 方法,query() 方法做两件事情:

init(): 重置所有数据get_posts(): 查询文章

get_posts() 方法会调用当前类的 parse_query() 方法,此方法接近1000行,是整个wordpress中最核心的逻辑。

parse_query() 逻辑复杂,我们只关注两件事情:

解析请求的数据(get/post),构造读取/写入的数据和条件,包括日期数据文章标题附件。。。。其他条件根据url请求参数判断当前请求是哪个页面,例如设置is_home = true,就表示为首页,is_page = true 表示是文章页面;设置当前页面,在数据渲染阶段会用到。

parse_query() 方法查询到的文章,会放到当前类的posts属性和post属性中。如果我们需要debug,可以在此方法的尾部dump查询的sql

var_dump($where . $groupby . $orderby . $limits . $join);

帖子查询

get_posts()执行完毕后,我们也就获取到了想要的文章(根据url参数查询出来的),然后进入注册全局数据阶段,即 register_globals();

register_globals() 方法的代码相对简单,如下:

public function register_globals() { global $wp_query; // Extract updated query vars back into global namespace. foreach ( (array) $wp_query->query_vars as $key => $value ) { $GLOBALS[ $key ] = $value; } $GLOBALS['query_string'] = $this->query_string; $GLOBALS['posts'] = & $wp_query->posts; $GLOBALS['post'] = isset( $wp_query->post ) ? $wp_query->post : null; $GLOBALS['request'] = $wp_query->request; if ( $wp_query->is_single() || $wp_query->is_page() ) { $GLOBALS['more'] = 1; $GLOBALS['single'] = 1; } if ( $wp_query->is_author() && isset( $wp_query->post ) ) { $GLOBALS['authordata'] = get_userdata( $wp_query->post->post_author ); } }

其中主要的为:

$GLOBALS['posts'] = & $wp_query->posts;$GLOBALS['post'] = isset( $wp_query->post ) ? $wp_query->post : null;

这两行代码将上一步查询到的文章和文章的列表,放入了$GLOBALS中,在渲染阶段,会从 $GLOBALS 读取文章内容。

渲染阶段

入口为 template-loader.php 文件,会先判断当前请求的是哪个页面,在数据准备阶段中的 parse_query() 已经计算出来了。核心代码如下:

$tag_templates = array( 'is_embed' => 'get_embed_template', 'is_404' => 'get_404_template', 'is_search' => 'get_search_template', 'is_front_page' => 'get_front_page_template', 'is_home' => 'get_home_template', 'is_privacy_policy' => 'get_privacy_policy_template', 'is_post_type_archive' => 'get_post_type_archive_template', 'is_tax' => 'get_taxonomy_template', 'is_attachment' => 'get_attachment_template', 'is_single' => 'get_single_template', 'is_page' => 'get_page_template', 'is_singular' => 'get_singular_template', 'is_category' => 'get_category_template', 'is_tag' => 'get_tag_template', 'is_author' => 'get_author_template', 'is_date' => 'get_date_template', 'is_archive' => 'get_archive_template', ); $template = false; // Loop through each of the template conditionals, and find the appropriate template file. foreach ( $tag_templates as $tag => $template_getter ) { if ( call_user_func( $tag ) ) { $template = call_user_func( $template_getter ); } if ( $template ) { if ( 'is_attachment' === $tag ) { remove_filter( 'the_content', 'prepend_attachment' ); } break; } } if ( ! $template ) { $template = get_index_template(); }

$tag_templates 数组中的 key, 其实是一个回调函数,全部定义在 wp-includes/query.php中,实际调用的方法是 class-wp-query.php 中对应的方法名。

在计算出当前页面后,就根据当前配置的主题,去加载主题下的模板文件。

在模板文件中,我们就可以获取到之前准备好的数据($GLOBALS中的数据)。以文章内容为例,

在模板中,调用the_content()方法,the_content()方法定义在post-template.php中,the_content() 再调用 get_the_content(), get_the_content() 调用 post.php 中的 get_post(), get_post()定义如下:

function get_post( $post = null, $output = OBJECT, $filter = 'raw' ) { if ( empty( $post ) && isset( $GLOBALS['post'] ) ) { $post = $GLOBALS['post']; } if ( $post instanceof WP_Post ) { $_post = $post; } // 其他代码 ..... return $_post;}

方法的第一行就用 $GLOBALS 中取到了之前准备好的文章数据。

拿到文章数据后,get_the_content() 方法会对文章数据进行组装,返回给 the_content() , 由the_content() 方法进行输出。

总结

到此,我们将这个wordpress的加载流程梳理一下,希望对大家在看源码时有所帮助。源码中还有很多细节需要自行梳理,在梳理时,需要注意的就是global的使用。

WordPress 恶意代码的分析和排查方法

自【网站安全的「灯下黑」隐患:账号安全】一文发表后,明月收到了很多站长们有关 WordPress 站点安全的问题咨询,明月总结分析了一下几乎 90%以上都是“恶意代码”造成的,而给站点带来恶意代码的插件就占了 80%以上(有官网插件、网上流传的插件等等),其他的就是主题了(以破解版、盗版主题为主),其实无论是“恶意代码”还是“后门木马”都是以代码的形式在服务器上传播破坏的,今天明月就跟大家讲讲如何通过对代码的分析来提前找出这些“肮脏”的东西。

function.php 里恶意代码的分析和排查

在 WordPress 里“恶意代码”最容易出现的地方就是主题目录下的 function(s).php 里,一般正规的 function.php 文件结尾都会有类似下面这样的结尾注释:

//全部结束?>

如果你发现没有这个结束注释的时候,那么基本可以判定你的 function.php 文件被篡改过了,就需要好好的检查了,比如下面这些代码行:

function _checkactive_widgetsfunction _get_allwidgets_contfunction striposfunction strriposfunction scandirfunction _getprepare_widgetfunction __popular_postsadd_action("admin_head", "_checkactive_widgets");add_action(“init”, “_getprepare_widget”);_verify_isactivate_widgets_check_isactive_widget_get_allwidgetscont_prepare_widgets__popular_posts

每行是独立存在的,如果你的 functions.php 中有上面任意一段代码,那么你可能就中招了。其中 function、add_action 这类一般都是属于“恶意代码”做“准备活动”的代码。

清除 function.php 恶意病毒代码

清理也很简单,直接在 function.php 文件里面找到上面的类似代码删除即可,但因为一旦感染会导致你 themes 主题目录所有主题都感染,因此你只清除当前使用主题是无效的,你清除后很快就会生成,因此你清除掉一个主题的代码后,把 functions.php 文件设置为 444 权限,然后再清理其他主题即可。至于最后 functions.php 文件的 444 权限是否需要改回去,个人建议 444 挺好挺安全的,等要修改的时候再修改就行了。

当然“恶意代码”并不仅仅局限于 function.php 文件里,明月就碰到过隐藏在 WordPress 根目录 index.php 文件里、wp-include 或者 wp-admin 目录下某个 PHP 文件、cache 这样有读写权限的目录等等,可以说是五花八门、脑洞大开的无所不在,这些其实就是【网站安全的「灯下黑」隐患:账号安全】一文里所讲的账号权限被获取到的后果,这样的结果最难排查,就明月的经验来说大致可以分为两种,分别如下:

手动排查和清除

所谓的“手动”其实就是借助 FTP 软件的“目录对比”、“同步浏览”来排查服务器端的 PHP 文件大小和最后被修改日期来进行分析清理的,因为一般来说 WordPress 站点的 wp-include 和 wp-admin 这两个目录里的文件基本上都是固定不变的,只有在升级、更新 WordPress 的时候才会有部分的覆盖替换,平时来说理论上是不会对这些文件进行修改操作的,通过与本地同样版本的目录文件进行对比可以快速的找出服务器上多出来的文件已经被修改过的文件(文件大小、最后修改日期),以 FileZilla FTP 软件为例,如下图所示:

可以看到通过 FileZilla FTP 软件的“同步浏览”和“目录对比”可以快速的找到被修改过的服务器端的文件,当然也可以找出“多出来”的 PHP 文件(这类基本上直接删除清理就可以了,几乎百分百肯定是恶意代码自建的)这里只是为了告诉大家这种方法的,具体还需要大家自行来理解和试用了,这种方法对于非 WordPress 平台的博客系统来说比较实用( WordPress 有安全插件可以扫描“恶意代码”)。

第三方插件、工具扫描排查

WordPress 明月一直比较推荐 Wordfence Security 插件的,这是一个集防火墙和恶意代码扫描为主的 WordPress 安全插件,由一个大型团队建立和维护,100%专注于 WordPress 安全性。虽然有收费模块,但是我们可以使用免费模块“Scan”来扫描自己 WordPress 站点排查含有“恶意代码”的 PHP 文件,虽然有一定的误报率(主要是一些正版插件、主题的加密组件会被误报),但是查找“恶意代码”绝对是事半功倍的高效率,需要注意的是,这个插件明月不建议常开启着,因为其防火墙和安全防护会对数据库造成一定的负载压力影响网站整体性能的发挥,一般需要的时候启用插件运行“Scan”扫描排查,完成后关闭插件保留以备不时之需就可以了。

Wordfence Security 插件使用注意事项:

1、在开始“Scane”之前,为了保证扫描的稳定执行,最好停用其他所有的插件(仅仅保留 Wordfence Security 插件启用)。

2、因为 Wordfence Security 插件扫描会造成服务器 CPU 负载飙升,建议选择扫描时间段在凌晨以后进行或者是网站访问量最低的时候。

3、我们仅仅是借助 Wordfence Security 的“Scan”恶意代码的规则而已,所以注意记录扫描结果里提示的可疑 php 文件的路径,方便手动先备份后清理删除。

其实还有一个本地工具是个查找 PHP 文件恶意代码的最佳存在,那就是微软的 MSE,我们可以将服务器端的 PHP 文件下载到本地,让微软的 MSE 扫描检测也可以找到“恶意代码”、“木马后门”的。这点而比国内的那些啥“卫士”、“管家”、“XX 霸”之流强的不只是一点点哦。

第三方工具有不少选择,具体如何选择大家根据自己的情况自由选择就是了,这里不得不叹服一下 WordPress 的生态环境真的是最好的, Wordfence Security 这样的插件存在就很能说明这个问题了。

最后明月要再次强调一下, WordPress 丰富的插件、主题是优势也是一把“双刃剑”,大家在选择、使用插件、主题的时候一定要慎重, WordPress 不安全主要的因素就是插件、主题这些不是 WordPress 可以控制的,毕竟都是第三方开发提交的。热衷于体验各种插件和炫酷主题的站长们 Wordfence Security 插件建议永久保留以备不时之需,有好好运营网站打算的站长们建议购买正规渠道的正版主题和插件,免费的、盗版的、破解版的都有“不干净”的隐患。

Wordpress建站教程:分享几种安装谷歌分析代码的方法

继续分享wordpress建站教程。如果是做wordpress外贸建站,有些用户可能需要添加谷歌分析代码(如果不做竞价,其实不加也可以),之前谷歌分析是GA3,现在已经升级为GA4了。

作者:悦然wordpress建站

今天给大家分享一下如何给wordpress网站添加谷歌分析代码,我会同时分享多种方法,总有一个适合你。

步骤一:创建谷歌分析

使用谷歌帐号登陆Google Analytics谷歌分析,然后创建帐户添加网站,新添加的默认为GA4。



填写正确的网址,设置名称,然后点【Create stream】



然后你会得到一个唯一的GA4代ID【假设为G-XXXXXXXXXM】,如上图所示,使用这个ID就可以关联网站,但需要特定的wordpress主题和插件支持。还有一种代码的添加方式,这个方法是通用的,我们可以点击【View tag instructions】。

然后在弹出的窗口中点【Install manually】,这里会得到一段代码,我们只需要把这段代码添加到网站的header或footer中就可以了。

(此处已添加小程序,请到今日头条客户端查看)步骤二:添加谷歌分析代码

这里提供3种添加方式,前两种分别是使用ID和代码来添加,第3种是使用SEO插件来添加。

方法一:使用ID

有部分wordpress主题会集成谷歌分析的设置入口,比如Blocksy就有。

打开Blocksy主题设置【Customizing】-【General】-【Visitor Engagement】,然后可以看到这里有一个【Google Analytics v4】的设置,我们把上一步得到了ID填写进来就可以了,填写之后点Publish发布。

方法二:使用代码

如果你使用的wordpress主题有代码添加功能,类似上面这样,那就可以直接把上一步得到的代码填写进来,然后保存即可。如果你的主题没有代码添加功能,那么也可以使用插件来添加,比如可以安装一个插件WPCode,使用它可以把谷歌分析代码添加到header或footer中,保存即可。

方法三:使用插件

这里所说的使用插件是直接通过插件链接,不需要我们手动去添加代码,常见的谷歌SEO优化插件Rank math或Yoast SEO都支持,下面悦然wordpress建站以Rank math为例讲解。

安装Rank math插件后,打开General Settings,然后选中Analytics,再点Connect Your Rank Math Account按钮,然后根据提示登陆谷歌帐号关联就可以了。

Rank Math插件关联好谷歌帐号后我们可以同时关联谷歌站长平台(Search Console)和谷歌分析,如上图所示,选中帐号中对应的网站就可以了。这里需要注意的是很多用户关联后可能出现谷歌分析没有数据的情况,因为他们可能只打开了上面的第一个开关,没有打开第二个Analytics的开关,如果没有打开请点一下Install analytics code,然后保存设置就可以了。

检查谷歌分析代码是否生效

大家可以根据自己的wordpress网站情况,选择一个适合自己的方式去添加谷歌分析代码。代码安排好后我们可以通过浏览器来检查代码是否生效。

浏览器打开任意页面,然后按F12查看网络加载项,如果发现其中有【js?id=G-XXXXXXXXXX】这样的加载项,那就说明谷歌分析代码安装成功了,我们只需慢慢等它抓取数据即可,一般一天左右就可以在谷歌分析后台看到统计数据了。