分析 `add_rewrite_rule()` 函数的源码,它如何将新的重写规则添加到 WordPress 的重写规则列表中?

大家好,我是你们今天的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->rulesWP_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;
    }
}

代码解释:

  1. add_action( 'init', 'my_custom_rewrite_rules' );: 我们使用 init 动作钩子来确保在WordPress初始化时添加我们的重写规则。
  2. add_rewrite_rule():
    • '^books/([^/]*)/?': 这是一个正则表达式,它匹配以 /books/ 开头,后面跟着任意字符(除了斜杠),最后可能有一个斜杠的URL。([^/]*) 这部分会捕获URL中 /books/ 后面的内容,并将其存储在 $matches[1] 中。
    • 'index.php?book_title=$matches[1]': 这是重写后的URL。我们将捕获到的内容(也就是书名)赋值给 book_title 这个查询变量。
    • 'top': 指定规则的优先级为顶部。
  3. add_filter( 'query_vars', 'my_custom_query_vars' );:
    • 我们必须告诉WordPress,book_title 是一个有效的查询变量。否则,WordPress会忽略它。
  4. 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吧!记住,代码的世界充满了乐趣,只要你敢于探索,就能发现更多的惊喜。下次再见!

发表回复

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