探讨 WordPress 如何通过 `add_rewrite_rule()` 函数实现自定义的 URL 重写规则。

好吧,各位观众老爷们,今天咱们聊聊 WordPress 里一个有点神秘,但其实挺好玩的功能:add_rewrite_rule(),也就是自定义 URL 重写规则。这玩意儿就像给你的网站地址做了个整容手术,让它变得更漂亮、更易于理解,而且对 SEO 也很有帮助。

开场白:URL 重写的必要性

想象一下,你开了一家小卖部,如果顾客想买东西,只能通过货架编号来查找,那得多麻烦?URL 重写就相当于给货架贴上了标签,顾客可以直接根据标签找到自己想要的东西。

WordPress 默认的 URL 结构有时候并不友好,比如 /?p=123 这种,鬼知道 123 代表啥?通过 URL 重写,我们可以把它变成 /product/my-awesome-product/,是不是瞬间高大上了?

第一幕:add_rewrite_rule() 登场

add_rewrite_rule() 函数是 WordPress 提供的一个用于添加自定义重写规则的利器。它的基本语法如下:

add_rewrite_rule( $regex, $redirect, $priority );
  • $regex (string): 这是一个正则表达式,用于匹配传入的 URL。
  • $redirect (string): 这是重定向的目标 URL,也就是匹配到的 URL 应该被转换成什么。
  • $priority (string, optional): 重写规则的优先级。默认是 'bottom',意思是放在规则列表的底部。 'top' 优先级最高。

第二幕:一个简单的例子:把 go/xxx 重定向到特定页面

假设我们想把所有 go/xxx 形式的 URL 重定向到 /?special_link=xxx。 我们可以这样写:

add_action( 'init', 'my_custom_rewrite_rule' );
function my_custom_rewrite_rule() {
    add_rewrite_rule(
        '^go/([^/]*)/?',
        'index.php?special_link=$matches[1]',
        'top'
    );
}

代码解释:

  1. add_action( 'init', 'my_custom_rewrite_rule' );: 这行代码告诉 WordPress,在 init 钩子(也就是 WordPress 初始化的时候)执行 my_custom_rewrite_rule 函数。

  2. *`^go/([^/])/?`**: 这是正则表达式。

    • ^:表示从字符串的开头开始匹配。
    • go/:匹配字面字符串 "go/"。
    • ([^/]*):这是一个捕获组。
      • (...):表示一个捕获组,会将匹配到的内容保存起来,方便后面使用。
      • [^/]:表示匹配除了斜杠 / 以外的任何字符。
      • *:表示匹配零个或多个前面的字符(这里是除了斜杠以外的字符)。
      • 所以,([^/]*) 匹配 "go/" 之后的所有字符,直到遇到下一个斜杠为止(或者字符串结束)。
    • /:匹配一个斜杠 /
    • ?:表示前面的斜杠 / 是可选的,也就是说,URL 可以以 "go/xxx/" 结尾,也可以以 "go/xxx" 结尾。
    • $:表示字符串的结尾。
  3. index.php?special_link=$matches[1]: 这是重定向的目标 URL。

    • index.php:表示 WordPress 的入口文件。
    • ?special_link=:表示一个 URL 参数。
    • $matches[1]:这是正则表达式中第一个捕获组的内容,也就是 go/ 后面的字符串。 $matches 是一个数组,保存了正则表达式匹配到的所有内容。 $matches[0] 保存的是整个匹配到的字符串,$matches[1] 保存的是第一个捕获组的内容,以此类推。

重要提示:别忘了刷新固定链接!

添加或修改重写规则后,一定要去 WordPress 后台的 "设置 -> 固定链接" 页面,点击 "保存更改" 按钮。 这样做会刷新 WordPress 的重写规则,让新的规则生效。 否则,你会发现你的规则根本不起作用。 这就像你贴了新的货架标签,但没告诉顾客,他们还是只能靠货架编号找东西。

第三幕:深入正则表达式的海洋

正则表达式是 URL 重写的灵魂。 掌握了正则表达式,你就能驾驭各种复杂的 URL 结构。 下面是一些常用的正则表达式符号:

