大家好,我是你们今天的WordPress重写规则特邀讲师。准备好一起深入挖掘 add_rewrite_rule()
这个小精灵,看看它是怎么把新规则偷偷塞进WordPress的重写王国了吗?今天咱们就来一场代码级的探险,保证让大家理解得透透的。
第一幕: 重写规则的前世今生 (以及为什么要重写)
在开始解剖 add_rewrite_rule()
之前,我们先简单回顾一下重写规则存在的意义。想象一下,你希望用户访问http://example.com/movies/action/terminator
,但实际上这个页面并不真实存在。你希望WordPress能够把这个URL“重写”成一个内部的请求,比如http://example.com/index.php?post_type=movie&genre=action&title=terminator
,然后WordPress再根据这些参数去动态生成页面。
这就是重写规则的魔力所在:它允许我们创建更友好的URL,同时保持内部逻辑的清晰。
第二幕: add_rewrite_rule()
粉墨登场
add_rewrite_rule()
函数是WordPress提供的一个方便的接口,用于向重写规则列表中添加新的规则。它的基本语法如下:
add_rewrite_rule( string $regex, string $redirect, string $priority = 'top' )
$regex
:这是一个正则表达式,用于匹配传入的URL。$redirect
:这是匹配成功后,URL应该被重写成的内部地址。$priority
:可选参数,指定规则的优先级,可以是'top'
或'bottom'
。'top'
表示规则会被添加到规则列表的顶部,'bottom'
表示添加到底部。默认是'top'
。
第三幕: 源码探秘之旅 (核心部分来啦!)
现在,让我们深入到WordPress的源码中,看看 add_rewrite_rule()
到底做了些什么。为了方便理解,我会把源码分解成几个关键步骤,并用注释进行解释。
1. 函数定义与参数处理
首先找到 add_rewrite_rule()
函数的定义,它位于 wp-includes/rewrite.php
文件中。
function add_rewrite_rule( $regex, $redirect, $priority = 'top' ) {
global $wp_rewrite;
if ( ! is_object( $wp_rewrite ) || ! $wp_rewrite->using_permalinks() ) {
return;
}
$wp_rewrite->add_rule( $regex, $redirect, $priority );
}
- 全局变量
$wp_rewrite
: 首先,它访问了全局变量$wp_rewrite
。这个变量是一个WP_Rewrite
类的实例,负责处理所有的重写规则。 - 检查Permalink: 接着,它检查
$wp_rewrite
是否是一个对象,并且当前是否正在使用固定链接(permalinks)。如果不是,函数会直接返回,因为重写规则只有在使用固定链接时才有效。 - 调用
WP_Rewrite::add_rule()
: 最关键的一步,它调用了$wp_rewrite
对象的add_rule()
方法,将我们的规则传递给它。
2. WP_Rewrite::add_rule()
的内部运作
接下来,我们进入 WP_Rewrite
类的 add_rule()
方法,继续追踪规则是如何被添加的。
function add_rule( $regex, $redirect, $priority = 'top' ) {
if ( 'bottom' == $priority ) {
$this->rules[$regex] = $redirect;
} else {
$this->rules = array( $regex => $redirect ) + $this->rules;
}
$this->flush_rules();
}
- 存储规则:
$this->rules
是WP_Rewrite
类的一个成员变量,它是一个关联数组,用于存储所有的重写规则。$regex
作为键,$redirect
作为值。 - 优先级处理: 根据
$priority
参数的值,规则会被添加到数组的顶部或底部。- 如果
$priority
是'bottom'
,则使用$this->rules[$regex] = $redirect;
直接将规则添加到数组的末尾。 - 如果
$priority
是'top'
(默认值),则使用array( $regex => $redirect ) + $this->rules;
将新的规则数组与现有的规则数组合并。注意,加号+
运算符会将新的数组放在前面,从而实现将规则添加到顶部的效果。
- 如果
- 刷新规则: 最后,调用
$this->flush_rules()
方法来刷新重写规则。
3. WP_Rewrite::flush_rules()
的幕后工作
flush_rules()
方法的作用是重新生成 .htaccess
文件(如果服务器支持)并更新 WordPress 的重写规则选项。
function flush_rules( $hard = true ) {
global $wp_filter;
// Prevent endless loops. flush_rules() calls itself.
remove_action( 'shutdown', 'wp_ob_end_flush_all', 1 );
/**
* Fires before the rewrite rules are flushed.
*
* @since 2.1.0
*/
do_action( 'flush_rewrite_rules' );
$this->rewrite_rules();
if ( is_network_admin() ) {
return;
}
if ( $hard ) {
$this->wp_rewrite_rules();
}
/**
* Fires after the rewrite rules are flushed.
*
* @since 2.1.0
*/
do_action( 'after_rewrite_rules' );
wp_ob_end_flush_all();
}
- 移除钩子: 移除
shutdown
动作上的wp_ob_end_flush_all
函数,防止无限循环。 flush_rewrite_rules
动作: 触发flush_rewrite_rules
动作,允许其他插件或主题在重写规则刷新之前执行一些操作。$this->rewrite_rules()
: 调用$this->rewrite_rules()
方法,这个方法会生成最终的重写规则数组。- 网络管理员检查: 如果是网络管理员界面,则直接返回,不执行后续操作。
$this->wp_rewrite_rules()
: 调用$this->wp_rewrite_rules()
方法,这个方法负责将重写规则写入.htaccess
文件(如果可用)或者更新 WordPress 的rewrite_rules
选项。after_rewrite_rules
动作: 触发after_rewrite_rules
动作,允许其他插件或主题在重写规则刷新之后执行一些操作。wp_ob_end_flush_all()
: 恢复wp_ob_end_flush_all
函数。
4. $this->wp_rewrite_rules()
写入规则
$this->wp_rewrite_rules()
函数是最终将规则写入配置的关键。
function wp_rewrite_rules() {
global $wp_filesystem;
if ( ! is_object( $wp_filesystem ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
WP_Filesystem();
}
$home_path = get_home_path();
$htaccess_file = $home_path . '.htaccess';
/*
* Using fopen() / fwrite() instead of $wp_filesystem->{read,write}_file()
* to avoid creating an empty .htaccess file when the directory isn't writable.
*/
if ( ( ! file_exists( $htaccess_file ) && is_writable( $home_path ) ) || is_writable( $htaccess_file ) ) {
$rules = $this->mod_rewrite_rules();
if ( ! empty( $rules ) ) {
$rules = explode( "n", $rules );
$rules = array_filter( $rules );
$rules = array_merge( (array) $this->rewrite_rules_mod_rewrite( true ), $rules, (array) $this->rewrite_rules_mod_rewrite( false ) );
$rules = array_unique( $rules );
$rules = implode( "n", $rules );
// Detect conflicts with other rules.
$existing_rules = false;
if ( file_exists( $htaccess_file ) ) {
$existing_rules = $wp_filesystem->get_contents( $htaccess_file );
}
if ( false === $existing_rules || ! preg_match( '/# BEGIN WordPress/', $existing_rules ) || ! preg_match( '/# END WordPress/', $existing_rules ) ) {
if ( false === $existing_rules ) {
$existing_rules = '';
}
if ( ! empty( $existing_rules ) ) {
$existing_rules = trim( $existing_rules ) . "nn";
}
$rules = $existing_rules . $rules;
} else {
$rules = preg_replace( '/# BEGIN WordPress.*?# END WordPress/is', trim( $rules ), $existing_rules );
}
if ( ! empty( $rules ) ) {
$wp_filesystem->put_contents( $htaccess_file, $rules, FS_CHMOD_FILE );
}
}
}
$this->rewrite_rules = $this->rewrite_rules();
update_option( 'rewrite_rules', $this->rewrite_rules );
}
- 初始化文件系统: 确保
$wp_filesystem
对象已经初始化,以便进行文件操作。 - 获取
.htaccess
文件路径: 获取网站根目录下的.htaccess
文件的路径。 - 检查
.htaccess
文件是否可写: 检查.htaccess
文件是否存在,以及网站根目录或.htaccess
文件本身是否可写。 - 生成 Mod_Rewrite 规则: 调用
$this->mod_rewrite_rules()
方法生成 Apache 的 Mod_Rewrite 规则字符串。 - 合并规则: 将生成的规则与 WordPress 的默认规则合并,并去除重复的规则。
- 检测冲突: 检测现有的
.htaccess
文件中是否已经存在 WordPress 的规则块。如果存在,则替换现有的规则块;否则,将新的规则添加到文件的末尾。 - 写入
.htaccess
文件: 使用$wp_filesystem->put_contents()
方法将最终的规则字符串写入.htaccess
文件。 - 更新
rewrite_rules
选项: 将当前的重写规则数组更新到 WordPress 的rewrite_rules
选项中。
第四幕: 一个简单的例子 (理论联系实际)
假设我们想要创建一个重写规则,将所有访问 /books/
开头的URL重写到 index.php?book_title=
。我们可以这样使用 add_rewrite_rule()
:
add_action( 'init', 'my_custom_rewrite_rules' );
function my_custom_rewrite_rules() {
add_rewrite_rule(
'^books/([^/]*)/?',
'index.php?book_title=$matches[1]',
'top'
);
}
add_filter( 'query_vars', 'my_custom_query_vars' );
function my_custom_query_vars( $query_vars ) {
$query_vars[] = 'book_title';
return $query_vars;
}
add_action( 'template_redirect', 'my_custom_template_redirect' );
function my_custom_template_redirect() {
if ( get_query_var( 'book_title' ) ) {
// 根据 book_title 的值加载相应的模板或内容
include( get_template_directory() . '/book-template.php' );
exit;
}
}
代码解释:
add_action( 'init', 'my_custom_rewrite_rules' );
: 我们使用init
动作钩子来确保在WordPress初始化时添加我们的重写规则。add_rewrite_rule()
:'^books/([^/]*)/?'
: 这是一个正则表达式,它匹配以/books/
开头,后面跟着任意字符(除了斜杠),最后可能有一个斜杠的URL。([^/]*)
这部分会捕获URL中/books/
后面的内容,并将其存储在$matches[1]
中。'index.php?book_title=$matches[1]'
: 这是重写后的URL。我们将捕获到的内容(也就是书名)赋值给book_title
这个查询变量。'top'
: 指定规则的优先级为顶部。
add_filter( 'query_vars', 'my_custom_query_vars' );
:- 我们必须告诉WordPress,
book_title
是一个有效的查询变量。否则,WordPress会忽略它。
- 我们必须告诉WordPress,
add_action( 'template_redirect', 'my_custom_template_redirect' );
:- 在
template_redirect
动作中,我们检查book_title
是否被设置。如果被设置,我们就知道用户正在访问一个/books/
开头的URL。 - 然后,我们加载一个自定义的模板文件
book-template.php
,并在这个模板文件中根据book_title
的值来显示相应的书籍内容。
- 在
重要提示:
每次你添加或修改重写规则后,一定要刷新固定链接设置。你可以简单地访问 WordPress 后台的 "设置 -> 固定链接" 页面,然后点击 "保存更改" 按钮。这会触发 flush_rewrite_rules()
函数,更新 .htaccess
文件和 rewrite_rules
选项。否则,你的新规则可能不会生效。
第五幕: 常见问题与注意事项
- 正则表达式: 正则表达式是重写规则的核心。确保你理解正则表达式的语法,并能够编写正确的表达式来匹配你想要重写的URL。
- 优先级: 规则的优先级非常重要。如果你的规则与其他规则冲突,可能会导致URL无法正确重写。尝试调整规则的优先级,将最重要的规则放在顶部。
- 查询变量: 记住,你必须使用
query_vars
过滤器来注册你自定义的查询变量。否则,WordPress会忽略这些变量。 .htaccess
文件权限: 确保 WordPress 能够写入.htaccess
文件。如果文件权限不正确,WordPress可能无法更新重写规则。- 性能: 过多的重写规则可能会影响网站的性能。尽量保持规则的简洁和高效。
- 调试: 使用
var_dump($wp_rewrite->rules)
可以查看当前所有的重写规则,帮助你调试问题。
第六幕: 总结
add_rewrite_rule()
函数是WordPress重写规则的核心。它通过调用 WP_Rewrite
类的 add_rule()
方法将新的规则添加到规则列表中。然后,flush_rules()
方法负责更新 .htaccess
文件和 rewrite_rules
选项,使新的规则生效。
希望通过今天的讲解,大家对 add_rewrite_rule()
函数的内部运作有了更深入的了解。现在,拿起你的代码编辑器,去创造属于你的友好的URL吧!记住,代码的世界充满了乐趣,只要你敢于探索,就能发现更多的惊喜。下次再见!