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:
- first remove all possible filters that can interfere with the query, and store them inside an helper array
- 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
- immediately after this query is ran, all original filters (if there were any) are restored and all subsequent queries will works as expected.
WP_Query()
to return no results may or may not be the best way to answer that question. It also might be helpful if you would describe the search pattern that you want to be unqueryable. Knowing the search pattern might help ferret out a solution.