分析 `add_rewrite_rule()` 函数的源码,它是如何将新的重写规则添加到 `WP_Rewrite` 对象的?

好的,今天咱们就来聊聊 WordPress 里那个神秘又强大的 add_rewrite_rule() 函数,看看它到底是怎么把你的自定义 URL 规则塞进 WordPress 的大脑里,让它乖乖听话的。

(清清嗓子,调整麦克风) 各位观众老爷,晚上好!

今天咱们要讲的这个 add_rewrite_rule(),那可是 WordPress URL 重写机制中的一员大将。 你想让你的网站看起来更酷、更 SEO 友好,或者搞一些奇奇怪怪的自定义 URL,那就得靠它了。 但是,直接用它可能有点懵,因为它背后藏着不少细节。 所以,咱们就一层一层扒开它的源码,看看它到底是怎么运作的。

一、 add_rewrite_rule() 的基本用法

先来个热身,回顾一下 add_rewrite_rule() 最基本的用法:

add_action( 'init', 'my_custom_rewrite_rule' );
function my_custom_rewrite_rule() {
  add_rewrite_rule(
    '^products/([0-9]+)/?$',  // 正则表达式,匹配 URL
    'index.php?pagename=products&product_id=$matches[1]', // 替换成什么
    'top' // 位置:'top' (默认), 'bottom'
  );
  flush_rewrite_rules(); // 刷新重写规则!非常重要!
}

这段代码的意思是:

  • add_action( 'init', 'my_custom_rewrite_rule' );: 在 WordPress 初始化的时候,执行 my_custom_rewrite_rule 函数。 init 是个好时机,因为这个时候 WordPress 已经加载了大部分核心功能,包括重写规则。
  • add_rewrite_rule(): 核心函数,添加重写规则。
    • '^products/([0-9]+)/?$': 这是一个正则表达式,用来匹配 URL。 比如 products/123/ 就会被匹配到。 ([0-9]+) 用来捕获数字,稍后可以通过 $matches[1] 访问。
    • 'index.php?pagename=products&product_id=$matches[1]': 当 URL 匹配到上面的正则表达式时,就用这个字符串来替换它。 这里实际上是告诉 WordPress,把这个 URL 当作访问 index.php,并传递 pagenameproduct_id 这两个参数。
    • 'top': 可选参数,指定规则的位置。 'top' 表示放在规则列表的顶部,'bottom' 表示放在底部。 默认是 'top'。 放在顶部意味着这条规则会优先被匹配。
  • flush_rewrite_rules(): 非常重要! 每次添加或修改重写规则后,都必须刷新重写规则。 这个函数会重新生成 .htaccess 文件(如果你的服务器支持),或者更新 WordPress 数据库中的重写规则。 不刷新,你的规则就不会生效!

二、深入 add_rewrite_rule() 的源码

现在,让我们深入 add_rewrite_rule() 的源码,看看它背后到底做了什么。 add_rewrite_rule() 函数位于 wp-includes/rewrite.php 文件中。 我们简化一下,只保留核心部分:

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

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

看到没? add_rewrite_rule() 自己啥也没干,它只是调用了 $wp_rewrite 对象的 add_rule() 方法。 $wp_rewrite 是一个全局对象,它是 WP_Rewrite 类的一个实例。 WP_Rewrite 类负责管理所有的重写规则。

所以,真正的秘密都藏在 WP_Rewrite::add_rule() 方法里。

三、 WP_Rewrite::add_rule() 的秘密

让我们来看看 WP_Rewrite::add_rule() 的源码(简化版):

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

这段代码很简单,但是很重要:

  • $this->rules: 这是 WP_Rewrite 类的一个属性,它是一个数组,用来存储所有的重写规则。 数组的键是正则表达式,值是替换字符串。
  • if ( 'bottom' == $priority ): 判断规则的优先级。
    • 如果 $priority'bottom',就把新的规则添加到 $this->rules 数组的末尾。
    • 否则(默认情况下,$priority'top'),就把新的规则添加到 $this->rules 数组的开头。 array( $regex => $redirect ) + $this->rules 这种写法,会把新的规则放在数组的最前面。

也就是说,WP_Rewrite::add_rule() 只是简单地把你的规则添加到了 $this->rules 数组里。 并没有做任何复杂的处理。 那么,这些规则是怎么生效的呢?

