一、现象:查询条件对了,结果却不听话
开发关注动态时间线时,需求很简单:只看关注对象最新发布的文章、问答和评论。查询用 author__in 限定作者:
$fp_q = new WP_Query(array('post_type' => 'post', 'post_status' => 'publish', 'author__in' => $follow_ids, 'posts_per_page' => 20, 'no_found_rows' => true));
打印 $follow_ids 是干净的 [3, 2, 58, 792],不含当前用户自己。但结果里偏偏混进了自己(作者 ID 为 1)的文章。
二、排查:数据没问题,问题在查询
先核对关注表数据没有被污染,再打印查询参数也一切正常。直到把演示站和生产站放在一起对比才发现:同样的主题代码,演示站正常、生产站异常。同代码不同环境表现不同,几乎可以断定是环境里有额外的插件或钩子在影响查询。
三、根因:WP_Query 并不是“独立查询”
很多开发者以为 new WP_Query() 是独立 new 出来的查询,不受任何钩子影响,这是个误区。WP_Query 内部有三层钩子:
- pre_get_posts:只在主查询(
is_main_query())时触发,new WP_Query()确实能躲过它; - parse_query:在 WP_Query 构造时触发,对所有查询生效,插件可以在这里直接改查询参数;
- posts_where / posts_clauses / posts_results 等:在 SQL 生成和结果返回阶段触发,同样对所有查询生效,插件可以直接改 SQL 条件,甚至往结果数组里塞内容。
所以“new 出来的”只是躲过了 pre_get_posts,后面两层照样能“改造”它。
四、真正的元凶:置顶文章(Sticky Posts)
进一步对比发现,混入的文章全部是置顶文章。WP_Query 默认 ignore_sticky_posts = false,当查询里没有 post__in / post__not_in 时,置顶文章会被特殊合并进查询;正常情况下置顶会被 author__in 过滤掉,但环境里某个插件或钩子把置顶文章从作者过滤中“漏”了出来,于是置顶的这几篇就混进了结果。
五、解决:用直接 SQL 绕开钩子链
既然 WP_Query 的钩子链不可控,最彻底的办法是绕开它,直接用 $wpdb 查询:
$fp_rows = $wpdb->get_results("SELECT ID, post_date FROM {$wpdb->posts} WHERE post_type='post' AND post_status='publish' AND post_author IN (" . implode(',', $follow_ids) . ") ORDER BY post_date DESC LIMIT 20");
三个要点:
post_author IN是硬过滤,置顶不置顶都进不来;- 拼进 SQL 的值必须经过
intval或$wpdb->prepare,防止注入; - 这个场景一次取 20 条、渲染完即弃,绕开对象缓存没有副作用。
六、经验总结
- 只要是“按 ID 硬过滤”语义的查询(关注流、收藏、指定作者等),直接 SQL 比 WP_Query 更可控、更可预期;
- 一套代码部署多个环境时,两边插件和配置可能不同,验证不能只在一个环境做完就收工;
- 置顶文章这个坑很隐蔽,用 WP_Query 做聚合查询时,建议显式加上
'ignore_sticky_posts' => true。
阅读全文
轻语博客








评论前必须登录!
立即登录 注册