wp/wp-includes/class-wp-user-query.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    87 	 * @param array $args Query vars, as passed to `WP_User_Query`.
    87 	 * @param array $args Query vars, as passed to `WP_User_Query`.
    88 	 * @return array Complete query variables with undefined ones filled in with defaults.
    88 	 * @return array Complete query variables with undefined ones filled in with defaults.
    89 	 */
    89 	 */
    90 	public static function fill_query_vars( $args ) {
    90 	public static function fill_query_vars( $args ) {
    91 		$defaults = array(
    91 		$defaults = array(
    92 			'blog_id' => get_current_blog_id(),
    92 			'blog_id'             => get_current_blog_id(),
    93 			'role' => '',
    93 			'role'                => '',
    94 			'role__in' => array(),
    94 			'role__in'            => array(),
    95 			'role__not_in' => array(),
    95 			'role__not_in'        => array(),
    96 			'meta_key' => '',
    96 			'meta_key'            => '',
    97 			'meta_value' => '',
    97 			'meta_value'          => '',
    98 			'meta_compare' => '',
    98 			'meta_compare'        => '',
    99 			'include' => array(),
    99 			'include'             => array(),
   100 			'exclude' => array(),
   100 			'exclude'             => array(),
   101 			'search' => '',
   101 			'search'              => '',
   102 			'search_columns' => array(),
   102 			'search_columns'      => array(),
   103 			'orderby' => 'login',
   103 			'orderby'             => 'login',
   104 			'order' => 'ASC',
   104 			'order'               => 'ASC',
   105 			'offset' => '',
   105 			'offset'              => '',
   106 			'number' => '',
   106 			'number'              => '',
   107 			'paged' => 1,
   107 			'paged'               => 1,
   108 			'count_total' => true,
   108 			'count_total'         => true,
   109 			'fields' => 'all',
   109 			'fields'              => 'all',
   110 			'who' => '',
   110 			'who'                 => '',
   111 			'has_published_posts' => null,
   111 			'has_published_posts' => null,
   112 			'nicename' => '',
   112 			'nicename'            => '',
   113 			'nicename__in' => array(),
   113 			'nicename__in'        => array(),
   114 			'nicename__not_in' => array(),
   114 			'nicename__not_in'    => array(),
   115 			'login' => '',
   115 			'login'               => '',
   116 			'login__in' => array(),
   116 			'login__in'           => array(),
   117 			'login__not_in' => array()
   117 			'login__not_in'       => array(),
   118 		);
   118 		);
   119 
   119 
   120 		return wp_parse_args( $args, $defaults );
   120 		return wp_parse_args( $args, $defaults );
   121 	}
   121 	}
   122 
   122 
   131 	 * @since 4.4.0 Added 'paged', 'role__in', and 'role__not_in' parameters. The 'role' parameter was updated to
   131 	 * @since 4.4.0 Added 'paged', 'role__in', and 'role__not_in' parameters. The 'role' parameter was updated to
   132 	 *              permit an array or comma-separated list of values. The 'number' parameter was updated to support
   132 	 *              permit an array or comma-separated list of values. The 'number' parameter was updated to support
   133 	 *              querying for all users with using -1.
   133 	 *              querying for all users with using -1.
   134 	 * @since 4.7.0 Added 'nicename', 'nicename__in', 'nicename__not_in', 'login', 'login__in',
   134 	 * @since 4.7.0 Added 'nicename', 'nicename__in', 'nicename__not_in', 'login', 'login__in',
   135 	 *              and 'login__not_in' parameters.
   135 	 *              and 'login__not_in' parameters.
   136 	 *
       
   137 	 *
   136 	 *
   138 	 * @global wpdb $wpdb WordPress database abstraction object.
   137 	 * @global wpdb $wpdb WordPress database abstraction object.
   139 	 * @global int  $blog_id
   138 	 * @global int  $blog_id
   140 	 *
   139 	 *
   141 	 * @param string|array $query {
   140 	 * @param string|array $query {
   211 	public function prepare_query( $query = array() ) {
   210 	public function prepare_query( $query = array() ) {
   212 		global $wpdb;
   211 		global $wpdb;
   213 
   212 
   214 		if ( empty( $this->query_vars ) || ! empty( $query ) ) {
   213 		if ( empty( $this->query_vars ) || ! empty( $query ) ) {
   215 			$this->query_limit = null;
   214 			$this->query_limit = null;
   216 			$this->query_vars = $this->fill_query_vars( $query );
   215 			$this->query_vars  = $this->fill_query_vars( $query );
   217 		}
   216 		}
   218 
   217 
   219 		/**
   218 		/**
   220 		 * Fires before the WP_User_Query has been parsed.
   219 		 * Fires before the WP_User_Query has been parsed.
   221 		 *
   220 		 *
   229 		 */
   228 		 */
   230 		do_action( 'pre_get_users', $this );
   229 		do_action( 'pre_get_users', $this );
   231 
   230 
   232 		// Ensure that query vars are filled after 'pre_get_users'.
   231 		// Ensure that query vars are filled after 'pre_get_users'.
   233 		$qv =& $this->query_vars;
   232 		$qv =& $this->query_vars;
   234 		$qv =  $this->fill_query_vars( $qv );
   233 		$qv = $this->fill_query_vars( $qv );
   235 
   234 
   236 		if ( is_array( $qv['fields'] ) ) {
   235 		if ( is_array( $qv['fields'] ) ) {
   237 			$qv['fields'] = array_unique( $qv['fields'] );
   236 			$qv['fields'] = array_unique( $qv['fields'] );
   238 
   237 
   239 			$this->query_fields = array();
   238 			$this->query_fields = array();
   240 			foreach ( $qv['fields'] as $field ) {
   239 			foreach ( $qv['fields'] as $field ) {
   241 				$field = 'ID' === $field ? 'ID' : sanitize_key( $field );
   240 				$field                = 'ID' === $field ? 'ID' : sanitize_key( $field );
   242 				$this->query_fields[] = "$wpdb->users.$field";
   241 				$this->query_fields[] = "$wpdb->users.$field";
   243 			}
   242 			}
   244 			$this->query_fields = implode( ',', $this->query_fields );
   243 			$this->query_fields = implode( ',', $this->query_fields );
   245 		} elseif ( 'all' == $qv['fields'] ) {
   244 		} elseif ( 'all' == $qv['fields'] ) {
   246 			$this->query_fields = "$wpdb->users.*";
   245 			$this->query_fields = "$wpdb->users.*";
   247 		} else {
   246 		} else {
   248 			$this->query_fields = "$wpdb->users.ID";
   247 			$this->query_fields = "$wpdb->users.ID";
   249 		}
   248 		}
   250 
   249 
   251 		if ( isset( $qv['count_total'] ) && $qv['count_total'] )
   250 		if ( isset( $qv['count_total'] ) && $qv['count_total'] ) {
   252 			$this->query_fields = 'SQL_CALC_FOUND_ROWS ' . $this->query_fields;
   251 			$this->query_fields = 'SQL_CALC_FOUND_ROWS ' . $this->query_fields;
   253 
   252 		}
   254 		$this->query_from = "FROM $wpdb->users";
   253 
   255 		$this->query_where = "WHERE 1=1";
   254 		$this->query_from  = "FROM $wpdb->users";
       
   255 		$this->query_where = 'WHERE 1=1';
   256 
   256 
   257 		// Parse and sanitize 'include', for use by 'orderby' as well as 'include' below.
   257 		// Parse and sanitize 'include', for use by 'orderby' as well as 'include' below.
   258 		if ( ! empty( $qv['include'] ) ) {
   258 		if ( ! empty( $qv['include'] ) ) {
   259 			$include = wp_parse_id_list( $qv['include'] );
   259 			$include = wp_parse_id_list( $qv['include'] );
   260 		} else {
   260 		} else {
   275 
   275 
   276 			foreach ( $post_types as &$post_type ) {
   276 			foreach ( $post_types as &$post_type ) {
   277 				$post_type = $wpdb->prepare( '%s', $post_type );
   277 				$post_type = $wpdb->prepare( '%s', $post_type );
   278 			}
   278 			}
   279 
   279 
   280 			$posts_table = $wpdb->get_blog_prefix( $blog_id ) . 'posts';
   280 			$posts_table        = $wpdb->get_blog_prefix( $blog_id ) . 'posts';
   281 			$this->query_where .= " AND $wpdb->users.ID IN ( SELECT DISTINCT $posts_table.post_author FROM $posts_table WHERE $posts_table.post_status = 'publish' AND $posts_table.post_type IN ( " . join( ", ", $post_types ) . " ) )";
   281 			$this->query_where .= " AND $wpdb->users.ID IN ( SELECT DISTINCT $posts_table.post_author FROM $posts_table WHERE $posts_table.post_status = 'publish' AND $posts_table.post_type IN ( " . join( ', ', $post_types ) . ' ) )';
   282 		}
   282 		}
   283 
   283 
   284 		// nicename
   284 		// nicename
   285 		if ( '' !== $qv['nicename']) {
   285 		if ( '' !== $qv['nicename'] ) {
   286 			$this->query_where .= $wpdb->prepare( ' AND user_nicename = %s', $qv['nicename'] );
   286 			$this->query_where .= $wpdb->prepare( ' AND user_nicename = %s', $qv['nicename'] );
   287 		}
   287 		}
   288 
   288 
   289 		if ( ! empty( $qv['nicename__in'] ) ) {
   289 		if ( ! empty( $qv['nicename__in'] ) ) {
   290 			$sanitized_nicename__in = array_map( 'esc_sql', $qv['nicename__in'] );
   290 			$sanitized_nicename__in = array_map( 'esc_sql', $qv['nicename__in'] );
   291 			$nicename__in = implode( "','", $sanitized_nicename__in );
   291 			$nicename__in           = implode( "','", $sanitized_nicename__in );
   292 			$this->query_where .= " AND user_nicename IN ( '$nicename__in' )";
   292 			$this->query_where     .= " AND user_nicename IN ( '$nicename__in' )";
   293 		}
   293 		}
   294 
   294 
   295 		if ( ! empty( $qv['nicename__not_in'] ) ) {
   295 		if ( ! empty( $qv['nicename__not_in'] ) ) {
   296 			$sanitized_nicename__not_in = array_map( 'esc_sql', $qv['nicename__not_in'] );
   296 			$sanitized_nicename__not_in = array_map( 'esc_sql', $qv['nicename__not_in'] );
   297 			$nicename__not_in = implode( "','", $sanitized_nicename__not_in );
   297 			$nicename__not_in           = implode( "','", $sanitized_nicename__not_in );
   298 			$this->query_where .= " AND user_nicename NOT IN ( '$nicename__not_in' )";
   298 			$this->query_where         .= " AND user_nicename NOT IN ( '$nicename__not_in' )";
   299 		}
   299 		}
   300 
   300 
   301 		// login
   301 		// login
   302 		if ( '' !== $qv['login']) {
   302 		if ( '' !== $qv['login'] ) {
   303 			$this->query_where .= $wpdb->prepare( ' AND user_login = %s', $qv['login'] );
   303 			$this->query_where .= $wpdb->prepare( ' AND user_login = %s', $qv['login'] );
   304 		}
   304 		}
   305 
   305 
   306 		if ( ! empty( $qv['login__in'] ) ) {
   306 		if ( ! empty( $qv['login__in'] ) ) {
   307 			$sanitized_login__in = array_map( 'esc_sql', $qv['login__in'] );
   307 			$sanitized_login__in = array_map( 'esc_sql', $qv['login__in'] );
   308 			$login__in = implode( "','", $sanitized_login__in );
   308 			$login__in           = implode( "','", $sanitized_login__in );
   309 			$this->query_where .= " AND user_login IN ( '$login__in' )";
   309 			$this->query_where  .= " AND user_login IN ( '$login__in' )";
   310 		}
   310 		}
   311 
   311 
   312 		if ( ! empty( $qv['login__not_in'] ) ) {
   312 		if ( ! empty( $qv['login__not_in'] ) ) {
   313 			$sanitized_login__not_in = array_map( 'esc_sql', $qv['login__not_in'] );
   313 			$sanitized_login__not_in = array_map( 'esc_sql', $qv['login__not_in'] );
   314 			$login__not_in = implode( "','", $sanitized_login__not_in );
   314 			$login__not_in           = implode( "','", $sanitized_login__not_in );
   315 			$this->query_where .= " AND user_login NOT IN ( '$login__not_in' )";
   315 			$this->query_where      .= " AND user_login NOT IN ( '$login__not_in' )";
   316 		}
   316 		}
   317 
   317 
   318 		// Meta query.
   318 		// Meta query.
   319 		$this->meta_query = new WP_Meta_Query();
   319 		$this->meta_query = new WP_Meta_Query();
   320 		$this->meta_query->parse_query_vars( $qv );
   320 		$this->meta_query->parse_query_vars( $qv );
   321 
   321 
   322 		if ( isset( $qv['who'] ) && 'authors' == $qv['who'] && $blog_id ) {
   322 		if ( isset( $qv['who'] ) && 'authors' == $qv['who'] && $blog_id ) {
   323 			$who_query = array(
   323 			$who_query = array(
   324 				'key' => $wpdb->get_blog_prefix( $blog_id ) . 'user_level',
   324 				'key'     => $wpdb->get_blog_prefix( $blog_id ) . 'user_level',
   325 				'value' => 0,
   325 				'value'   => 0,
   326 				'compare' => '!=',
   326 				'compare' => '!=',
   327 			);
   327 			);
   328 
   328 
   329 			// Prevent extra meta query.
   329 			// Prevent extra meta query.
   330 			$qv['blog_id'] = $blog_id = 0;
   330 			$qv['blog_id'] = $blog_id = 0;
   360 		if ( isset( $qv['role__not_in'] ) ) {
   360 		if ( isset( $qv['role__not_in'] ) ) {
   361 			$role__not_in = (array) $qv['role__not_in'];
   361 			$role__not_in = (array) $qv['role__not_in'];
   362 		}
   362 		}
   363 
   363 
   364 		if ( $blog_id && ( ! empty( $roles ) || ! empty( $role__in ) || ! empty( $role__not_in ) || is_multisite() ) ) {
   364 		if ( $blog_id && ( ! empty( $roles ) || ! empty( $role__in ) || ! empty( $role__not_in ) || is_multisite() ) ) {
   365 			$role_queries  = array();
   365 			$role_queries = array();
   366 
   366 
   367 			$roles_clauses = array( 'relation' => 'AND' );
   367 			$roles_clauses = array( 'relation' => 'AND' );
   368 			if ( ! empty( $roles ) ) {
   368 			if ( ! empty( $roles ) ) {
   369 				foreach ( $roles as $role ) {
   369 				foreach ( $roles as $role ) {
   370 					$roles_clauses[] = array(
   370 					$roles_clauses[] = array(
   404 			}
   404 			}
   405 
   405 
   406 			// If there are no specific roles named, make sure the user is a member of the site.
   406 			// If there are no specific roles named, make sure the user is a member of the site.
   407 			if ( empty( $role_queries ) ) {
   407 			if ( empty( $role_queries ) ) {
   408 				$role_queries[] = array(
   408 				$role_queries[] = array(
   409 					'key' => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',
   409 					'key'     => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',
   410 					'compare' => 'EXISTS',
   410 					'compare' => 'EXISTS',
   411 				);
   411 				);
   412 			}
   412 			}
   413 
   413 
   414 			// Specify that role queries should be joined with AND.
   414 			// Specify that role queries should be joined with AND.
   426 
   426 
   427 			$this->meta_query->parse_query_vars( $this->meta_query->queries );
   427 			$this->meta_query->parse_query_vars( $this->meta_query->queries );
   428 		}
   428 		}
   429 
   429 
   430 		if ( ! empty( $this->meta_query->queries ) ) {
   430 		if ( ! empty( $this->meta_query->queries ) ) {
   431 			$clauses = $this->meta_query->get_sql( 'user', $wpdb->users, 'ID', $this );
   431 			$clauses            = $this->meta_query->get_sql( 'user', $wpdb->users, 'ID', $this );
   432 			$this->query_from .= $clauses['join'];
   432 			$this->query_from  .= $clauses['join'];
   433 			$this->query_where .= $clauses['where'];
   433 			$this->query_where .= $clauses['where'];
   434 
   434 
   435 			if ( $this->meta_query->has_or_relation() ) {
   435 			if ( $this->meta_query->has_or_relation() ) {
   436 				$this->query_fields = 'DISTINCT ' . $this->query_fields;
   436 				$this->query_fields = 'DISTINCT ' . $this->query_fields;
   437 			}
   437 			}
   438 		}
   438 		}
   439 
   439 
   440 		// sorting
   440 		// sorting
   441 		$qv['order'] = isset( $qv['order'] ) ? strtoupper( $qv['order'] ) : '';
   441 		$qv['order'] = isset( $qv['order'] ) ? strtoupper( $qv['order'] ) : '';
   442 		$order = $this->parse_order( $qv['order'] );
   442 		$order       = $this->parse_order( $qv['order'] );
   443 
   443 
   444 		if ( empty( $qv['orderby'] ) ) {
   444 		if ( empty( $qv['orderby'] ) ) {
   445 			// Default order is by 'user_login'.
   445 			// Default order is by 'user_login'.
   446 			$ordersby = array( 'user_login' => $order );
   446 			$ordersby = array( 'user_login' => $order );
   447 		} elseif ( is_array( $qv['orderby'] ) ) {
   447 		} elseif ( is_array( $qv['orderby'] ) ) {
   458 			}
   458 			}
   459 
   459 
   460 			if ( is_int( $_key ) ) {
   460 			if ( is_int( $_key ) ) {
   461 				// Integer key means this is a flat array of 'orderby' fields.
   461 				// Integer key means this is a flat array of 'orderby' fields.
   462 				$_orderby = $_value;
   462 				$_orderby = $_value;
   463 				$_order = $order;
   463 				$_order   = $order;
   464 			} else {
   464 			} else {
   465 				// Non-integer key means this the key is the field and the value is ASC/DESC.
   465 				// Non-integer key means this the key is the field and the value is ASC/DESC.
   466 				$_orderby = $_key;
   466 				$_orderby = $_key;
   467 				$_order = $_value;
   467 				$_order   = $_value;
   468 			}
   468 			}
   469 
   469 
   470 			$parsed = $this->parse_orderby( $_orderby );
   470 			$parsed = $this->parse_orderby( $_orderby );
   471 
   471 
   472 			if ( ! $parsed ) {
   472 			if ( ! $parsed ) {
   488 		$this->query_orderby = 'ORDER BY ' . implode( ', ', $orderby_array );
   488 		$this->query_orderby = 'ORDER BY ' . implode( ', ', $orderby_array );
   489 
   489 
   490 		// limit
   490 		// limit
   491 		if ( isset( $qv['number'] ) && $qv['number'] > 0 ) {
   491 		if ( isset( $qv['number'] ) && $qv['number'] > 0 ) {
   492 			if ( $qv['offset'] ) {
   492 			if ( $qv['offset'] ) {
   493 				$this->query_limit = $wpdb->prepare("LIMIT %d, %d", $qv['offset'], $qv['number']);
   493 				$this->query_limit = $wpdb->prepare( 'LIMIT %d, %d', $qv['offset'], $qv['number'] );
   494 			} else {
   494 			} else {
   495 				$this->query_limit = $wpdb->prepare( "LIMIT %d, %d", $qv['number'] * ( $qv['paged'] - 1 ), $qv['number'] );
   495 				$this->query_limit = $wpdb->prepare( 'LIMIT %d, %d', $qv['number'] * ( $qv['paged'] - 1 ), $qv['number'] );
   496 			}
   496 			}
   497 		}
   497 		}
   498 
   498 
   499 		$search = '';
   499 		$search = '';
   500 		if ( isset( $qv['search'] ) )
   500 		if ( isset( $qv['search'] ) ) {
   501 			$search = trim( $qv['search'] );
   501 			$search = trim( $qv['search'] );
       
   502 		}
   502 
   503 
   503 		if ( $search ) {
   504 		if ( $search ) {
   504 			$leading_wild = ( ltrim($search, '*') != $search );
   505 			$leading_wild  = ( ltrim( $search, '*' ) != $search );
   505 			$trailing_wild = ( rtrim($search, '*') != $search );
   506 			$trailing_wild = ( rtrim( $search, '*' ) != $search );
   506 			if ( $leading_wild && $trailing_wild )
   507 			if ( $leading_wild && $trailing_wild ) {
   507 				$wild = 'both';
   508 				$wild = 'both';
   508 			elseif ( $leading_wild )
   509 			} elseif ( $leading_wild ) {
   509 				$wild = 'leading';
   510 				$wild = 'leading';
   510 			elseif ( $trailing_wild )
   511 			} elseif ( $trailing_wild ) {
   511 				$wild = 'trailing';
   512 				$wild = 'trailing';
   512 			else
   513 			} else {
   513 				$wild = false;
   514 				$wild = false;
   514 			if ( $wild )
   515 			}
   515 				$search = trim($search, '*');
   516 			if ( $wild ) {
       
   517 				$search = trim( $search, '*' );
       
   518 			}
   516 
   519 
   517 			$search_columns = array();
   520 			$search_columns = array();
   518 			if ( $qv['search_columns'] ) {
   521 			if ( $qv['search_columns'] ) {
   519 				$search_columns = array_intersect( $qv['search_columns'], array( 'ID', 'user_login', 'user_email', 'user_url', 'user_nicename', 'display_name' ) );
   522 				$search_columns = array_intersect( $qv['search_columns'], array( 'ID', 'user_login', 'user_email', 'user_url', 'user_nicename', 'display_name' ) );
   520 			}
   523 			}
   521 			if ( ! $search_columns ) {
   524 			if ( ! $search_columns ) {
   522 				if ( false !== strpos( $search, '@') )
   525 				if ( false !== strpos( $search, '@' ) ) {
   523 					$search_columns = array('user_email');
   526 					$search_columns = array( 'user_email' );
   524 				elseif ( is_numeric($search) )
   527 				} elseif ( is_numeric( $search ) ) {
   525 					$search_columns = array('user_login', 'ID');
   528 					$search_columns = array( 'user_login', 'ID' );
   526 				elseif ( preg_match('|^https?://|', $search) && ! ( is_multisite() && wp_is_large_network( 'users' ) ) )
   529 				} elseif ( preg_match( '|^https?://|', $search ) && ! ( is_multisite() && wp_is_large_network( 'users' ) ) ) {
   527 					$search_columns = array('user_url');
   530 					$search_columns = array( 'user_url' );
   528 				else
   531 				} else {
   529 					$search_columns = array('user_login', 'user_url', 'user_email', 'user_nicename', 'display_name');
   532 					$search_columns = array( 'user_login', 'user_url', 'user_email', 'user_nicename', 'display_name' );
       
   533 				}
   530 			}
   534 			}
   531 
   535 
   532 			/**
   536 			/**
   533 			 * Filters the columns to search in a WP_User_Query search.
   537 			 * Filters the columns to search in a WP_User_Query search.
   534 			 *
   538 			 *
   535 			 * The default columns depend on the search term, and include 'user_email',
   539 			 * The default columns depend on the search term, and include 'user_email',
   536 			 * 'user_login', 'ID', 'user_url', 'display_name', and 'user_nicename'.
   540 			 * 'user_login', 'ID', 'user_url', 'display_name', and 'user_nicename'.
   537 			 *
   541 			 *
   538 			 * @since 3.6.0
   542 			 * @since 3.6.0
   539 			 *
   543 			 *
   540 			 * @param array         $search_columns Array of column names to be searched.
   544 			 * @param string[]      $search_columns Array of column names to be searched.
   541 			 * @param string        $search         Text being searched.
   545 			 * @param string        $search         Text being searched.
   542 			 * @param WP_User_Query $this           The current WP_User_Query instance.
   546 			 * @param WP_User_Query $this           The current WP_User_Query instance.
   543 			 */
   547 			 */
   544 			$search_columns = apply_filters( 'user_search_columns', $search_columns, $search, $this );
   548 			$search_columns = apply_filters( 'user_search_columns', $search_columns, $search, $this );
   545 
   549 
   546 			$this->query_where .= $this->get_search_sql( $search, $search_columns, $wild );
   550 			$this->query_where .= $this->get_search_sql( $search, $search_columns, $wild );
   547 		}
   551 		}
   548 
   552 
   549 		if ( ! empty( $include ) ) {
   553 		if ( ! empty( $include ) ) {
   550 			// Sanitized earlier.
   554 			// Sanitized earlier.
   551 			$ids = implode( ',', $include );
   555 			$ids                = implode( ',', $include );
   552 			$this->query_where .= " AND $wpdb->users.ID IN ($ids)";
   556 			$this->query_where .= " AND $wpdb->users.ID IN ($ids)";
   553 		} elseif ( ! empty( $qv['exclude'] ) ) {
   557 		} elseif ( ! empty( $qv['exclude'] ) ) {
   554 			$ids = implode( ',', wp_parse_id_list( $qv['exclude'] ) );
   558 			$ids                = implode( ',', wp_parse_id_list( $qv['exclude'] ) );
   555 			$this->query_where .= " AND $wpdb->users.ID NOT IN ($ids)";
   559 			$this->query_where .= " AND $wpdb->users.ID NOT IN ($ids)";
   556 		}
   560 		}
   557 
   561 
   558 		// Date queries are allowed for the user_registered field.
   562 		// Date queries are allowed for the user_registered field.
   559 		if ( ! empty( $qv['date_query'] ) && is_array( $qv['date_query'] ) ) {
   563 		if ( ! empty( $qv['date_query'] ) && is_array( $qv['date_query'] ) ) {
   560 			$date_query = new WP_Date_Query( $qv['date_query'], 'user_registered' );
   564 			$date_query         = new WP_Date_Query( $qv['date_query'], 'user_registered' );
   561 			$this->query_where .= $date_query->get_sql();
   565 			$this->query_where .= $date_query->get_sql();
   562 		}
   566 		}
   563 
   567 
   564 		/**
   568 		/**
   565 		 * Fires after the WP_User_Query has been parsed, and before
   569 		 * Fires after the WP_User_Query has been parsed, and before
   586 	public function query() {
   590 	public function query() {
   587 		global $wpdb;
   591 		global $wpdb;
   588 
   592 
   589 		$qv =& $this->query_vars;
   593 		$qv =& $this->query_vars;
   590 
   594 
   591 		$this->request = "SELECT $this->query_fields $this->query_from $this->query_where $this->query_orderby $this->query_limit";
       
   592 
       
   593 		if ( is_array( $qv['fields'] ) || 'all' == $qv['fields'] ) {
       
   594 			$this->results = $wpdb->get_results( $this->request );
       
   595 		} else {
       
   596 			$this->results = $wpdb->get_col( $this->request );
       
   597 		}
       
   598 
       
   599 		/**
   595 		/**
   600 		 * Filters SELECT FOUND_ROWS() query for the current WP_User_Query instance.
   596 		 * Filters the users array before the query takes place.
   601 		 *
   597 		 *
   602 		 * @since 3.2.0
   598 		 * Return a non-null value to bypass WordPress's default user queries.
       
   599 		 * Filtering functions that require pagination information are encouraged to set
       
   600 		 * the `total_users` property of the WP_User_Query object, passed to the filter
       
   601 		 * by reference. If WP_User_Query does not perform a database query, it will not
       
   602 		 * have enough information to generate these values itself.
   603 		 *
   603 		 *
   604 		 * @global wpdb $wpdb WordPress database abstraction object.
   604 		 * @since 5.1.0
   605 		 *
   605 		 *
   606 		 * @param string $sql The SELECT FOUND_ROWS() query for the current WP_User_Query.
   606 		 * @param array|null $results Return an array of user data to short-circuit WP's user query
       
   607 		 *                            or null to allow WP to run its normal queries.
       
   608 		 * @param WP_User_Query $this The WP_User_Query instance (passed by reference).
   607 		 */
   609 		 */
   608 		if ( isset( $qv['count_total'] ) && $qv['count_total'] )
   610 		$this->results = apply_filters_ref_array( 'users_pre_query', array( null, &$this ) );
   609 			$this->total_users = (int) $wpdb->get_var( apply_filters( 'found_users_query', 'SELECT FOUND_ROWS()' ) );
   611 
   610 
   612 		if ( null === $this->results ) {
   611 		if ( !$this->results )
   613 			$this->request = "SELECT $this->query_fields $this->query_from $this->query_where $this->query_orderby $this->query_limit";
       
   614 
       
   615 			if ( is_array( $qv['fields'] ) || 'all' == $qv['fields'] ) {
       
   616 				$this->results = $wpdb->get_results( $this->request );
       
   617 			} else {
       
   618 				$this->results = $wpdb->get_col( $this->request );
       
   619 			}
       
   620 
       
   621 			if ( isset( $qv['count_total'] ) && $qv['count_total'] ) {
       
   622 				/**
       
   623 				 * Filters SELECT FOUND_ROWS() query for the current WP_User_Query instance.
       
   624 				 *
       
   625 				 * @since 3.2.0
       
   626 				 * @since 5.1.0 Added the `$this` parameter.
       
   627 				 *
       
   628 				 * @global wpdb $wpdb WordPress database abstraction object.
       
   629 				 *
       
   630 				 * @param string $sql         The SELECT FOUND_ROWS() query for the current WP_User_Query.
       
   631 				 * @param WP_User_Query $this The current WP_User_Query instance.
       
   632 				 */
       
   633 				$found_users_query = apply_filters( 'found_users_query', 'SELECT FOUND_ROWS()', $this );
       
   634 
       
   635 				$this->total_users = (int) $wpdb->get_var( $found_users_query );
       
   636 			}
       
   637 		}
       
   638 
       
   639 		if ( ! $this->results ) {
   612 			return;
   640 			return;
       
   641 		}
   613 
   642 
   614 		if ( 'all_with_meta' == $qv['fields'] ) {
   643 		if ( 'all_with_meta' == $qv['fields'] ) {
   615 			cache_users( $this->results );
   644 			cache_users( $this->results );
   616 
   645 
   617 			$r = array();
   646 			$r = array();
   618 			foreach ( $this->results as $userid )
   647 			foreach ( $this->results as $userid ) {
   619 				$r[ $userid ] = new WP_User( $userid, '', $qv['blog_id'] );
   648 				$r[ $userid ] = new WP_User( $userid, '', $qv['blog_id'] );
       
   649 			}
   620 
   650 
   621 			$this->results = $r;
   651 			$this->results = $r;
   622 		} elseif ( 'all' == $qv['fields'] ) {
   652 		} elseif ( 'all' == $qv['fields'] ) {
   623 			foreach ( $this->results as $key => $user ) {
   653 			foreach ( $this->results as $key => $user ) {
   624 				$this->results[ $key ] = new WP_User( $user, '', $qv['blog_id'] );
   654 				$this->results[ $key ] = new WP_User( $user, '', $qv['blog_id'] );
   633 	 *
   663 	 *
   634 	 * @param string $query_var Query variable key.
   664 	 * @param string $query_var Query variable key.
   635 	 * @return mixed
   665 	 * @return mixed
   636 	 */
   666 	 */
   637 	public function get( $query_var ) {
   667 	public function get( $query_var ) {
   638 		if ( isset( $this->query_vars[$query_var] ) )
   668 		if ( isset( $this->query_vars[ $query_var ] ) ) {
   639 			return $this->query_vars[$query_var];
   669 			return $this->query_vars[ $query_var ];
       
   670 		}
   640 
   671 
   641 		return null;
   672 		return null;
   642 	}
   673 	}
   643 
   674 
   644 	/**
   675 	/**
   648 	 *
   679 	 *
   649 	 * @param string $query_var Query variable key.
   680 	 * @param string $query_var Query variable key.
   650 	 * @param mixed $value Query variable value.
   681 	 * @param mixed $value Query variable value.
   651 	 */
   682 	 */
   652 	public function set( $query_var, $value ) {
   683 	public function set( $query_var, $value ) {
   653 		$this->query_vars[$query_var] = $value;
   684 		$this->query_vars[ $query_var ] = $value;
   654 	}
   685 	}
   655 
   686 
   656 	/**
   687 	/**
   657 	 * Used internally to generate an SQL string for searching across multiple columns
   688 	 * Used internally to generate an SQL string for searching across multiple columns
   658 	 *
   689 	 *
   667 	 * @return string
   698 	 * @return string
   668 	 */
   699 	 */
   669 	protected function get_search_sql( $string, $cols, $wild = false ) {
   700 	protected function get_search_sql( $string, $cols, $wild = false ) {
   670 		global $wpdb;
   701 		global $wpdb;
   671 
   702 
   672 		$searches = array();
   703 		$searches      = array();
   673 		$leading_wild = ( 'leading' == $wild || 'both' == $wild ) ? '%' : '';
   704 		$leading_wild  = ( 'leading' == $wild || 'both' == $wild ) ? '%' : '';
   674 		$trailing_wild = ( 'trailing' == $wild || 'both' == $wild ) ? '%' : '';
   705 		$trailing_wild = ( 'trailing' == $wild || 'both' == $wild ) ? '%' : '';
   675 		$like = $leading_wild . $wpdb->esc_like( $string ) . $trailing_wild;
   706 		$like          = $leading_wild . $wpdb->esc_like( $string ) . $trailing_wild;
   676 
   707 
   677 		foreach ( $cols as $col ) {
   708 		foreach ( $cols as $col ) {
   678 			if ( 'ID' == $col ) {
   709 			if ( 'ID' == $col ) {
   679 				$searches[] = $wpdb->prepare( "$col = %s", $string );
   710 				$searches[] = $wpdb->prepare( "$col = %s", $string );
   680 			} else {
   711 			} else {
   681 				$searches[] = $wpdb->prepare( "$col LIKE %s", $like );
   712 				$searches[] = $wpdb->prepare( "$col LIKE %s", $like );
   682 			}
   713 			}
   683 		}
   714 		}
   684 
   715 
   685 		return ' AND (' . implode(' OR ', $searches) . ')';
   716 		return ' AND (' . implode( ' OR ', $searches ) . ')';
   686 	}
   717 	}
   687 
   718 
   688 	/**
   719 	/**
   689 	 * Return the list of users.
   720 	 * Return the list of users.
   690 	 *
   721 	 *
   729 			$_orderby = $orderby;
   760 			$_orderby = $orderby;
   730 		} elseif ( 'name' == $orderby || 'display_name' == $orderby ) {
   761 		} elseif ( 'name' == $orderby || 'display_name' == $orderby ) {
   731 			$_orderby = 'display_name';
   762 			$_orderby = 'display_name';
   732 		} elseif ( 'post_count' == $orderby ) {
   763 		} elseif ( 'post_count' == $orderby ) {
   733 			// todo: avoid the JOIN
   764 			// todo: avoid the JOIN
   734 			$where = get_posts_by_author_sql( 'post' );
   765 			$where             = get_posts_by_author_sql( 'post' );
   735 			$this->query_from .= " LEFT OUTER JOIN (
   766 			$this->query_from .= " LEFT OUTER JOIN (
   736 				SELECT post_author, COUNT(*) as post_count
   767 				SELECT post_author, COUNT(*) as post_count
   737 				FROM $wpdb->posts
   768 				FROM $wpdb->posts
   738 				$where
   769 				$where
   739 				GROUP BY post_author
   770 				GROUP BY post_author
   740 			) p ON ({$wpdb->users}.ID = p.post_author)
   771 			) p ON ({$wpdb->users}.ID = p.post_author)
   741 			";
   772 			";
   742 			$_orderby = 'post_count';
   773 			$_orderby          = 'post_count';
   743 		} elseif ( 'ID' == $orderby || 'id' == $orderby ) {
   774 		} elseif ( 'ID' == $orderby || 'id' == $orderby ) {
   744 			$_orderby = 'ID';
   775 			$_orderby = 'ID';
   745 		} elseif ( 'meta_value' == $orderby || $this->get( 'meta_key' ) == $orderby ) {
   776 		} elseif ( 'meta_value' == $orderby || $this->get( 'meta_key' ) == $orderby ) {
   746 			$_orderby = "$wpdb->usermeta.meta_value";
   777 			$_orderby = "$wpdb->usermeta.meta_value";
   747 		} elseif ( 'meta_value_num' == $orderby ) {
   778 		} elseif ( 'meta_value_num' == $orderby ) {
   748 			$_orderby = "$wpdb->usermeta.meta_value+0";
   779 			$_orderby = "$wpdb->usermeta.meta_value+0";
   749 		} elseif ( 'include' === $orderby && ! empty( $this->query_vars['include'] ) ) {
   780 		} elseif ( 'include' === $orderby && ! empty( $this->query_vars['include'] ) ) {
   750 			$include = wp_parse_id_list( $this->query_vars['include'] );
   781 			$include     = wp_parse_id_list( $this->query_vars['include'] );
   751 			$include_sql = implode( ',', $include );
   782 			$include_sql = implode( ',', $include );
   752 			$_orderby = "FIELD( $wpdb->users.ID, $include_sql )";
   783 			$_orderby    = "FIELD( $wpdb->users.ID, $include_sql )";
   753 		} elseif ( 'nicename__in' === $orderby ) {
   784 		} elseif ( 'nicename__in' === $orderby ) {
   754 			$sanitized_nicename__in = array_map( 'esc_sql', $this->query_vars['nicename__in'] );
   785 			$sanitized_nicename__in = array_map( 'esc_sql', $this->query_vars['nicename__in'] );
   755 			$nicename__in = implode( "','", $sanitized_nicename__in );
   786 			$nicename__in           = implode( "','", $sanitized_nicename__in );
   756 			$_orderby = "FIELD( user_nicename, '$nicename__in' )";
   787 			$_orderby               = "FIELD( user_nicename, '$nicename__in' )";
   757 		} elseif ( 'login__in' === $orderby ) {
   788 		} elseif ( 'login__in' === $orderby ) {
   758 			$sanitized_login__in = array_map( 'esc_sql', $this->query_vars['login__in'] );
   789 			$sanitized_login__in = array_map( 'esc_sql', $this->query_vars['login__in'] );
   759 			$login__in = implode( "','", $sanitized_login__in );
   790 			$login__in           = implode( "','", $sanitized_login__in );
   760 			$_orderby = "FIELD( user_login, '$login__in' )";
   791 			$_orderby            = "FIELD( user_login, '$login__in' )";
   761 		} elseif ( isset( $meta_query_clauses[ $orderby ] ) ) {
   792 		} elseif ( isset( $meta_query_clauses[ $orderby ] ) ) {
   762 			$meta_clause = $meta_query_clauses[ $orderby ];
   793 			$meta_clause = $meta_query_clauses[ $orderby ];
   763 			$_orderby = sprintf( "CAST(%s.meta_value AS %s)", esc_sql( $meta_clause['alias'] ), esc_sql( $meta_clause['cast'] ) );
   794 			$_orderby    = sprintf( 'CAST(%s.meta_value AS %s)', esc_sql( $meta_clause['alias'] ), esc_sql( $meta_clause['cast'] ) );
   764 		}
   795 		}
   765 
   796 
   766 		return $_orderby;
   797 		return $_orderby;
   767 	}
   798 	}
   768 
   799 
   845 	/**
   876 	/**
   846 	 * Make private/protected methods readable for backward compatibility.
   877 	 * Make private/protected methods readable for backward compatibility.
   847 	 *
   878 	 *
   848 	 * @since 4.0.0
   879 	 * @since 4.0.0
   849 	 *
   880 	 *
   850 	 * @param callable $name      Method to call.
   881 	 * @param string   $name      Method to call.
   851 	 * @param array    $arguments Arguments to pass when calling.
   882 	 * @param array    $arguments Arguments to pass when calling.
   852 	 * @return mixed Return value of the callback, false otherwise.
   883 	 * @return mixed Return value of the callback, false otherwise.
   853 	 */
   884 	 */
   854 	public function __call( $name, $arguments ) {
   885 	public function __call( $name, $arguments ) {
   855 		if ( 'get_search_sql' === $name ) {
   886 		if ( 'get_search_sql' === $name ) {