深入理解 WordPress `WP_Rewrite` 类的源码:如何通过 `$rules` 数组管理 URL 重写规则,并解释其在数据库中的存储。

各位观众老爷,晚上好!今天咱们来聊聊 WordPress 的 URL 重写机制,深入研究一下 WP_Rewrite 这个类,看看它如何通过 $rules 数组来管理 URL 重写规则,以及这些规则在数据库里是怎么藏猫猫的。

咱们的讲座主要分以下几个部分:

  1. WP_Rewrite 类:概览与初始化
  2. $rules 数组:URL 重写规则的核心
  3. 规则的生成:从结构到正则
  4. 规则的存储:数据库中的秘密
  5. 规则的应用:URL 匹配与查询
  6. 自定义规则:让你的网站更个性
  7. 常见问题与调试技巧

准备好了吗?那咱们开始吧!

1. WP_Rewrite 类:概览与初始化

WP_Rewrite 类是 WordPress 负责 URL 重写的核心类。它负责生成、存储和应用 URL 重写规则,让你的 WordPress 网站的 URL 看起来更漂亮、更友好(也更容易被搜索引擎抓取)。

首先,我们来看看 WP_Rewrite 类的初始化过程。通常情况下,你不需要手动实例化这个类,WordPress 会在引导过程中自动帮你搞定。在 wp-includes/class-wp-rewrite.php 文件中,你会找到这个类的定义。

class WP_Rewrite {

    /**
     * Permalink structure.
     *
     * @since 2.0.0
     * @var string
     */
    public $permalink_structure;

    /**
     * Whether permalinks are enabled.
     *
     * @since 3.0.0
     * @var bool
     */
    public $using_permalinks;

    /**
     * Whether mod_rewrite is being used.
     *
     * @since 2.0.0
     * @var bool
     */
    public $mod_rewrite;

    /**
     * Whether .htaccess exists.
     *
     * @since 2.0.0
     * @var bool
     */
    public $use_trailing_slashes;

    /**
     * Whether the rewrite module is enabled.
     *
     * @since 2.0.0
     * @var bool
     */
    public $rewrite_base;

    /**
     * The rewrite rules.
     *
     * @since 2.0.0
     * @var array
     */
    public $rules = array();

    // ... 其他属性和方法
}

注意看,这里有个 $rules 属性,类型是数组。这货就是咱们今天的主角!所有的 URL 重写规则都存储在这个数组里。

在 WordPress 初始化时,WP_Rewrite 类的实例会被创建,并且会调用 WP_Rewrite::wp_rewrite_rules() 方法来填充 $rules 数组。这个过程会读取 WordPress 默认的重写规则,以及你在主题或插件中自定义的规则。

2. $rules 数组:URL 重写规则的核心

$rules 数组是一个关联数组,它的键是正则表达式,值是对应的查询字符串。

$this->rules = array(
    'index.php$' => 'index.php',
    'author/([^/]+)/?$' => 'index.php?author_name=$matches[1]',
    'category/([^/]+)/?$' => 'index.php?category_name=$matches[1]',
    // ... 更多规则
);
  • 键 (正则表达式):用于匹配请求的 URL。如果 URL 匹配了这个正则表达式,那么对应的查询字符串就会被使用。
  • 值 (查询字符串):定义了如何处理匹配到的 URL。它通常包含 index.php 以及一些 GET 参数,这些参数会被 WordPress 用来确定要显示的内容。

举个例子:

假设你的网站的 URL 是 http://example.com/category/news/,而 $rules 数组中包含以下规则:

'category/([^/]+)/?$' => 'index.php?category_name=$matches[1]'

当 WordPress 收到这个 URL 请求时,它会遍历 $rules 数组,找到匹配的规则。在这个例子中,正则表达式 category/([^/]+)/?$ 匹配了 http://example.com/category/news/

$matches[1] 表示正则表达式中第一个捕获组的内容,也就是 news。因此,WordPress 会将 URL 重写为 index.php?category_name=news,然后将这个查询字符串传递给 WordPress 的查询系统,最终显示 news 分类下的文章。

3. 规则的生成:从结构到正则

WordPress 并不是凭空生成这些规则的。它会根据你的 Permalink 设置、分类结构、文章结构等信息,动态生成这些规则。