符号 含义 例子
. 匹配任意单个字符(除了换行符) a.c 可以匹配 "abc", "acc", "adc" 等
* 匹配前面的字符零次或多次 ab*c 可以匹配 "ac", "abc", "abbc" 等
+ 匹配前面的字符一次或多次 ab+c 可以匹配 "abc", "abbc" 等,但不能匹配 "ac"
? 匹配前面的字符零次或一次 ab?c 可以匹配 "ac", "abc"
[] 匹配方括号中的任意一个字符 [abc] 可以匹配 "a", "b", "c"
[^] 匹配不在方括号中的任意一个字符 [^abc] 可以匹配 "d", "e", "f"
() 捕获组,用于提取匹配到的内容 (ab)c 可以匹配 "abc",并将 "ab" 捕获
^ 匹配字符串的开头 ^abc 只能匹配以 "abc" 开头的字符串
$ 匹配字符串的结尾 abc$ 只能匹配以 "abc" 结尾的字符串
| 或,匹配 | 前后任意一个表达式 a|b 可以匹配 "a" 或 "b"
d 匹配一个数字字符 d+ 可以匹配 "123", "456"
w 匹配一个单词字符(字母、数字、下划线) w+ 可以匹配 "abc", "123", "a_b"
s 匹配一个空白字符(空格、制表符、换行符等) s+ 可以匹配一个或多个空白字符

第四幕:一个更复杂的例子:自定义文章类型和分类的 URL

假设我们有一个自定义文章类型 product 和一个自定义分类 product_category。 我们想让 product 的 URL 结构是 /products/category/product-name/

首先,我们需要注册自定义文章类型和分类:

add_action( 'init', 'register_product_post_type' );
function register_product_post_type() {
    $args = array(
        'public' => true,
        'label'  => 'Products',
        'rewrite' => array( 'slug' => 'products' ), // 基本的 slug
        'supports' => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
        'taxonomies' => array( 'product_category' ) // 关联分类
    );
    register_post_type( 'product', $args );

    $category_args = array(
        'hierarchical' => true,
        'label'        => 'Product Categories',
        'rewrite'      => array( 'slug' => 'products/category' ) // 分类的 slug
    );
    register_taxonomy( 'product_category', 'product', $category_args );
}

这段代码注册了一个名为 product 的自定义文章类型,并指定了它的 slug 为 products。 同时,它也注册了一个名为 product_category 的自定义分类,并指定了它的 slug 为 products/category

接下来,我们需要添加重写规则来处理 product 的 URL:

add_filter( 'post_type_link', 'product_permalink', 1, 3 );
function product_permalink( $permalink, $post, $leavename ) {

    if ( 'product' != $post->post_type ) {
        return $permalink;
    }

    // 获取分类
    $terms = get_the_terms( $post->ID, 'product_category' );

    if ( ! $terms ) {
        return str_replace( '%product_category%', 'uncategorized', $permalink );  // 如果没有分类,使用 uncategorized
    }

    $category = $terms[0]->slug; // 获取第一个分类的 slug

    $permalink = str_replace( '%product_category%', $category, $permalink );

    return $permalink;
}

add_filter( 'rewrite_rules_array', 'product_rewrite_rules' );
function product_rewrite_rules( $rules ) {
    $new_rules = array();
    $new_rules['products/category/(.+?)/([^/]+)/?$'] = 'index.php?product=$matches[2]';
    return $new_rules + $rules;
}

add_action( 'init', 'flush_rewrite_rules_on_product_register' );
function flush_rewrite_rules_on_product_register() {
    if ( get_option( 'product_rewrite_rules_flushed' ) ) {
        return;
    }
    register_product_post_type(); // 确保文章类型已注册
    flush_rewrite_rules();
    update_option( 'product_rewrite_rules_flushed', 1 );
}

代码解释:

  1. product_permalink 函数: 这个函数用于修改 product 的固定链接。它首先检查当前文章是否是 product 类型。如果是,则获取文章的第一个 product_category 分类的 slug,然后将固定链接中的 %product_category% 替换为分类的 slug。 如果文章没有分类,则将 %product_category% 替换为 uncategorized

  2. product_rewrite_rules 函数: 这个函数用于添加自定义的重写规则。它添加了一个新的重写规则 products/category/(.+?)/([^/]+)/?$,将匹配到的 URL 重定向到 index.php?product=$matches[2]

    • products/category/(.+?)/([^/]+)/?$ 这个正则表达式的含义是:

      • products/category/: 匹配字面字符串 "products/category/"。
      • (.+?): 匹配任意字符(除了换行符)一次或多次,非贪婪模式。 非贪婪模式意味着它会尽可能少地匹配字符。 这个捕获组用于匹配分类的 slug。
      • /: 匹配一个斜杠 /
      • ([^/]+): 匹配除了斜杠 / 以外的任意字符一次或多次。 这个捕获组用于匹配文章的 slug。
      • /?: 匹配一个可选的斜杠 /
      • $:匹配字符串的结尾
    • index.php?product=$matches[2]:将匹配到的 URL 重定向到 WordPress 的入口文件,并设置 product 参数为第二个捕获组的内容,也就是文章的 slug。

  3. flush_rewrite_rules_on_product_register 函数: 这个函数用于在注册 product 文章类型后刷新重写规则。 它使用 flush_rewrite_rules() 函数来刷新重写规则,并使用 update_option() 函数来设置一个选项,防止重复刷新重写规则。 频繁刷新重写规则会影响网站性能,所以只在文章类型注册后刷新一次即可。

