Is it possible to completely stop WP_Query retrieving posts?

At the moment, it is not possible.

When 'pre_get_posts' runs, is too late to stop WP_Query to perform a query.

WordPress itself, when you try to query a taxonomy that does not exists, adds AND (0 = 1) to the WHERE clause of the SQL query, to ensure it returns no results very quickly…

There’s a trac ticket with a patch that will probably lands in core with WP 4.6, that introduces a new filter: 'posts_pre_query'. Returning an array on that filter will make WP_Query stop processing and use the array provided as its posts array.

This could somehow helps you in implementing what you are trying to do.

Waiting fot this, anything you could do is somehow hackish, the trick core itself uses is quite hackish as well.

Recently, I’m starting using a trick when I want to stop WordPress to do things that I can’t stop in a clean way: I throw an exception and catch it to continue application flow.

I’ll show you an example. Note all the code here is completely untested.

First of all, let’s write a custom exception:

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;
   }
}

The exception is designed to act as a sort of DTO to transport a query object, so that in a catch block you can get and use it.

Better explained with code:

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());
        }
    }
});

This should more or less work, however, there are a lot of hooks that you are not going to fire, for example "the_posts" and much more… if you have code that use one of those hooks to trigger in, it will break.

You can use the cached_query_set function to fire some of the hooks that your theme / plugins may require.

Leave a Comment