WordPress源码深度解析之:`WordPress`的`rewrite`规则:`add_rewrite_rule()`和`flush_rewrite_rules()`的底层实现。

各位观众老爷,晚上好! 欢迎来到今晚的 “WordPress 源码解剖大会” 。我是你们的导游兼主刀医生,今天咱们要解剖的是 WordPress 的 rewrite 规则,重点是 add_rewrite_rule()flush_rewrite_rules() 这两个家伙。

别害怕,虽然是源码,但保证不血腥,只会让你感觉“哦,原来是这样啊!”。 准备好了吗? Let’s dive in!

Rewrite 规则是个啥?

在开始之前,咱们先明确一下 rewrite 规则是干嘛的。简单来说,rewrite 规则就是告诉 Web 服务器(通常是 Apache 或 Nginx),当用户访问某个 URL 时,实际上应该访问哪个文件或执行哪个 PHP 脚本。

举个例子,假设你有一个文章,它的实际 URL 可能是 index.php?p=123,但你想让用户通过 blog/my-awesome-article 这样的 URL 访问它。 这就是 rewrite 规则的用武之地了。它会把 blog/my-awesome-article "翻译" 成 index.php?p=123,用户看到的还是友好的 URL,但服务器知道该怎么处理。

add_rewrite_rule():规则制造者

顾名思义,add_rewrite_rule() 的作用就是添加一条新的 rewrite 规则。 它的原型是这样的:

function add_rewrite_rule( $regex, $redirect, $priority = 'bottom' ) {
    global $wp_rewrite;

    $wp_rewrite->add_rule( $regex, $redirect, $priority );
}

看起来很简单,对吧?它实际上只是调用了 $wp_rewrite 对象的 add_rule() 方法。$wp_rewrite 是一个全局对象,负责管理所有的 rewrite 规则。

咱们来看看 $wp_rewrite->add_rule() 做了什么:

function add_rule( $regex, $redirect, $priority = 'bottom' ) {
    if ( 'top' === $priority ) {
        $this->rules = array_merge( array( $regex => $redirect ), $this->rules );
    } else {
        $this->rules[ $regex ] = $redirect;
    }
}

这段代码也很简单。它把新的规则添加到 $this->rules 数组中。 $this->rules$wp_rewrite 对象的一个属性,它存储了所有的 rewrite 规则。

  • $regex:这是一个正则表达式,用于匹配 URL。
  • $redirect:这是实际要访问的 URL(通常包含 index.php 和一些参数)。
  • $priority:指定规则的优先级。 'top' 表示优先匹配,'bottom' 表示最后匹配。

例子:添加一个简单的 rewrite 规则

假设我们要创建一个规则,把 books/([0-9]+) 重写成 index.php?book_id=$matches[1]。 这段代码可以这么写:

add_action( 'init', 'my_custom_rewrite_rule' );
function my_custom_rewrite_rule() {
    add_rewrite_rule(
        'books/([0-9]+)/?', // Regex to match
        'index.php?book_id=$matches[1]', // Redirect to
        'top' // Priority
    );
}

注意几点:

  1. 我们把 add_rewrite_rule() 放在 init 钩子中执行。 这是因为 rewrite 规则需要在 WordPress 初始化之后才能添加。
  2. 正则表达式 books/([0-9]+)/? 匹配以 books/ 开头,后面跟着一个或多个数字,最后可以有一个斜杠的 URL。
  3. $matches[1] 是正则表达式中第一个括号捕获的内容,也就是书的 ID。

flush_rewrite_rules():规则的刷新者

添加了 rewrite 规则之后,并不会立即生效。 你需要调用 flush_rewrite_rules() 函数来刷新 rewrite 规则。 这个函数会更新 .htaccess 文件(如果是 Apache 服务器)或者其他服务器配置文件,让服务器知道新的规则。

flush_rewrite_rules() 的原型是这样的:

function flush_rewrite_rules( $hard = true ) {
    global $wp_rewrite;

    $wp_rewrite->flush_rules( $hard );
}

同样,它也是调用了 $wp_rewrite 对象的 flush_rules() 方法。

$wp_rewrite->flush_rules() 的实现比较复杂,咱们一点点来分析:

function flush_rules( $hard = true ) {
    global $wp_filesystem;

    // Check if the rules need to be flushed.
    $rules = $this->wp_rewrite_rules();

    if ( false === $rules ) {
        return false;
    }

    if ( $hard ) {
        $this->rewrite_rules();
    }

    if ( is_multisite() ) {
        delete_transient( 'rewrite_rules' );
        return true;
    }

    // If using mod_rewrite, update the .htaccess file.
    if ( $this->using_mod_rewrite_permalinks() ) {
        if ( ! $wp_filesystem ) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
            WP_Filesystem();
        }

        $home_path = get_home_path();
        $htaccess_file = $home_path . '.htaccess';

        // ... (读取 .htaccess 文件,更新规则) ...

        if ( $wp_filesystem->is_writable( $htaccess_file ) ) {
            $wp_filesystem->put_contents( $htaccess_file, $rules, FS_CHMOD_FILE );
        } else {
            // ... (提示用户手动更新 .htaccess 文件) ...
        }
    } else {
        // ... (如果不是 mod_rewrite,则更新 web.config 文件) ...
    }

    delete_transient( 'rewrite_rules' );

    return true;
}