四、 WP_Rewrite::wp_rewrite_rules()WP_Rewrite::generate_rewrite_rules()

关键在于 WP_Rewrite::wp_rewrite_rules()WP_Rewrite::generate_rewrite_rules() 这两个方法。 这两个方法负责把 $this->rules 数组里的规则转换成 Apache 的 .htaccess 文件里的规则(或者 Nginx 的配置规则,或者数据库里的规则)。

WP_Rewrite::wp_rewrite_rules() 方法会调用 WP_Rewrite::generate_rewrite_rules() 方法来生成重写规则。 我们来看看 WP_Rewrite::generate_rewrite_rules() 的简化版:

public function generate_rewrite_rules( $rewritecode = '', $rewritereplace = '' ) {
  $rules = array();

  foreach ( (array) $this->rules as $match => $query ) {
    // 一大堆复杂的逻辑,处理正则表达式,处理 query 参数,等等...
    // 最终生成 Apache 的 RewriteRule 和 RewriteCond 规则
    $rules[$rewritecode . $match] = $rewritereplace . $query;
  }

  return $rules;
}

这段代码做了很多事情,包括:

  • 遍历 $this->rules 数组,取出每一条规则(正则表达式和替换字符串)。
  • 对正则表达式进行处理,比如添加 ^$ 符号,确保正则表达式匹配整个 URL。
  • 对替换字符串进行处理,比如把 $matches[1] 替换成实际的参数值。
  • 生成 Apache 的 RewriteRuleRewriteCond 规则。 RewriteRule 负责匹配 URL,RewriteCond 负责添加条件。

最终,WP_Rewrite::generate_rewrite_rules() 方法会返回一个数组,这个数组包含了所有的 RewriteRuleRewriteCond 规则。

五、 flush_rewrite_rules() 的作用

现在,我们再回到 flush_rewrite_rules() 函数。 这个函数会做以下事情:

  1. 调用 WP_Rewrite::wp_rewrite_rules() 方法,生成所有的重写规则。
  2. 把这些重写规则写入 .htaccess 文件(或者更新数据库)。
  3. 清除 WordPress 的缓存,确保新的规则立即生效。

所以,flush_rewrite_rules() 函数是让你的自定义规则真正生效的关键。 如果你不刷新重写规则,WordPress 就不会知道你的新规则,你的 URL 就不会被正确地重写。

六、 总结

让我们来总结一下 add_rewrite_rule() 的工作流程:

  1. 你调用 add_rewrite_rule() 函数,添加一条自定义的重写规则。
  2. add_rewrite_rule() 函数把你的规则添加到 $wp_rewrite 对象的 $this->rules 数组里。
  3. 你调用 flush_rewrite_rules() 函数,刷新重写规则。
  4. flush_rewrite_rules() 函数会:
    • 调用 WP_Rewrite::wp_rewrite_rules() 方法,生成所有的重写规则。
    • 把这些重写规则写入 .htaccess 文件(或者更新数据库)。
    • 清除 WordPress 的缓存。
  5. 当用户访问你的网站时,Apache(或者 Nginx)会根据 .htaccess 文件里的规则,对 URL 进行重写。
  6. WordPress 会根据重写后的 URL,找到对应的页面或文章。

七、一些高级技巧和注意事项

  • 正则表达式要写对。 正则表达式是 URL 重写的灵魂。 如果你写错了正则表达式,你的规则就永远不会生效。 可以使用在线正则表达式测试工具来验证你的正则表达式。
  • 注意优先级。 如果你的规则和其他规则冲突,可以通过调整优先级来解决。 'top' 表示优先级最高,'bottom' 表示优先级最低。
  • 小心缓存。 WordPress 会缓存重写规则,所以有时候即使你刷新了重写规则,新的规则也不会立即生效。 可以尝试清除 WordPress 的缓存,或者禁用缓存插件。
  • 不要过度使用重写规则。 过多的重写规则会影响网站的性能。 尽量使用 WordPress 提供的 API 来实现你的需求。
  • 了解 .htaccess 文件。 .htaccess 文件是 Apache 的配置文件,它控制着网站的很多行为,包括 URL 重写。 了解 .htaccess 文件的语法和用法,可以帮助你更好地理解 URL 重写机制。
  • Nginx 的配置和 Apache 不同 如果你用的是Nginx,刷新重写规则并不会自动修改Nginx的配置文件。你需要手动修改Nginx配置文件,然后重启Nginx服务。

