From 486961626c1ad8ae479649c3944abe806cabc1f8 Mon Sep 17 00:00:00 2001 From: Boone Gorges Date: Tue, 14 Jun 2016 02:00:29 +0000 Subject: [PATCH] Query: Allow plugins to supply post results instead of having `WP_Query` fetch them from the database. Returning a non-null value from the new `posts_pre_query` filter will cause `WP_Query` to skip its database query, so that posts data can be provided from elsewhere. This is useful in cases where post data may be mirrored in a separate location, such as an external search application. Developers should note that the `WP_Query` properties generally used to calculate pagination - specifically, `found_posts` and `max_num_pages`, which are determined by default in `set_found_posts()` - must be provided explicitly when using the `posts_pre_query` filter; since `WP_Query` will not be contacting the database, it will have no access to `SELECT FOUND_ROWS()`. The `WP_Query` instance is passed to `posts_pre_query` by reference, so that these properties can be set manually if needed. Props jpdavoutian, tlovett1. Fixes #36687. Built from https://develop.svn.wordpress.org/trunk@37692 git-svn-id: http://core.svn.wordpress.org/trunk@37658 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/query.php | 100 +++++++++++++++++++++++++--------------- wp-includes/version.php | 2 +- 2 files changed, 64 insertions(+), 38 deletions(-) diff --git a/wp-includes/query.php b/wp-includes/query.php index d6915de507..8ec85b2127 100644 --- a/wp-includes/query.php +++ b/wp-includes/query.php @@ -3556,8 +3556,29 @@ class WP_Query { $this->request = apply_filters_ref_array( 'posts_request', array( $this->request, &$this ) ); } + /** + * Filters the posts array before the query takes place. + * + * Return a non-null value to bypass WordPress's default post queries. + * + * Filtering functions that require pagination information are encouraged to set + * the `found_posts` and `max_num_pages` properties of the WP_Query object, + * passed to the filter by reference. If WP_Query does not perform a database + * query, it will not have enough information to generate these values itself. + * + * @since 4.6.0 + * + * @param array|null $posts Return an array of post data to short-circuit WP's query, + * or null to allow WP to run its normal queries. + * @param WP_Query $this The WP_Query instance, passed by reference. + */ + $this->posts = apply_filters_ref_array( 'posts_pre_query', array( null, &$this ) ); + if ( 'ids' == $q['fields'] ) { - $this->posts = $wpdb->get_col( $this->request ); + if ( null === $this->posts ) { + $this->posts = $wpdb->get_col( $this->request ); + } + $this->posts = array_map( 'intval', $this->posts ); $this->post_count = count( $this->posts ); $this->set_found_posts( $q, $limits ); @@ -3566,7 +3587,10 @@ class WP_Query { } if ( 'id=>parent' == $q['fields'] ) { - $this->posts = $wpdb->get_results( $this->request ); + if ( null === $this->posts ) { + $this->posts = $wpdb->get_results( $this->request ); + } + $this->post_count = count( $this->posts ); $this->set_found_posts( $q, $limits ); @@ -3581,54 +3605,56 @@ class WP_Query { return $r; } - $split_the_query = ( $old_request == $this->request && "$wpdb->posts.*" == $fields && !empty( $limits ) && $q['posts_per_page'] < 500 ); - - /** - * Filters whether to split the query. - * - * Splitting the query will cause it to fetch just the IDs of the found posts - * (and then individually fetch each post by ID), rather than fetching every - * complete row at once. One massive result vs. many small results. - * - * @since 3.4.0 - * - * @param bool $split_the_query Whether or not to split the query. - * @param WP_Query $this The WP_Query instance. - */ - $split_the_query = apply_filters( 'split_the_query', $split_the_query, $this ); - - if ( $split_the_query ) { - // First get the IDs and then fill in the objects - - $this->request = "SELECT $found_rows $distinct $wpdb->posts.ID FROM $wpdb->posts $join WHERE 1=1 $where $groupby $orderby $limits"; + if ( null === $this->posts ) { + $split_the_query = ( $old_request == $this->request && "$wpdb->posts.*" == $fields && !empty( $limits ) && $q['posts_per_page'] < 500 ); /** - * Filters the Post IDs SQL request before sending. + * Filters whether to split the query. + * + * Splitting the query will cause it to fetch just the IDs of the found posts + * (and then individually fetch each post by ID), rather than fetching every + * complete row at once. One massive result vs. many small results. * * @since 3.4.0 * - * @param string $request The post ID request. - * @param WP_Query $this The WP_Query instance. + * @param bool $split_the_query Whether or not to split the query. + * @param WP_Query $this The WP_Query instance. */ - $this->request = apply_filters( 'posts_request_ids', $this->request, $this ); + $split_the_query = apply_filters( 'split_the_query', $split_the_query, $this ); - $ids = $wpdb->get_col( $this->request ); + if ( $split_the_query ) { + // First get the IDs and then fill in the objects - if ( $ids ) { - $this->posts = $ids; - $this->set_found_posts( $q, $limits ); - _prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] ); + $this->request = "SELECT $found_rows $distinct $wpdb->posts.ID FROM $wpdb->posts $join WHERE 1=1 $where $groupby $orderby $limits"; + + /** + * Filters the Post IDs SQL request before sending. + * + * @since 3.4.0 + * + * @param string $request The post ID request. + * @param WP_Query $this The WP_Query instance. + */ + $this->request = apply_filters( 'posts_request_ids', $this->request, $this ); + + $ids = $wpdb->get_col( $this->request ); + + if ( $ids ) { + $this->posts = $ids; + _prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] ); + } else { + $this->posts = array(); + } } else { - $this->posts = array(); + $this->posts = $wpdb->get_results( $this->request ); } - } else { - $this->posts = $wpdb->get_results( $this->request ); - $this->set_found_posts( $q, $limits ); } - // Convert to WP_Post objects - if ( $this->posts ) + // Convert to WP_Post objects and set the found-post totals. + if ( $this->posts ) { $this->posts = array_map( 'get_post', $this->posts ); + $this->set_found_posts( $q, $limits ); + } if ( ! $q['suppress_filters'] ) { /** diff --git a/wp-includes/version.php b/wp-includes/version.php index e7c4a78b10..e99754371c 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -4,7 +4,7 @@ * * @global string $wp_version */ -$wp_version = '4.6-alpha-37691'; +$wp_version = '4.6-alpha-37692'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.