这段代码做了以下几件事:

  1. 检查是否需要刷新规则: 通过调用 $this->wp_rewrite_rules() 方法获取当前的 rewrite 规则。 如果返回 false,则表示不需要刷新。

  2. 生成新的 rewrite 规则: 如果 $hard 参数为 true,则调用 $this->rewrite_rules() 方法重新生成所有的 rewrite 规则。 这个方法会将所有的规则组合成一个字符串,用于更新 .htaccess 文件。

  3. 多站点处理: 如果是 WordPress 多站点,则删除 rewrite_rules 缓存。

  4. 更新 .htaccess 文件: 如果使用了 mod_rewrite (Apache 服务器),则读取 .htaccess 文件,将新的 rewrite 规则写入该文件。 如果 .htaccess 文件不可写,则提示用户手动更新。

  5. 更新 web.config 文件: 如果没有使用 mod_rewrite (例如 Nginx 服务器),则更新 web.config 文件(如果存在)。

  6. 删除缓存: 删除 rewrite_rules 缓存。

$this->wp_rewrite_rules():规则获取器

这个方法负责生成最终的 rewrite 规则字符串,用于更新 .htaccess 文件。 它的代码比较长,咱们只看关键部分:

function wp_rewrite_rules() {
    $rewrite_rules = apply_filters( 'rewrite_rules_array', $this->rules );

    if ( empty( $rewrite_rules ) ) {
        return false;
    }

    $lines = array();

    foreach ( (array) $rewrite_rules as $match => $query ) {
        $match = preg_replace( '#^/#', '', $match );
        $match = str_replace( array( "rn", "n" ), "n", $match );
        $query = str_replace( array( "rn", "n" ), "n", $query );
        $lines[] = 'RewriteRule ^' . $match . ' ' . $query . ' [QSA,L]';
    }

    $rules = "n# BEGIN WordPressn<IfModule mod_rewrite.c>nRewriteEngine OnnRewriteBase " . trailingslashit( $this->rewrite_base ) . "nRewriteRule ^index.php$ - [L]n" . implode( "n", $lines ) . "n</IfModule>n# END WordPressn";

    return $rules;
}

这段代码做了以下几件事:

  1. 应用过滤器: 通过 apply_filters( 'rewrite_rules_array', $this->rules ) 应用 rewrite_rules_array 过滤器。 允许插件修改 rewrite 规则。

  2. 遍历规则: 遍历 $this->rules 数组,将每一条规则转换成 Apache rewrite 规则的格式。 例如,将 'books/([0-9]+)/?' => 'index.php?book_id=$matches[1]' 转换成 RewriteRule ^books/([0-9]+)/? index.php?book_id=$matches[1] [QSA,L]

  3. 生成规则字符串: 将所有的规则组合成一个字符串,包括 RewriteEngine OnRewriteBase 等指令。

$this->using_mod_rewrite_permalinks():判断是否使用 mod_rewrite

这个方法用于判断是否使用了 mod_rewrite (Apache 服务器)。 它的实现很简单:

function using_mod_rewrite_permalinks() {
    if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && strpos( $_SERVER['SERVER_SOFTWARE'], 'Apache' ) !== false && $this->using_permalinks() ) {
        return true;
    }

    return false;
}

它检查 $_SERVER['SERVER_SOFTWARE'] 变量是否包含 Apache 字符串,并且 using_permalinks() 方法返回 trueusing_permalinks()方法检查是否设置了固定的永久链接格式。

重点总结

函数/方法 作用 参数 返回值
add_rewrite_rule() 添加一条新的 rewrite 规则。 $regex (正则表达式), $redirect (重定向 URL), $priority (优先级)
flush_rewrite_rules() 刷新 rewrite 规则,更新 .htaccess 或其他服务器配置文件。 $hard (是否强制刷新,默认为 true) true (成功) 或 false (失败)
$wp_rewrite->add_rule() 实际上执行添加 rewrite 规则的操作。 $regex (正则表达式), $redirect (重定向 URL), $priority (优先级)
$wp_rewrite->flush_rules() 实际上执行刷新 rewrite 规则的操作。 $hard (是否强制刷新,默认为 true) true (成功) 或 false (失败)
$wp_rewrite->wp_rewrite_rules() 生成用于更新 .htaccess 文件的 rewrite 规则字符串。 包含 rewrite 规则的字符串
$wp_rewrite->using_mod_rewrite_permalinks() 判断是否使用了 mod_rewrite (Apache 服务器)。 true (使用了 mod_rewrite) 或 false (未使用 mod_rewrite)

注意事项

  • 性能问题: 频繁调用 flush_rewrite_rules() 会导致性能问题,因为它会更新 .htaccess 文件。 应该尽量避免在每次页面加载时都调用它。 建议只在添加或修改 rewrite 规则时调用。
  • .htaccess 文件权限: 确保 .htaccess 文件可写,否则 WordPress 无法自动更新 rewrite 规则。
  • 正则表达式: 正确编写正则表达式非常重要。 如果正则表达式写错了,可能会导致 rewrite 规则无法生效。
  • 优先级: 规则的优先级也很重要。 如果有多个规则匹配同一个 URL,那么优先级最高的规则会生效。

总结

add_rewrite_rule()flush_rewrite_rules() 是 WordPress 中用于管理 rewrite 规则的两个核心函数。 理解它们的底层实现可以帮助你更好地控制 WordPress 的 URL 结构,创建更友好的 URL,并提高网站的 SEO。

希望今天的解剖对大家有所帮助。 记住,源码并不可怕,只要你敢于探索,就能发现其中的奥秘。

下次再见!

发表回复

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