是否有可能完全停止WP_QUERY检索帖子?

时间:2016-06-14 作者:dan9vu

我想用WP Redis 缓存整个$wp_query 键为的对象为$query_vars_hash.

这就是$wp_query 已添加到$wp_object_cache:

add_action(\'wp\', function($wp)
{
    if ( is_admin() ) return;

    global $wp_query;

    if ( !wp_cache_get($wp_query->query_vars_hash, \'globals\') )
    {
        wp_cache_add($wp_query->query_vars_hash, $wp_query, \'globals\');
    }
});
然后,我需要检查查询之前是否已经缓存过WP_Query 可以检索帖子:

add_action(\'pre_get_posts\', function($query)
{
    if ( is_admin() ) return;

    $cached_query = wp_cache_get($query->query_vars_hash, \'globals\');

    if ($cached_query)
    {
        $GLOBALS[\'wp_query\'] = &$cached_query;

        return; // Return immediately to prevent retrieving posts again.
    }
});
Problem:

returnexit 在这种情况下不起作用。然后WP_Query 仍将点击数据库以再次检索帖子。

Question:

不管插件是什么,是否可以完全停止WP_Query 正在检索帖子?

4 个回复
最合适的回答,由SO网友:gmazzap 整理而成

目前,这是不可能的。

什么时候\'pre_get_posts\' 跑,已经来不及停了WP_Query 执行查询。

WordPress本身,当您尝试查询不存在的分类时,会添加AND (0 = 1)WHERE 子句,以确保它不会很快返回任何结果。。。

有一个trac ticket 有一个补丁可能会在WP 4.6的核心中使用,它引入了一个新的过滤器:\'posts_pre_query\'. 在该筛选器上返回数组将使WP_Query 停止处理并将提供的数组用作其posts数组。

这可以帮助你实现你想要做的事情。

等着瞧吧,你能做的任何事都是某种程度上的黑客行为,核心本身使用的技巧也是非常黑客的。

最近,当我想停止WordPress做一些我无法以干净的方式停止的事情时,我开始使用一个技巧:我抛出一个异常并捕获它以继续应用程序流。

我给你举个例子。注意,这里的所有代码都没有经过测试。

首先,让我们编写一个自定义异常:

class My_StopWpQueryException extends Exception {

   private $query;

   public static forQuery(WP_Query $query) {
     $instance = new static();
     $instance->query = $query;

     return $instance;
   }

   public function wpQuery() {
     return $this->query;
   }
}
异常被设计为作为一种DTO来传输查询对象,以便在catch 阻止您可以获取并使用它。

更好地解释代码:

function maybe_cached_query(WP_Query $query) {
    $cached_query = wp_cache_get($query->query_vars_hash, \'globals\');
    if ($cached_query instanceof WP_Query)
       throw My_StopWpQueryException::forQuery($cached_query);
}

function cached_query_set(WP_Query $query) {
    $GLOBALS[\'wp_query\'] = $query;
    $GLOBALS[\'wp_the_query\'] = $query;
    // maybe some more fine-tuning here...
}

add_action(\'pre_get_posts\', function(WP_Query $query) {
    if ($query->is_main_query() && ! is_admin()) {
        try {
           maybe_cached_query($query);
        } catch(My_StopWpQueryException $e) {
           cached_query_set($e->wpQuery());
        }
    }
});
这或多或少应该是可行的,但是,例如,有很多钩子是你不会激发的"the_posts" 还有更多。。。如果您有使用其中一个钩子触发的代码,它将中断。

您可以使用cached_query_set 函数启动主题/插件可能需要的一些挂钩。

SO网友:Sumit

这是PHP问题,而不是WordPress问题。

@Mark 评论:

从操作返回不通过魔术从调用函数返回

这是真的。放置return 在函数中表示退出函数,在PHP文件中放置return表示退出文件。不要与PHP构造混淆exit() :P(关于PHP,您可能会找到更好的答案return).

回答你的问题

您可以通过获取单个列而不是完整表来减少查询负载。喜欢@birgire 在这里做的Remove the Homepage Query

也许是一个更好的答案。我只是分享了我所知道的:)

SO网友:Mark Kaplun

在4.6中(假设在发布之前没有任何更改),可以使用新的posts_pre_query 滤器https://core.trac.wordpress.org/ticket/36687

SO网友:OzTheGreat

是的,这取决于你想缓存什么。我也做了类似的事情来缓存主页上的主循环。基本上,您可以使用posts_requestposts_results 要劫持查询并命中缓存,请使用found_posts 更正分页。

从我们的代码中提取了一个非常粗糙的示例(未经测试),但您应该帮助您理解:

<?php
/**
 * Kill the query if we have the result in the cache
 * @var [type]
 */
add_filter( \'posts_request\', function( $request, $query ) {
    if ( is_home() && $query->is_main_query() ) {

        $page = ( get_query_var( \'paged\' ) ) ? get_query_var( \'paged\' ) : 1;

        $key = \'homepage_query_cache_\' . $page;

        if ( wp_cache_get( $key, \'cache_group\' ) )
            $request = null;

    }

    return $request;
}, 10, 2 );

/**
 * Get the result from the cache and set it as the query result
 * Or add the query result to the cache if it\'s not there
 * @var [type]
 */
add_filter( \'posts_results\', function( $posts, $query ) {

    if ( is_home() && $query->is_main_query() ) {

        $page = ( get_query_var( \'paged\' ) ) ? get_query_var( \'paged\' ) : 1;

        $key = \'homepage_query_cache_\' . $page;

        if ( $cached_posts = wp_cache_get( $key, \'cache_group\' ) ) {
            $posts = $cached_posts;
        } else {
            wp_cache_set( $key . \'_found_posts\', $query->found_posts, \'cache_group\', HOUR_IN_SECONDS );
            wp_cache_set( $key, $posts, \'cache_group\', HOUR_IN_SECONDS );
        }
    }

    return $posts;

}, 10, 2 );

/**
 * Correct the found posts number if we\'ve hijacked the query results
 * @var [type]
 */
add_filter( \'found_posts\', function( $num, $query ) {
    if ( is_home() && $query->is_main_query() ) {
        $page = ( get_query_var( \'paged\' ) ) ? get_query_var( \'paged\' ) : 1;

        $key = \'homepage_query_cache_\' . $page;

        if ( $found_posts = wp_cache_get( $key . \'_found_posts\', \'cache_group\' ) )
            $num = $found_posts;
    }

    return $num;
}, 10, 2 );
更多信息:https://www.reddit.com/r/Wordpress/comments/19crcn/best_practice_for_hijacking_main_loop_and_caching/

相关推荐

Load my plugin before cache

我正在为我的工作开发一个快速插件,我需要的是让我的插件尽早加载,因为它使用ob\\u start来缓冲正在发生的一切。我可以将插件作为第一个加载:function my_plugin_load_first() { $path = str_replace( WP_PLUGIN_DIR . \'/\', \'\', __FILE__ ); if ( $plugins = get_option( \'active_plugins\' ) ) { i