WP_Rewrite 类提供了一些方法来生成规则,例如:

  • generate_rewrite_rules():用于生成文章、页面、分类、标签等类型的重写规则。
  • generate_date_rewrite_rules():用于生成日期归档的重写规则。
  • generate_category_rewrite_rules():用于生成分类的重写规则。

这些方法会根据你的 Permalink 设置,生成不同的正则表达式和查询字符串。例如,如果你选择了 "朴素" Permalink 结构(/?p=%post_id%),那么生成的规则可能如下:

'index.php?p=([0-9]+)$' => 'index.php?p=$matches[1]'

如果你选择了 "文章名" Permalink 结构(/sample-post/),那么生成的规则可能如下:

'sample-post/?$' => 'index.php?name=sample-post'

更复杂的例子,比如分类目录:

如果你的分类目录结构是 /category/,而你有一个名为 news 的分类,那么 WordPress 可能会生成如下规则:

'category/news/?$' => 'index.php?category_name=news'

如果你的分类目录结构是 /topics/,那么对应的规则会变成:

'topics/news/?$' => 'index.php?category_name=news'

关键在于 WP_Rewrite 类会根据你的设置,动态调整生成的正则表达式和查询字符串,以适应你的网站结构。

4. 规则的存储:数据库中的秘密

生成了这些规则之后,WP_Rewrite 类会将它们存储到数据库中。存储的位置是 wp_options 表,对应的 option_namerewrite_rules

SELECT * FROM wp_options WHERE option_name = 'rewrite_rules';

这个 option_value 字段存储的是一个序列化的 PHP 数组,也就是 $rules 数组。当你修改 Permalink 设置或者添加自定义规则时,WordPress 会更新这个 option_value 字段。

为什么要序列化?因为数据库字段通常只能存储字符串或数字等简单类型,而 $rules 数组是一个复杂的关联数组,包含正则表达式和查询字符串。通过序列化,可以将这个数组转换为一个字符串,方便存储到数据库中。

当你需要使用这些规则时,WordPress 会从数据库中读取这个序列化的字符串,然后反序列化,重新得到 $rules 数组。

$rewrite_rules = get_option( 'rewrite_rules' );
if ( ! is_array( $rewrite_rules ) ) {
    $rewrite_rules = array();
}

5. 规则的应用:URL 匹配与查询

当 WordPress 收到一个 URL 请求时,它会遍历 $rules 数组,尝试找到匹配的规则。这个过程发生在 WP::parse_request() 方法中。

public function parse_request( $query_vars = array() ) {
    global $wp_rewrite;

    // Process PATH_INFO, REQUEST_URI, and argv.
    $this->query_vars = apply_filters( 'request', $this->query_vars );

    // Parse current request.
    $rewrite = $wp_rewrite->wp_rewrite_rules(); // 获取重写规则

    if ( ! empty( $rewrite ) ) {
        $this->matched_rule = $wp_rewrite->wp_find_matching_rewrite_rule( $this->request_uri, $rewrite ); // 查找匹配的规则
        if ( $this->matched_rule ) {
            $this->matched_query = $rewrite[ $this->matched_rule ];
            parse_str( $this->matched_query, $qv );
            $_GET = array_merge( $_GET, $qv );
            $_REQUEST = array_merge( $_REQUEST, $qv );
        }
    }

    // ... 其他代码
}

WP_Rewrite::wp_rewrite_rules() 方法会从数据库中读取并反序列化 rewrite_rules option,得到 $rules 数组。然后,WP_Rewrite::wp_find_matching_rewrite_rule() 方法会遍历 $rules 数组,找到第一个匹配当前 URL 的规则。

如果找到了匹配的规则,WordPress 会将对应的查询字符串解析成 GET 参数,然后将这些参数合并到 $_GET$_REQUEST 数组中。

最后,WordPress 会根据这些 GET 参数,确定要显示的内容。例如,如果 category_name 参数是 news,那么 WordPress 就会显示 news 分类下的文章。

6. 自定义规则:让你的网站更个性

除了 WordPress 默认的重写规则,你还可以自定义规则,让你的网站的 URL 更加个性化。

有两种主要的方式来添加自定义规则:

  • 通过 add_rewrite_rule() 函数:这是一种比较简单的方式,可以直接在你的主题或插件中使用。
  • 通过 add_rewrite_tag()add_rewrite_endpoint() 函数:这是一种更灵活的方式,可以定义自定义的查询变量和端点。

