diff --git a/src/wp-includes/class-wp-user-query.php b/src/wp-includes/class-wp-user-query.php index c75f392c24..23f108dbd3 100644 --- a/src/wp-includes/class-wp-user-query.php +++ b/src/wp-includes/class-wp-user-query.php @@ -592,29 +592,47 @@ class WP_User_Query { $qv =& $this->query_vars; - $this->request = "SELECT $this->query_fields $this->query_from $this->query_where $this->query_orderby $this->query_limit"; + /** + * Filters the users array before the query takes place. + * + * Return a non-null value to bypass WordPress's default user queries. + * Filtering functions that require pagination information are encouraged to set + * the `total_users` property of the WP_User_Query object, passed to the filter + * by reference. If WP_User_Query does not perform a database query, it will not + * have enough information to generate these values itself. + * + * @since 5.0.3 + * + * @param array|null $results Return an array of user data to short-circuit WP's user query or null to allow WP to run its normal queries. + * @param WP_User_Query $this The WP_User_Query instance (passed by reference). + */ + $this->results = apply_filters_ref_array( 'users_pre_query', array( null, &$this ) ); - if ( is_array( $qv['fields'] ) || 'all' == $qv['fields'] ) { - $this->results = $wpdb->get_results( $this->request ); - } else { - $this->results = $wpdb->get_col( $this->request ); - } + if ( null === $this->results ) { + $this->request = "SELECT $this->query_fields $this->query_from $this->query_where $this->query_orderby $this->query_limit"; - if ( isset( $qv['count_total'] ) && $qv['count_total'] ) { - /** - * Filters SELECT FOUND_ROWS() query for the current WP_User_Query instance. - * - * @since 3.2.0 - * @since 5.1.0 Added the `$this` parameter. - * - * @global wpdb $wpdb WordPress database abstraction object. - * - * @param string $sql The SELECT FOUND_ROWS() query for the current WP_User_Query. - * @param WP_User_Query $this The current WP_User_Query instance. - */ - $found_users_query = apply_filters( 'found_users_query', 'SELECT FOUND_ROWS()', $this ); + if ( is_array( $qv['fields'] ) || 'all' == $qv['fields'] ) { + $this->results = $wpdb->get_results( $this->request ); + } else { + $this->results = $wpdb->get_col( $this->request ); + } - $this->total_users = (int) $wpdb->get_var( $found_users_query ); + if ( isset( $qv['count_total'] ) && $qv['count_total'] ) { + /** + * Filters SELECT FOUND_ROWS() query for the current WP_User_Query instance. + * + * @since 3.2.0 + * @since 5.0.3 Added the `$this` parameter. + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string $sql The SELECT FOUND_ROWS() query for the current WP_User_Query. + * @param WP_User_Query $this The current WP_User_Query instance. + */ + $found_users_query = apply_filters( 'found_users_query', 'SELECT FOUND_ROWS()', $this ); + + $this->total_users = (int) $wpdb->get_var( $found_users_query ); + } } if ( ! $this->results ) { diff --git a/tests/phpunit/tests/user/query.php b/tests/phpunit/tests/user/query.php index 13d73343a4..1330d8b5c9 100644 --- a/tests/phpunit/tests/user/query.php +++ b/tests/phpunit/tests/user/query.php @@ -1690,4 +1690,37 @@ class Tests_User_Query extends WP_UnitTestCase { /* must not include user that has same string in other fields */ $this->assertEquals( array(), $ids ); } + + /** + * @ticket 44169 + */ + public function test_users_pre_query_filter_should_bypass_database_query() { + global $wpdb; + + add_filter( 'users_pre_query', array( __CLASS__, 'filter_users_pre_query' ), 10, 2 ); + + $num_queries = $wpdb->num_queries; + $q = new WP_User_Query( + array( + 'fields' => 'ID', + ) + ); + + remove_filter( 'users_pre_query', array( __CLASS__, 'filter_users_pre_query' ), 10, 2 ); + + // Make sure no queries were executed. + $this->assertSame( $num_queries, $wpdb->num_queries ); + + // We manually inserted a non-existing user and overrode the results with it. + $this->assertSame( array( 555 ), $q->results ); + + // Make sure manually setting total_users doesn't get overwritten. + $this->assertEquals( 1, $q->total_users ); + } + + public static function filter_users_pre_query( $posts, $query ) { + $query->total_users = 1; + + return array( 555 ); + } }