From 2b4cecf316f22967078ca5bfb91147b3a880609b Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 12 Sep 2019 22:16:08 +0000 Subject: [PATCH] Multisite: improve `sites_pre_query` and `networks_pre_query` filters, avoiding db queries. Improve the `pre_query` filters in multisite classes introduced in r44983. Return (non null) values immediately, avoiding the database queries entirely, similar to other `pre_query` filters. Props spacedmonkey, SergeyBiryukov, felipeelia. Fixes #47599. git-svn-id: https://develop.svn.wordpress.org/trunk@46100 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-network-query.php | 67 ++++++++++--------- src/wp-includes/class-wp-site-query.php | 65 ++++++++++-------- .../phpunit/tests/multisite/networkQuery.php | 9 +-- tests/phpunit/tests/multisite/siteQuery.php | 9 +-- 4 files changed, 79 insertions(+), 71 deletions(-) diff --git a/src/wp-includes/class-wp-network-query.php b/src/wp-includes/class-wp-network-query.php index 9db0a60462..a12656c702 100644 --- a/src/wp-includes/class-wp-network-query.php +++ b/src/wp-includes/class-wp-network-query.php @@ -197,50 +197,57 @@ class WP_Network_Query { */ do_action_ref_array( 'pre_get_networks', array( &$this ) ); - $network_ids = null; + $network_data = null; /** - * Filter the sites array before the query takes place. + * Filter the network data before the query takes place. * - * Return a non-null value to bypass WordPress's default site queries. + * Return a non-null value to bypass WordPress's default network queries. + * + * The expected return type from this filter depends on the value passed in the request query_vars. + * When `$this->query_vars['count']` is set, the filter should return the network count as an int. + * When `'ids' === $this->query_vars['fields']`, the filter should return an array of network ids. + * Otherwise the filter should return an array of WP_Network objects. * * @since 5.2.0 * - * @param array|null $site_ids Return an array of site data to short-circuit WP's site query, - * or null to allow WP to run its normal queries. - * @param WP_Network_Query $this The WP_Network_Query instance, passed by reference. + * @param array|null $network_data Return an array of network data to short-circuit WP's network query, + * the network count as an integer if `$this->query_vars['count']` is set, + * or null to allow WP to run its normal queries. + * @param WP_Network_Query $this The WP_Network_Query instance, passed by reference. */ - $network_ids = apply_filters_ref_array( 'networks_pre_query', array( $network_ids, &$this ) ); + $network_data = apply_filters_ref_array( 'networks_pre_query', array( $network_data, &$this ) ); - if ( null === $network_ids ) { + if ( null !== $network_data ) { + return $network_data; + } - // $args can include anything. Only use the args defined in the query_var_defaults to compute the key. - $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ); + // $args can include anything. Only use the args defined in the query_var_defaults to compute the key. + $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ); - // Ignore the $fields argument as the queried result will be the same regardless. - unset( $_args['fields'] ); + // Ignore the $fields argument as the queried result will be the same regardless. + unset( $_args['fields'] ); - $key = md5( serialize( $_args ) ); - $last_changed = wp_cache_get_last_changed( 'networks' ); + $key = md5( serialize( $_args ) ); + $last_changed = wp_cache_get_last_changed( 'networks' ); - $cache_key = "get_network_ids:$key:$last_changed"; - $cache_value = wp_cache_get( $cache_key, 'networks' ); + $cache_key = "get_network_ids:$key:$last_changed"; + $cache_value = wp_cache_get( $cache_key, 'networks' ); - if ( false === $cache_value ) { - $network_ids = $this->get_network_ids(); - if ( $network_ids ) { - $this->set_found_networks(); - } - - $cache_value = array( - 'network_ids' => $network_ids, - 'found_networks' => $this->found_networks, - ); - wp_cache_add( $cache_key, $cache_value, 'networks' ); - } else { - $network_ids = $cache_value['network_ids']; - $this->found_networks = $cache_value['found_networks']; + if ( false === $cache_value ) { + $network_ids = $this->get_network_ids(); + if ( $network_ids ) { + $this->set_found_networks(); } + + $cache_value = array( + 'network_ids' => $network_ids, + 'found_networks' => $this->found_networks, + ); + wp_cache_add( $cache_key, $cache_value, 'networks' ); + } else { + $network_ids = $cache_value['network_ids']; + $this->found_networks = $cache_value['found_networks']; } if ( $this->found_networks && $this->query_vars['number'] ) { diff --git a/src/wp-includes/class-wp-site-query.php b/src/wp-includes/class-wp-site-query.php index ec42cde2d2..64b139dbbf 100644 --- a/src/wp-includes/class-wp-site-query.php +++ b/src/wp-includes/class-wp-site-query.php @@ -288,50 +288,57 @@ class WP_Site_Query { $this->meta_query_clauses = $this->meta_query->get_sql( 'blog', $wpdb->blogs, 'blog_id', $this ); } - $site_ids = null; + $site_data = null; /** - * Filter the sites array before the query takes place. + * Filter the site data before the get_sites query takes place. * * Return a non-null value to bypass WordPress's default site queries. * + * The expected return type from this filter depends on the value passed in the request query_vars: + * When `$this->query_vars['count']` is set, the filter should return the site count as an int. + * When `'ids' == $this->query_vars['fields']`, the filter should return an array of site ids. + * Otherwise the filter should return an array of WP_Site objects. + * * @since 5.2.0 * - * @param array|null $site_ids Return an array of site data to short-circuit WP's site query, - * or null to allow WP to run its normal queries. - * @param WP_Site_Query $this The WP_Site_Query instance, passed by reference. + * @param array|int|null $site_data Return an array of site data to short-circuit WP's site query, + * the site count as an integer if `$this->query_vars['count']` is set, + * or null to run the normal queries. + * @param WP_Site_Query $this The WP_Site_Query instance, passed by reference. */ - $site_ids = apply_filters_ref_array( 'sites_pre_query', array( $site_ids, &$this ) ); + $site_data = apply_filters_ref_array( 'sites_pre_query', array( $site_data, &$this ) ); - if ( null === $site_ids ) { + if ( null !== $site_data ) { + return $site_data; + } - // $args can include anything. Only use the args defined in the query_var_defaults to compute the key. - $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ); + // $args can include anything. Only use the args defined in the query_var_defaults to compute the key. + $_args = wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ); - // Ignore the $fields argument as the queried result will be the same regardless. - unset( $_args['fields'] ); + // Ignore the $fields argument as the queried result will be the same regardless. + unset( $_args['fields'] ); - $key = md5( serialize( $_args ) ); - $last_changed = wp_cache_get_last_changed( 'sites' ); + $key = md5( serialize( $_args ) ); + $last_changed = wp_cache_get_last_changed( 'sites' ); - $cache_key = "get_sites:$key:$last_changed"; - $cache_value = wp_cache_get( $cache_key, 'sites' ); + $cache_key = "get_sites:$key:$last_changed"; + $cache_value = wp_cache_get( $cache_key, 'sites' ); - if ( false === $cache_value ) { - $site_ids = $this->get_site_ids(); - if ( $site_ids ) { - $this->set_found_sites(); - } - - $cache_value = array( - 'site_ids' => $site_ids, - 'found_sites' => $this->found_sites, - ); - wp_cache_add( $cache_key, $cache_value, 'sites' ); - } else { - $site_ids = $cache_value['site_ids']; - $this->found_sites = $cache_value['found_sites']; + if ( false === $cache_value ) { + $site_ids = $this->get_site_ids(); + if ( $site_ids ) { + $this->set_found_sites(); } + + $cache_value = array( + 'site_ids' => $site_ids, + 'found_sites' => $this->found_sites, + ); + wp_cache_add( $cache_key, $cache_value, 'sites' ); + } else { + $site_ids = $cache_value['site_ids']; + $this->found_sites = $cache_value['found_sites']; } if ( $this->found_sites && $this->query_vars['number'] ) { diff --git a/tests/phpunit/tests/multisite/networkQuery.php b/tests/phpunit/tests/multisite/networkQuery.php index 0b48fdc8d5..0318998129 100644 --- a/tests/phpunit/tests/multisite/networkQuery.php +++ b/tests/phpunit/tests/multisite/networkQuery.php @@ -525,6 +525,7 @@ if ( is_multisite() ) : /** * @ticket 45749 + * @ticket 47599 */ public function test_networks_pre_query_filter_should_bypass_database_query() { global $wpdb; @@ -534,11 +535,7 @@ if ( is_multisite() ) : $num_queries = $wpdb->num_queries; $q = new WP_Network_Query(); - $results = $q->query( - array( - 'fields' => 'ids', - ) - ); + $results = $q->query( array() ); remove_filter( 'networks_pre_query', array( __CLASS__, 'filter_networks_pre_query' ), 10, 2 ); @@ -546,7 +543,7 @@ if ( is_multisite() ) : $this->assertSame( $num_queries, $wpdb->num_queries ); // We manually inserted a non-existing site and overrode the results with it. - $this->assertSame( array( 555 ), $q->networks ); + $this->assertSame( array( 555 ), $results ); // Make sure manually setting total_users doesn't get overwritten. $this->assertEquals( 1, $q->found_networks ); diff --git a/tests/phpunit/tests/multisite/siteQuery.php b/tests/phpunit/tests/multisite/siteQuery.php index c17f977932..c8bcff2256 100644 --- a/tests/phpunit/tests/multisite/siteQuery.php +++ b/tests/phpunit/tests/multisite/siteQuery.php @@ -914,6 +914,7 @@ if ( is_multisite() ) : /** * @ticket 45749 + * @ticket 47599 */ public function test_sites_pre_query_filter_should_bypass_database_query() { global $wpdb; @@ -923,11 +924,7 @@ if ( is_multisite() ) : $num_queries = $wpdb->num_queries; $q = new WP_Site_Query(); - $results = $q->query( - array( - 'fields' => 'ids', - ) - ); + $results = $q->query( array() ); remove_filter( 'sites_pre_query', array( __CLASS__, 'filter_sites_pre_query' ), 10, 2 ); @@ -935,7 +932,7 @@ if ( is_multisite() ) : $this->assertSame( $num_queries, $wpdb->num_queries ); // We manually inserted a non-existing site and overrode the results with it. - $this->assertSame( array( 555 ), $q->sites ); + $this->assertSame( array( 555 ), $results ); // Make sure manually setting total_users doesn't get overwritten. $this->assertEquals( 1, $q->found_sites );