6.1 add_rewrite_rule()

add_rewrite_rule() 函数可以让你添加一条自定义的重写规则。

add_rewrite_rule( string $regex, string $redirect, string $priority = 'top' )
  • $regex:用于匹配 URL 的正则表达式。
  • $redirect:对应的查询字符串。
  • $priority:规则的优先级,可以是 topbottom

例如,你想创建一个规则,将 http://example.com/books/([0-9]+)/ 重写为 index.php?book_id=$matches[1],你可以这样写:

add_action( 'init', 'my_custom_rewrite_rules' );
function my_custom_rewrite_rules() {
    add_rewrite_rule(
        'books/([0-9]+)/?',
        'index.php?book_id=$matches[1]',
        'top'
    );
    flush_rewrite_rules(); // Important!
}

注意: 在添加或修改重写规则后,一定要调用 flush_rewrite_rules() 函数,否则规则不会生效。这个函数会清空并重建 WordPress 的重写规则,更新数据库中的 rewrite_rules option。

6.2 add_rewrite_tag()add_rewrite_endpoint()

add_rewrite_tag() 函数可以让你定义自定义的查询变量。

add_rewrite_tag( string $tag, string $regex, string $query = '' )
  • $tag:查询变量的名称,例如 %book_id%
  • $regex:用于匹配查询变量值的正则表达式,例如 ([0-9]+)
  • $query:可选的查询字符串,用于覆盖默认的查询字符串。

add_rewrite_endpoint() 函数可以让你创建一个自定义的端点。

add_rewrite_endpoint( string $endpoint, int $places )
  • $endpoint:端点的名称,例如 book
  • $places:端点的位置,可以是 EP_PERMALINKEP_PAGESEP_CATEGORIES 等。

例如,你想创建一个 book 端点,用于显示书籍的信息,你可以这样写:

add_action( 'init', 'my_custom_rewrite_endpoint' );
function my_custom_rewrite_endpoint() {
    add_rewrite_endpoint( 'book', EP_PERMALINK );
    flush_rewrite_rules(); // Important!
}

add_filter( 'template_include', 'my_custom_template_include' );
function my_custom_template_include( $template ) {
    if ( get_query_var( 'book' ) ) {
        return locate_template( 'single-book.php' ); // 自定义模板
    }
    return $template;
}

这样,你就可以通过 http://example.com/sample-post/book/ 访问 sample-post 这篇文章的 book 端点。

7. 常见问题与调试技巧

  • 规则不生效: 确保在添加或修改重写规则后调用了 flush_rewrite_rules() 函数。
  • 冲突: 如果多个规则匹配同一个 URL,WordPress 会使用第一个匹配的规则。检查你的规则的优先级,确保优先级高的规则排在前面。
  • 调试: 可以使用 global $wp_rewrite; var_dump( $wp_rewrite->rules ); 来查看当前的重写规则,或者使用 WP_Query 对象的 request 属性来查看 WordPress 最终生成的 SQL 查询语句。
  • .htaccess 文件: 如果你的网站使用 Apache 服务器,WordPress 会尝试更新 .htaccess 文件,以实现 URL 重写。确保你的 .htaccess 文件可写,或者手动更新 .htaccess 文件。
  • Nginx 服务器: 如果你的网站使用 Nginx 服务器,你需要手动配置 Nginx 的重写规则。WordPress 不会自动更新 Nginx 的配置文件。

表格总结

函数/方法 作用
WP_Rewrite 负责 URL 重写的核心类
$rules 数组 存储 URL 重写规则
add_rewrite_rule() 添加一条自定义的重写规则
add_rewrite_tag() 定义自定义的查询变量
add_rewrite_endpoint() 创建一个自定义的端点
flush_rewrite_rules() 清空并重建 WordPress 的重写规则,更新数据库
get_option('rewrite_rules') 从数据库获取重写规则

好啦,今天的讲座就到这里。希望大家对 WordPress 的 URL 重写机制有了更深入的了解。记住,理解这些底层机制,能让你更好地控制你的 WordPress 网站,让它更强大、更灵活。 如果有问题,欢迎随时提问! 咱们下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注