WordPress 高级路由:使用 WP_Rewrite
实现复杂的路由规则和查询变量
大家好,今天我们来深入探讨 WordPress 的路由系统,重点是如何利用 WP_Rewrite
类来实现复杂的路由规则和自定义查询变量。 WordPress 默认的路由机制对于简单的页面和文章结构来说已经足够,但当我们需要构建更复杂的应用程序,比如自定义插件、主题或者需要高度定制 URL 结构的项目时,就需要用到 WP_Rewrite
来实现更灵活的路由控制。
1. WordPress 路由机制概览
在深入 WP_Rewrite
之前,我们需要对 WordPress 的路由机制有一个基本的了解。 当用户在浏览器中输入一个 URL 并访问 WordPress 站点时,WordPress 会经过以下几个关键步骤来解析 URL:
- URL 解析: WordPress 首先会解析 URL,提取出请求的路径(
REQUEST_URI
)。 - Rewrite Rules 匹配: WordPress 会将提取的路径与一系列预定义的和自定义的重写规则(Rewrite Rules)进行匹配。 这些规则定义了 URL 应该如何被解释,以及哪些查询变量应该被设置。
- 查询变量设置: 如果 URL 匹配到某个重写规则,WordPress 会根据规则设置相应的查询变量(Query Vars)。 查询变量是 WordPress 用来传递请求参数的关键机制。
- 模板加载: WordPress 根据查询变量的值,决定加载哪个模板文件来生成最终的页面内容。
理解这个流程对于使用 WP_Rewrite
至关重要。 WP_Rewrite
的核心作用就是定义和管理重写规则,以及注册和处理自定义的查询变量。
2. WP_Rewrite
类简介
WP_Rewrite
类是 WordPress 用来管理重写规则的核心类。 它提供了添加、修改和删除重写规则的方法,以及处理查询变量的机制。
WP_Rewrite
类主要包含以下几个关键属性和方法:
$rules
: 一个关联数组,存储着所有的重写规则。 键是用于匹配 URL 的正则表达式,值是 URL 应该被重写成什么形式。$queryvar_whitelist
: 一个数组,存储着所有允许被识别的查询变量。 只有在这个列表中的查询变量才会被 WordPress 识别和处理。add_rule( $regex, $redirect, $priority = 'bottom' )
: 添加一个新的重写规则。$regex
是用于匹配 URL 的正则表达式,$redirect
是 URL 应该被重写成的形式,$priority
指定规则的优先级(top
或bottom
)。add_permastruct( $name, $permalink, $args = array() )
: 添加一个永久链接结构(Permastruct)。 永久链接结构定义了 URL 的基本格式,例如文章的 URL 格式。flush_rules( $hard = true )
: 刷新重写规则。 当你添加、修改或删除重写规则后,需要调用这个方法来更新 WordPress 的重写规则缓存。$hard
参数指定是否强制刷新,建议设置为true
。generate_rewrite_rules( $permalink, $epmask, $paged = true, $feed = true, $forcomments = false, $walk_dirs = true, $add_endpoints = true )
: 生成重写规则。 这个方法通常用于生成基于永久链接结构的重写规则。rewrite_rules()
: 获取当前所有重写规则。
3. 添加自定义查询变量
在使用 WP_Rewrite
添加自定义路由规则之前,我们需要先注册自定义的查询变量。 只有注册过的查询变量才能被 WordPress 识别和处理。
可以通过 query_vars
钩子来注册自定义查询变量:
add_filter( 'query_vars', 'my_custom_query_vars' );
function my_custom_query_vars( $query_vars ) {
$query_vars[] = 'my_custom_var'; // 添加自定义查询变量 'my_custom_var'
$query_vars[] = 'my_second_var'; // 添加第二个自定义查询变量 'my_second_var'
return $query_vars;
}
这段代码将 'my_custom_var'
和 'my_second_var'
添加到 WordPress 的查询变量列表中。 现在,我们就可以在 URL 中使用这两个查询变量了。
4. 添加自定义重写规则
添加自定义重写规则是使用 WP_Rewrite
的核心步骤。 可以通过 init
钩子来添加自定义重写规则:
add_action( 'init', 'my_custom_rewrite_rules' );
function my_custom_rewrite_rules() {
global $wp_rewrite;
// 添加一个简单的重写规则
add_rewrite_rule(
'^my-custom-page/?$', // 用于匹配 URL 的正则表达式
'index.php?pagename=my-custom-page', // URL 应该被重写成的形式
'top' // 规则的优先级
);
// 添加一个带有查询变量的重写规则
add_rewrite_rule(
'^my-custom-page/([^/]+)/([^/]+)/?$', // 用于匹配 URL 的正则表达式
'index.php?pagename=my-custom-page&my_custom_var=$matches[1]&my_second_var=$matches[2]', // URL 应该被重写成的形式
'top' // 规则的优先级
);
// 刷新重写规则
$wp_rewrite->flush_rules();
}
这段代码添加了两个重写规则:
- 第一个规则将
my-custom-page
的 URL 重写到名为my-custom-page
的页面。 - 第二个规则将类似
my-custom-page/value1/value2
的 URL 重写到名为my-custom-page
的页面,并将value1
赋值给my_custom_var
查询变量,value2
赋值给my_second_var
查询变量。
代码解释:
add_rewrite_rule()
函数用于添加重写规则。- 第一个参数是用于匹配 URL 的正则表达式。
^
表示 URL 的开头,$
表示 URL 的结尾,?
表示前面的字符是可选的。 - 第二个参数是 URL 应该被重写成的形式。
index.php
是 WordPress 的入口文件,pagename
是一个预定义的查询变量,用于指定要加载的页面。$matches[1]
和$matches[2]
分别表示正则表达式中第一个和第二个捕获组的值。 - 第三个参数是规则的优先级。
top
表示规则应该被放在重写规则列表的顶部,这意味着它会比其他规则更早被匹配。 $wp_rewrite->flush_rules()
函数用于刷新重写规则。 每次添加、修改或删除重写规则后,都需要调用这个函数来更新 WordPress 的重写规则缓存。 否则,新的规则可能不会生效。
重要提示:
每次添加或修改重写规则后,必须刷新重写规则。 最简单的方法是访问 WordPress 后台的“设置” -> “固定链接”页面,然后点击“保存更改”按钮。 这将触发 flush_rules()
函数,并更新重写规则缓存。
5. 获取查询变量的值
在模板文件中,可以通过 get_query_var()
函数来获取查询变量的值:
<?php
$my_custom_var = get_query_var( 'my_custom_var' );
$my_second_var = get_query_var( 'my_second_var' );
if ( ! empty( $my_custom_var ) ) {
echo 'My Custom Var: ' . esc_html( $my_custom_var ) . '<br>';
}
if ( ! empty( $my_second_var ) ) {
echo 'My Second Var: ' . esc_html( $my_second_var ) . '<br>';
}
?>
这段代码获取了 my_custom_var
和 my_second_var
查询变量的值,并在页面上显示它们。 esc_html()
函数用于转义 HTML 字符,以防止 XSS 攻击。
6. 使用 Permastructs
Permastructs 定义了 URL 的基本格式,例如文章的 URL 格式。 可以使用 add_permastruct()
函数来添加自定义的 Permastructs。
add_action( 'init', 'my_custom_permastruct' );
function my_custom_permastruct() {
global $wp_rewrite;
$permastruct_args = array(
'with_front' => false, // 是否包含前缀(例如 /blog/)
'ep_mask' => EP_NONE, // endpoint mask,用于处理分页和 feed
'paged' => false, // 是否支持分页
'feed' => false, // 是否支持 feed
'query_var' => true // 是否生成查询变量
);
add_permastruct( 'my_custom', 'my-custom/%my_custom_param%/', $permastruct_args );
add_rewrite_rule(
'^my-custom/([^/]+)/?$',
'index.php?my_custom_param=$matches[1]',
'top'
);
$wp_rewrite->flush_rules();
}
add_filter( 'query_vars', 'my_custom_query_vars' );
function my_custom_query_vars( $query_vars ) {
$query_vars[] = 'my_custom_param';
return $query_vars;
}
add_filter( 'post_type_link', 'my_custom_post_type_link', 10, 2 );
function my_custom_post_type_link( $post_link, $post ) {
if ( $post->post_type == 'my_custom_post_type' ) {
$post_link = str_replace( '%my_custom_param%', get_post_meta( $post->ID, 'my_custom_param', true ), $post_link );
}
return $post_link;
}
代码解释:
add_permastruct()
函数用于添加 Permastruct。- 第一个参数是 Permastruct 的名称。
- 第二个参数是 Permastruct 的 URL 结构。
%my_custom_param%
是一个占位符,将在生成 URL 时被替换为实际的值。 - 第三个参数是一个数组,包含 Permastruct 的参数。
with_front
指定是否包含前缀。 如果设置为true
,则 URL 将包含 WordPress 的前缀(例如/blog/
)。ep_mask
指定 endpoint mask。 Endpoint mask 用于处理分页和 feed。paged
指定是否支持分页。feed
指定是否支持 feed。query_var
指定是否生成查询变量。
为了使用 Permastruct,需要注册一个新的查询变量 'my_custom_param'
,并定义一个重写规则,将 URL 重写到 index.php
并设置查询变量。
为了配合自定义文章类型, 需要通过post_type_link
钩子修改文章链接,将占位符替换为实际的值。
7. 复杂的路由场景实例
下面我们通过几个复杂的路由场景来演示如何使用 WP_Rewrite
。
场景 1:自定义文章类型和分类法
假设我们有一个自定义文章类型 product
和一个自定义分类法 product_category
。 我们希望 URL 结构如下:
/products/category/category-slug/product-slug/
实现代码如下:
add_action( 'init', 'my_custom_rewrite_rules_product' );
function my_custom_rewrite_rules_product() {
global $wp_rewrite;
add_rewrite_rule(
'^products/category/([^/]+)/([^/]+)/?$',
'index.php?product=$matches[2]&product_category=$matches[1]',
'top'
);
$wp_rewrite->flush_rules();
}
add_filter( 'query_vars', 'my_custom_query_vars_product' );
function my_custom_query_vars_product( $query_vars ) {
$query_vars[] = 'product';
$query_vars[] = 'product_category';
return $query_vars;
}
场景 2:多级分类结构
假设我们有一个分类法,并且允许分类之间存在父子关系。 我们希望 URL 结构能够反映分类的层级关系:
/category/parent-category/child-category/post-slug/
这种场景需要更复杂的正则表达式和重写规则,并且需要递归地处理分类的层级关系。 这里提供一个思路:
- 获取所有分类: 首先,获取所有分类,并构建一个分类层级关系的树形结构。
- 生成重写规则: 遍历分类树,为每个分类生成一个重写规则。 重写规则的正则表达式应该能够匹配该分类及其所有子分类的 URL。
- 处理查询变量: 根据 URL 中匹配到的分类,设置相应的查询变量。
由于代码量较大,这里不提供完整的代码示例,但以上思路可以帮助你解决多级分类结构的路由问题。
场景 3:使用 Endpoint
Endpoint 是添加到标准 WordPress URL 结构中的附加 URL 部分。例如,你可以为用户个人资料页面添加一个 "edit" endpoint: /profile/username/edit/
。
add_action( 'init', 'my_custom_endpoint' );
function my_custom_endpoint() {
add_rewrite_endpoint( 'edit', EP_PERMALINK | EP_PAGES );
}
add_filter( 'query_vars', 'my_custom_endpoint_query_vars' );
function my_custom_endpoint_query_vars( $vars ) {
$vars[] = 'edit';
return $vars;
}
// 确保flush_rules()被调用,例如通过访问固定链接设置页面
这段代码添加了一个名为 "edit" 的 endpoint,它适用于文章和页面。 现在,你可以通过 get_query_var( 'edit' )
来判断用户是否访问了 "edit" endpoint。
8. 调试 WP_Rewrite
规则
调试 WP_Rewrite
规则可能比较困难,因为重写规则的匹配过程是隐藏的。 以下是一些调试技巧:
- 使用
global $wp_rewrite; print_r( $wp_rewrite->rules );
: 在init
钩子中打印出所有的重写规则,可以帮助你了解 WordPress 的重写规则是如何组织的。 - 使用
var_dump( $wp_query->query_vars );
: 在模板文件中打印出$wp_query->query_vars
数组,可以帮助你了解 WordPress 是如何解析 URL 并设置查询变量的。 - 逐步调试: 使用
error_log()
函数在代码中插入调试信息,可以帮助你跟踪代码的执行过程。 - 使用插件: 有一些插件可以帮助你调试
WP_Rewrite
规则,例如 "Rewrite Rules Inspector"。
9. 注意事项和最佳实践
- 性能: 过多的重写规则会影响 WordPress 的性能。 尽量减少重写规则的数量,并优化正则表达式。
- 冲突: 自定义重写规则可能会与其他插件或主题的重写规则冲突。 确保你的重写规则不会覆盖或干扰其他规则。
- 可读性: 编写清晰易懂的正则表达式,并添加注释,可以提高代码的可读性和可维护性。
- 安全性: 对查询变量的值进行验证和转义,以防止 XSS 攻击。
- 刷新规则: 每次修改重写规则后,都要刷新重写规则缓存。
总结
我们学习了如何利用 WP_Rewrite
类来实现复杂的路由规则和自定义查询变量,希望这些知识对你有所帮助。 掌握 WP_Rewrite
可以帮助你构建更灵活、更强大的 WordPress 应用程序。
结语
希望以上内容能够帮助你更好地理解和使用 WP_Rewrite
。 掌握这一技术可以让你在 WordPress 开发中拥有更大的灵活性和控制权。 记住,实践是最好的老师,多尝试,多调试,你将会成为 WordPress 路由专家。