八、代码示例:更复杂的重写规则

假设你想创建一个自定义的 "书籍" 分类,URL 结构如下:

  • /books/: 显示所有书籍。
  • /books/genre/{genre_slug}/: 显示特定类型的所有书籍。
  • /books/{book_slug}/: 显示特定书籍的详情。

可以这样实现:

add_action( 'init', 'my_custom_book_rewrite_rules' );
function my_custom_book_rewrite_rules() {
  // 1. 显示所有书籍
  add_rewrite_rule(
    '^books/?$',
    'index.php?post_type=book',
    'top'
  );

  // 2. 显示特定类型的所有书籍
  add_rewrite_rule(
    '^books/genre/([^/]+)/?$',
    'index.php?post_type=book&genre=$matches[1]',
    'top'
  );

  // 3. 显示特定书籍的详情
  add_rewrite_rule(
    '^books/([^/]+)/?$',
    'index.php?book=$matches[1]', // 注意这里假设你用的是自定义的 "book" post type 并且 slug 就是 book
    'top'
  );

  flush_rewrite_rules();
}

// 注册自定义查询变量
add_filter( 'query_vars', 'my_custom_book_query_vars' );
function my_custom_book_query_vars( $query_vars ) {
  $query_vars[] = 'genre'; // 添加 genre 查询变量
  $query_vars[] = 'book'; // 添加 book 查询变量
  return $query_vars;
}

// 确保 book post type 注册了
add_action( 'init', 'register_book_post_type' );
function register_book_post_type() {
  register_post_type( 'book', array(
      'labels' => array(
          'name' => __('Books'),
          'singular_name' => __('Book')
      ),
      'public' => true,
      'has_archive' => true,
      'rewrite' => array('slug' => 'books'),
      'supports' => array( 'title', 'editor', 'custom-fields' ),
  ));

  //注册自定义分类
  register_taxonomy(
    'genre',
    'book',
    array(
      'label' => __( 'Genre' ),
      'rewrite' => array( 'slug' => 'genre' ),
      'hierarchical' => true,
    )
  );
}

这个例子展示了如何使用 add_rewrite_rule() 创建更复杂的 URL 结构。 关键点:

  • 正则表达式要精确。 确保正则表达式能够正确匹配你想要的 URL。
  • 添加自定义查询变量。 使用 add_filter( 'query_vars', ... ) 添加自定义查询变量,这样 WordPress 才能识别你的 URL 参数。
  • 注册自定义文章类型和分类法。 确保你已经注册了自定义文章类型和分类法,这样 WordPress 才能正确处理你的内容。

九、表格总结

函数/方法 作用 位于哪个文件? 关键点
add_rewrite_rule() 添加一条自定义的重写规则到 $wp_rewrite 对象。 wp-includes/rewrite.php 只是简单地调用 $wp_rewrite->add_rule()
WP_Rewrite::add_rule() 将规则添加到 $this->rules 数组中,根据优先级决定添加的位置。 wp-includes/rewrite.php $this->rules 是存储所有重写规则的数组。
WP_Rewrite::generate_rewrite_rules() 根据 $this->rules 数组生成 Apache 的 RewriteRuleRewriteCond 规则。 wp-includes/rewrite.php 复杂的逻辑,处理正则表达式,处理 query 参数。
flush_rewrite_rules() 刷新重写规则,将生成的规则写入 .htaccess 文件(或更新数据库),并清除缓存。 wp-includes/rewrite.php 让你的自定义规则真正生效的关键。 必须调用!
add_filter( 'query_vars', ... ) 添加自定义查询变量,让 WordPress 能够识别 URL 参数。 functions.php 或插件文件中 确保 WordPress 能够识别你的自定义 URL 参数。

十、最后的忠告

URL 重写是一个强大的工具,但也是一个容易出错的地方。 在使用 add_rewrite_rule() 时,一定要小心谨慎,多做测试,确保你的规则能够正常工作。 如果你遇到了问题,可以查阅 WordPress 官方文档,或者在 Stack Overflow 上提问。

好了,今天的讲座就到这里。 希望大家能够掌握 add_rewrite_rule() 的用法,创造出更酷、更 SEO 友好的 WordPress 网站! 谢谢大家!

(鞠躬,下台)

发表回复

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