阐述 WordPress `pre_get_posts` 钩子源码：在 `WP_Query` 执行前如何修改查询参数。

admin
Wed, 13 Aug 2025 11:08:59 +0000
PHP, pre, wordpress, 源码

各位观众老爷们，晚上好！欢迎来到今天的 WordPress 源码解读小课堂。今天咱们聊聊 pre_get_posts 这个神奇的钩子，它就像一个在厨师做菜之前，你可以偷偷往锅里加点料的魔法开关。咱们保证让你听得懂、学得会、用得上！ 开场白：WP_Query，WordPress 的心脏 在深入 pre_get_posts 之前，咱得先了解 WP_Query 是个啥。简单来说，WP_Query 是 WordPress 里负责从数据库里捞数据的核心类。 无论是你访问首页、分类页、搜索结果页，还是自定义的页面，背后都离不开 WP_Query 在默默干活。 pre_get_posts：截胡的艺术 pre_get_posts 钩子，就给了我们一个机会，在 WP_Query 真正执行数据库查询之前，拦截并修改它的查询参数。想象一下，你原本想吃红烧肉，但通过 pre_get_posts，你可以让厨师把红烧肉变成糖醋里脊，是不是很棒？ pre_get_posts 的源码在哪里？ pre_get_posts 本身并不是一个函数，而是一个 action hook。它在 WP_Query::get_posts …

继续阅读“阐述 WordPress `pre_get_posts` 钩子源码：在 `WP_Query` 执行前如何修改查询参数。”

The post 阐述 WordPress `pre_get_posts` 钩子源码：在 `WP_Query` 执行前如何修改查询参数。 first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

各位观众老爷们，晚上好！欢迎来到今天的 WordPress 源码解读小课堂。今天咱们聊聊 pre_get_posts 这个神奇的钩子，它就像一个在厨师做菜之前，你可以偷偷往锅里加点料的魔法开关。咱们保证让你听得懂、学得会、用得上！

开场白：WP_Query，WordPress 的心脏

在深入 pre_get_posts 之前，咱得先了解 WP_Query 是个啥。简单来说，WP_Query 是 WordPress 里负责从数据库里捞数据的核心类。 无论是你访问首页、分类页、搜索结果页，还是自定义的页面，背后都离不开 WP_Query 在默默干活。

pre_get_posts：截胡的艺术

pre_get_posts 钩子，就给了我们一个机会，在 WP_Query 真正执行数据库查询之前，拦截并修改它的查询参数。想象一下，你原本想吃红烧肉，但通过 pre_get_posts，你可以让厨师把红烧肉变成糖醋里脊，是不是很棒？

pre_get_posts 的源码在哪里？

pre_get_posts 本身并不是一个函数，而是一个 action hook。它在 WP_Query::get_posts() 方法里被触发。 咱们简单追踪一下：

1. WP_Query::get_posts(): 这个方法是 WP_Query 类里真正执行查询的地方。

2. do_action_ref_array( 'pre_get_posts', array( &$this ) );: 在 get_posts() 方法的开头，你会看到这行代码。 do_action_ref_array() 就是触发 action hook 的函数。 pre_get_posts 就是我们今天要研究的钩子，而 &$this 则是把当前的 WP_Query 对象以引用的方式传递给所有挂载到 pre_get_posts 的函数。

举个栗子：修改首页的文章数量

假设你想在首页只显示 5 篇文章，而不是默认的 10 篇。你可以这样写：

function my_custom_pre_get_posts( $query ) {
  if ( $query->is_home() && $query->is_main_query() ) {
    $query->set( 'posts_per_page', 5 );
  }
}
add_action( 'pre_get_posts', 'my_custom_pre_get_posts' );

这段代码做了什么？

my_custom_pre_get_posts( $query ): 这是一个自定义函数，它接收一个 $query 对象，这个对象就是当前的 WP_Query 实例。

$query->is_home(): 检查当前是否是首页。

$query->is_main_query(): 检查当前是否是主查询。这个非常重要，因为 WordPress 里可能有很多 WP_Query 实例，我们通常只想修改主查询。

$query->set( 'posts_per_page', 5 ): 使用 set() 方法修改查询参数。这里我们把 posts_per_page 设置为 5。

add_action( 'pre_get_posts', 'my_custom_pre_get_posts' ): 把我们的自定义函数挂载到 pre_get_posts 钩子上。

几个重要的函数和方法

在 pre_get_posts 里，你经常会用到以下几个函数和方法：

函数/方法 | 作用
$query->is_home() | 检查是否是首页
$query->is_single() | 检查是否是单篇文章页
$query->is_archive() | 检查是否是文章归档页（分类、标签、作者等）
$query->is_category() | 检查是否是分类页
$query->is_tag() | 检查是否是标签页
$query->is_search() | 检查是否是搜索结果页
$query->is_main_query() | 检查是否是主查询
$query->set( $key, $value ) | 设置查询参数。 $key 是参数名，$value 是参数值。
$query->get( $key ) | 获取查询参数。 $key 是参数名。

更高级的用法：根据用户角色修改查询

假设你想让管理员看到所有文章，而普通用户只能看到已发布的文章。你可以这样做：

function my_custom_pre_get_posts( $query ) {
  if ( ! is_admin() && $query->is_main_query() ) {
    $current_user = wp_get_current_user();
    if ( ! in_array( 'administrator', (array) $current_user->roles ) ) {
      $query->set( 'post_status', 'publish' );
    }
  }
}
add_action( 'pre_get_posts', 'my_custom_pre_get_posts' );

