For a project I needed to heavily modify the behavior of WP_Query. So what I did was extending WP_Query basically like this:

class eacf_shared_query extends \WP_Query {

    function query( $query ) {

        // do things before ...

        $posts = parent::query($query);

        // do things after ...

        return $posts;

This works like a charm for all my custom queries for which I now use my extended class, but I'm unable to find a way to stuff this into the main_query.

So my question is: Is there any known way to replace/extend the WP_Query class in the main_query?

I did find the above to be the only solution and didn't get any further with using the filters supplied within the original class cause:

  • There seems to be no filter pair that is guaranteed to be run before and after the query so I can't safely achieve a construct like in the example above.
  • My "do things after" logic contains nested calls to WP_Query() which too easily end up in a query-loop if I use filters.
  • The SQL filters seem to be not powerfull enough to create a single query fetching all of the posts (like building a UNION query). I'm in the need of merging posts from different blogs in one query and it seems this can't be achieved with filters like posts_clauses_request or query.
  • Have you tried using pre_get_posts? If so: what problems did you experience? Commented Oct 30, 2013 at 13:35
  • @ChipBennett I didn't find a secure way to avoid the danger of query loops, i.e. attaching a filter to pre_get_posts which uses get_posts() itself was always causing a loop of death under some strange circumstances ...
    – s1lv3r
    Commented Oct 30, 2013 at 15:26
  • 1
    I'm trying this same thing. In your pre_get_posts filter you can do a check if( $query instanceof WP_Query ){ $query = new Extended_WP_Query( $query->query_vars ); } but I'm seeing strange results like it's obliterated my query vars. I think it's because the parent class has lots of &$this references in it. Commented Jan 28, 2014 at 18:22
  • @sanchothefat query_posts() is PHP-developers hell. :-) You could try to overwrite global $wp_the_query; in one of the earlier hooks like parse_request. I guess pre_get_posts is too late for this cause you are already in the context of the class you try to overwrite. Have a look here for more details: github.com/WordPress/WordPress/blob/master/wp-settings.php#L251 and github.com/WordPress/WordPress/blob/master/wp-includes/…
    – s1lv3r
    Commented Jan 28, 2014 at 21:00
  • 1
    @s1lv3r I'll give setup_theme or do_parse_request a go. All this to order by distance for latitude/longitude! Commented Jan 29, 2014 at 11:43

2 Answers 2


Ok so it's doable but could potentially mess up a few things down the line so it's at your own risk, but then so is everything :)

I've been trying to do this in order to use a GeoQuery extension class to order events by distance from some location.

The simplest way to do it is this:

add_action( 'after_setup_theme', 'alt_query_class', 1, 0 );
function alt_query_class() {
    if ( ! is_admin() && $GLOBALS[ 'wp_query' ] instanceof WP_Query ) {
         $GLOBALS[ 'wp_the_query' ] = new WP_Query_Extended();
         $GLOBALS[ 'wp_query' ] = $GLOBALS[ 'wp_the_query' ];

In your extended query class you would just do what you have above but if your added functionality is something that's not desirable for every query run then you'd have to do some setup/teardown logic because we're replacing every instance of WP_Query with the new one.

In my case I used the pre_get_posts filter with a late priority to check whether I needed to add my functionality or not after all the query manipulations were done.

class WP_Query_Extended extends WP_Query {

    function query( $args = array() ) {

        // setup functions
        add_filter( 'pre_get_posts', array( $this, 'check_setup_needed' ), 1000 );

        // run the query
        parent::query( $args );

        // tear down functions

    function check_setup_needed( $query ) {

        // test setup criteria
        if ( $query->get( 'somevar' ) ) {

        return $query;

    function setup() {
        // add any filters etc... here

    function teardown() {
        // remove filters etc... here
        remove_filter( 'pre_get_posts', array( $this, 'check_setup_needed' ) );



There's a big gotcha with this - if someone uses query_posts() eg. could be in a plugin or if you're writing a plugin then maybe a theme then that function resets the $wp_query global back to a WP_Query instance and there's no filter with which to change it again afterwards.

If you actually need to do a query_posts() (you shouldn't!) you can do $wp_query->query( $args ) instead.


I'd suggest you hook a filter to the posts_results hook to replace $posts with the result of your custom query. However this method does produce redundant processing as the main query is processed first, and then the results are "overwritten" with your customized results.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.