重要提示:使用 flush_rewrite_rules() 的注意事项

flush_rewrite_rules() 函数会重新生成 WordPress 的 .htaccess 文件(如果你的服务器使用 Apache),或者更新 Nginx 的配置。 这个操作比较耗时,所以不应该频繁调用。 最好只在插件激活或主题切换时调用一次。 上面的代码使用了一个选项 product_rewrite_rules_flushed 来确保只在 product 文章类型注册后刷新一次重写规则。

第五幕:调试 URL 重写规则

URL 重写规则有时候会让人头疼,因为它们可能与其他规则冲突,或者正则表达式写错了。 下面是一些调试 URL 重写规则的技巧:

  1. 使用 var_dump()print_r() 打印 $wp_rewrite->rules 数组: $wp_rewrite->rules 数组包含了 WordPress 所有的重写规则。 你可以使用 var_dump()print_r() 函数来打印这个数组,查看你的规则是否被正确添加。

    add_action( 'init', 'debug_rewrite_rules' );
    function debug_rewrite_rules() {
        global $wp_rewrite;
        echo '<pre>';
        print_r( $wp_rewrite->rules );
        echo '</pre>';
    }
  2. 使用 rewrite_rules_array 过滤器: rewrite_rules_array 过滤器允许你修改 WordPress 的重写规则。 你可以使用这个过滤器来添加调试信息,例如打印当前的 URL。

    add_filter( 'rewrite_rules_array', 'debug_rewrite_rules_array' );
    function debug_rewrite_rules_array( $rules ) {
        echo '<pre>';
        print_r( $_SERVER['REQUEST_URI'] ); // 打印当前的 URL
        print_r( $rules ); // 打印所有的重写规则
        echo '</pre>';
        return $rules;
    }
  3. 使用 Query Monitor 插件: Query Monitor 是一个强大的 WordPress 调试插件。 它可以显示当前的查询、模板、钩子、语言文件等信息。 它还可以显示当前的重写规则,以及匹配到的规则。

  4. 使用在线正则表达式测试工具: 像 regex101.com 这样的工具可以让你测试你的正则表达式,查看它是否能正确匹配你想要匹配的 URL。

第六幕:一些常见的坑

  1. 重写规则的顺序: WordPress 从上到下依次匹配重写规则。 如果你的规则顺序不对,可能会导致某些规则永远无法被匹配到。 通常情况下,应该把最具体的规则放在最前面,最通用的规则放在最后面。

  2. 正则表达式的转义: 在正则表达式中,某些字符具有特殊含义,例如 .*? 等。 如果想匹配这些字符本身,需要使用反斜杠 进行转义。 例如,如果想匹配 example.com,需要写成 example.com

  3. 忘记刷新固定链接: 这是最常见的错误。 添加或修改重写规则后,一定要刷新固定链接,否则新的规则不会生效。

  4. 与其他插件冲突: 某些插件可能会修改 WordPress 的重写规则,导致你的规则无法正常工作。 可以尝试禁用其他插件,看看是否能解决问题。

总结:URL 重写的艺术

add_rewrite_rule() 函数是 WordPress 提供的一个强大工具,可以让你自定义 URL 结构,让你的网站更易于理解和 SEO 友好。 但是,URL 重写也需要谨慎使用,因为它可能会影响网站的性能和稳定性。 掌握了正则表达式和调试技巧,你就能驾驭 URL 重写的艺术,让你的网站焕发新的光彩。

最后的忠告:

不要过度使用 URL 重写。 复杂的 URL 结构可能会让你的网站更难维护。 尽量保持 URL 结构简洁明了。

好了,今天的讲座就到这里。 感谢大家的观看,希望对你们有所帮助! 如果有什么问题,欢迎在评论区留言。

发表回复

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