这段代码做了什么？

! is_admin(): 确保代码不在后台运行。

wp_get_current_user(): 获取当前用户信息。

! in_array( 'administrator', (array) $current_user->roles ): 检查当前用户是否是管理员。

$query->set( 'post_status', 'publish' ): 如果用户不是管理员，就把 post_status 设置为 publish，只显示已发布的文章。

再来一个例子：自定义文章类型的排序

假设你有一个自定义文章类型 product，你想按照价格（自定义字段 price）排序。

function my_custom_pre_get_posts( $query ) {
  if ( ! is_admin() && $query->is_main_query() && $query->is_post_type_archive( 'product' ) ) {
    $query->set( 'orderby', 'meta_value_num' );
    $query->set( 'meta_key', 'price' );
    $query->set( 'order', 'ASC' ); // 升序
  }
}
add_action( 'pre_get_posts', 'my_custom_pre_get_posts' );

这段代码做了什么？

$query->is_post_type_archive( 'product' ): 检查当前是否是 product 这种文章类型的归档页。

$query->set( 'orderby', 'meta_value_num' ): 设置排序方式为按照数字类型的自定义字段排序。

$query->set( 'meta_key', 'price' ): 设置自定义字段的键名为 price。

$query->set( 'order', 'ASC' ): 设置排序顺序为升序。

pre_get_posts 的使用注意事项

1. is_main_query() 的重要性: 一定要检查是否是主查询。否则，你可能会影响到 WordPress 后台或者其他地方的查询，导致意想不到的问题。

2. 性能问题: pre_get_posts 会在每次查询之前都执行，所以你的代码要尽量简洁高效。避免执行复杂的逻辑或者数据库查询。

3. 调试: 如果你的代码没有生效，可以使用 var_dump( $query ) 或者 wp_die( print_r( $query, true ) ) 来查看 $query 对象的内容，看看你的修改是否正确。

进阶技巧：使用 parse_query 钩子

除了 pre_get_posts，还有一个类似的钩子叫做 parse_query。它们的主要区别在于：

parse_query: 在 WP_Query 对象初始化之后，但是在解析查询字符串之前触发。你可以在这里访问原始的查询字符串，并进行修改。

pre_get_posts: 在 WP_Query 对象解析完查询字符串之后，但在真正执行查询之前触发。你在这里可以修改已经解析好的查询参数。

通常情况下，pre_get_posts 更常用，因为它更方便修改查询参数。但是，如果你需要访问或修改原始的查询字符串，parse_query 可能会更适合你。

案例分析：实现一个自定义的排序选项

假设你想在文章归档页添加一个自定义的排序选项，允许用户按照文章标题的长度排序。

1. 前端添加排序选项: 首先，你需要在文章归档页添加一个下拉菜单，让用户选择排序方式。这部分涉及到 HTML 和 PHP，这里只给出 HTML 部分的示例：


默认排序
标题长度升序
标题长度降序


2. 获取用户选择的排序方式: 当用户选择排序方式并提交表单时，你需要获取用户选择的值。

$orderby_title_length = isset( $_GET['orderby_title_length'] ) ? $_GET['orderby_title_length'] : 'default';

3. 使用 pre_get_posts 修改查询: 接下来，你可以使用 pre_get_posts 钩子，根据用户选择的排序方式修改查询。

function my_custom_pre_get_posts( $query ) {
  if ( ! is_admin() && $query->is_main_query() && $query->is_archive() && isset( $_GET['orderby_title_length'] ) ) {
    $orderby_title_length = $_GET['orderby_title_length'];

    if ( $orderby_title_length === 'title_length_asc' ) {
      add_filter( 'posts_orderby', 'my_custom_orderby_title_length_asc' );
    } elseif ( $orderby_title_length === 'title_length_desc' ) {
      add_filter( 'posts_orderby', 'my_custom_orderby_title_length_desc' );
    }
  }
}
add_action( 'pre_get_posts', 'my_custom_pre_get_posts' );

function my_custom_orderby_title_length_asc( $orderby ) {
  global $wpdb;
  return "LENGTH({$wpdb->posts}.post_title) ASC";
}

function my_custom_orderby_title_length_desc( $orderby ) {
  global $wpdb;
  return "LENGTH({$wpdb->posts}.post_title) DESC";
}

这段代码做了什么？

isset( $_GET['orderby_title_length'] ): 检查 orderby_title_length 参数是否存在。

add_filter( 'posts_orderby', 'my_custom_orderby_title_length_asc' ): 根据用户选择的排序方式，添加相应的 posts_orderby 过滤器。 posts_orderby 是另一个钩子，允许你修改 SQL 查询语句的 ORDER BY 部分。

my_custom_orderby_title_length_asc( $orderby ) 和 my_custom_orderby_title_length_desc( $orderby ): 这两个函数分别返回按照标题长度升序和降序排序的 SQL 语句。

总结

pre_get_posts 钩子是 WordPress 里一个非常强大和灵活的工具，它可以让你在 WP_Query 执行之前，对查询参数进行各种各样的修改。 掌握 pre_get_posts，你就可以轻松地实现各种自定义的查询需求，让你的 WordPress 网站更加个性化和强大。 但请记住，能力越大，责任越大，使用 pre_get_posts 时一定要小心谨慎，避免影响网站的性能和稳定性。

今天的课程就到这里，希望大家有所收获！ 下课！