Can I force WP_Query to return no results?

I’m working on a website with a search feature that allows users to search through a lot of post meta. There is a specific search pattern that I would like to forcibly return no results for. The WP_Query technically will find results in the database, but I’d like to override that somehow to force it to return no results to trigger the if( $example->have_posts() ) to fail.

Is there some sort of parameter I can pass to WP_Query like 'force_no_results' => true that will force it to return no results?

Related posts

Leave a Reply

3 comments

  1. Curiously there is no clean/explicit way to short circuit WP_Query.

    If it’s main query you might work something out around WP->parse_request(), there seems to be relatively recent (3.5) do_parse_request filter there.

    But for WP_Query itself dirty hacks are usually in order, such as short-circuiting SQL query by adding AND 1=0 via posts_where filter, etc.

  2. The problems on set a query parameter to unexistent value are 2:

    • The query will run, so even if you already know will be no results there is a little performance price to pay
    • WordPress queries has 19 different 'posts_*' filter hooks ('posts_where', 'post_join', etc..) that act on query, so you can never be sure that even setting unexistent param the query return no results, a simple OR clause returned by a filter make return something.

    You need a little bit hardcore routine to be sure a query return no result and there is no (or very minimun) performance issue.

    To trigger that routine, you can use every method, technically you can pass any argument to WP_Query, event arguments that doesn’t exists.

    So if you like something like 'force_no_results' => true, you can use it like so:

    $a = new WP_Query( array( 's' => 'foo', 'force_no_results' => true ) );
    

    and add a callback running on 'pre_get_posts' that do the hard work:

    add_action( 'pre_get_posts', function( $q ) {
      if (array_key_exists('force_no_results', $q->query) && $q->query['force_no_results']) {
        $q->query = $q->query_vars = array();
        $added = array();
        $filters = array(
          'where', 'where_paged', 'join', 'join_paged', 'groupby', 'orderby', 'distinct',
          'limits', 'fields', 'request', 'clauses', 'where_request', 'groupby_request',
          'join_request', 'orderby_request', 'distinct_request','fields_request',
          'limits_request', 'clauses_request'
        );
        // remove all possible interfering filter and save for later restore
        foreach ( $filters as $f ) {
          if ( isset($GLOBALS['wp_filter']["posts_{$f}"]) ) {
            $added["posts_{$f}"] = $GLOBALS['wp_filter']["posts_{$f}"];
            unset($GLOBALS['wp_filter']["posts_{$f}"]);
          }
        }
        // be sure filters are not suppressed
        $q->set( 'suppress_filters', FALSE );
        $done = 0;
        // use a filter to return a non-sense request
        add_filter('posts_request', function( $r ) use( &$done ) {
          if ( $done === 0 ) { $done = 1;
            $r = "SELECT ID FROM {$GLOBALS['wpdb']->posts} WHERE 0 = 1";
          }
          return $r;
        });
        // restore any filter that was added and we removed
        add_filter('posts_results', function( $posts ) use( &$done, $added ) {
          if ( $done === 1 ) { $done = 2;
            foreach ( $added as $hook => $filters ) {
              $GLOBALS['wp_filter'][$hook] = $filters;
            }
          }
          return $posts;
        });
      }
    }, PHP_INT_MAX );
    

    What this code does is run on 'pre_get_posts' as late as possible. If the argument ‘force_no_results’ is present in the query, then:

    1. first remove all possible filters that can interfere with the query, and store them inside an helper array
    2. after be sure the filter are triggered, adda filter that return this kind of request: SELECT ID FROM wp_posts WHERE 0 = 1 once all filters are removed, there is no possibilities this query is changed and it is very fast, and has no result for sure
    3. immediately after this query is ran, all original filters (if there were any) are restored and all subsequent queries will works as expected.