<?php /** * User API: WP_User_Query class * * @package WordPress * @subpackage Users * @since 4.4.0 */
/** * Core class used for querying users. * * @since 3.1.0 * * @see WP_User_Query::prepare_query() for information on accepted arguments. */ #[AllowDynamicProperties] class WP_User_Query {
/** * Query vars, after parsing * * @since 3.5.0 * @var array */ public $query_vars = array();
/** * List of found user IDs. * * @since 3.1.0 * @var array */ private $results;
/** * Total number of found users for the current query * * @since 3.1.0 * @var int */ private $total_users = 0;
/** * Prepares the query variables. * * @since 3.1.0 * @since 4.1.0 Added the ability to order by the `include` value. * @since 4.2.0 Added 'meta_value_num' support for `$orderby` parameter. Added multi-dimensional array syntax * for `$orderby` parameter. * @since 4.3.0 Added 'has_published_posts' parameter. * @since 4.4.0 Added 'paged', 'role__in', and 'role__not_in' parameters. The 'role' parameter was updated to * permit an array or comma-separated list of values. The 'number' parameter was updated to support * querying for all users with using -1. * @since 4.7.0 Added 'nicename', 'nicename__in', 'nicename__not_in', 'login', 'login__in', * and 'login__not_in' parameters. * @since 5.1.0 Introduced the 'meta_compare_key' parameter. * @since 5.3.0 Introduced the 'meta_type_key' parameter. * @since 5.9.0 Added 'capability', 'capability__in', and 'capability__not_in' parameters. * Deprecated the 'who' parameter. * @since 6.3.0 Added 'cache_results' parameter. * * @global wpdb $wpdb WordPress database abstraction object. * @global WP_Roles $wp_roles WordPress role management object. * * @param string|array $query { * Optional. Array or string of query parameters. * * @type int $blog_id The site ID. Default is the current site. * @type string|string[] $role An array or a comma-separated list of role names that users * must match to be included in results. Note that this is * an inclusive list: users must match *each* role. Default empty. * @type string[] $role__in An array of role names. Matched users must have at least one * of these roles. Default empty array. * @type string[] $role__not_in An array of role names to exclude. Users matching one or more * of these roles will not be included in results. Default empty array. * @type string|string[] $meta_key Meta key or keys to filter by. * @type string|string[] $meta_value Meta value or values to filter by. * @type string $meta_compare MySQL operator used for comparing the meta value. * See WP_Meta_Query::__construct() for accepted values and default value. * @type string $meta_compare_key MySQL operator used for comparing the meta key. * See WP_Meta_Query::__construct() for accepted values and default value. * @type string $meta_type MySQL data type that the meta_value column will be CAST to for comparisons. * See WP_Meta_Query::__construct() for accepted values and default value. * @type string $meta_type_key MySQL data type that the meta_key column will be CAST to for comparisons. * See WP_Meta_Query::__construct() for accepted values and default value. * @type array $meta_query An associative array of WP_Meta_Query arguments. * See WP_Meta_Query::__construct() for accepted values. * @type string|string[] $capability An array or a comma-separated list of capability names that users * must match to be included in results. Note that this is * an inclusive list: users must match *each* capability. * Does NOT work for capabilities not in the database or filtered * via {@see 'map_meta_cap'}. Default empty. * @type string[] $capability__in An array of capability names. Matched users must have at least one * of these capabilities. * Does NOT work for capabilities not in the database or filtered * via {@see 'map_meta_cap'}. Default empty array. * @type string[] $capability__not_in An array of capability names to exclude. Users matching one or more * of these capabilities will not be included in results. * Does NOT work for capabilities not in the database or filtered * via {@see 'map_meta_cap'}. Default empty array. * @type int[] $include An array of user IDs to include. Default empty array. * @type int[] $exclude An array of user IDs to exclude. Default empty array. * @type string $search Search keyword. Searches for possible string matches on columns. * When `$search_columns` is left empty, it tries to determine which * column to search in based on search string. Default empty. * @type string[] $search_columns Array of column names to be searched. Accepts 'ID', 'user_login', * 'user_email', 'user_url', 'user_nicename', 'display_name'. * Default empty array. * @type string|array $orderby Field(s) to sort the retrieved users by. May be a single value, * an array of values, or a multi-dimensional array with fields as * keys and orders ('ASC' or 'DESC') as values. Accepted values are: * - 'ID' * - 'display_name' (or 'name') * - 'include' * - 'user_login' (or 'login') * - 'login__in' * - 'user_nicename' (or 'nicename') * - 'nicename__in' * - 'user_email (or 'email') * - 'user_url' (or 'url') * - 'user_registered' (or 'registered') * - 'post_count' * - 'meta_value' * - 'meta_value_num' * - The value of `$meta_key` * - An array key of `$meta_query` * To use 'meta_value' or 'meta_value_num', `$meta_key` * must be also be defined. Default 'user_login'. * @type string $order Designates ascending or descending order of users. Order values * passed as part of an `$orderby` array take precedence over this * parameter. Accepts 'ASC', 'DESC'. Default 'ASC'. * @type int $offset Number of users to offset in retrieved results. Can be used in * conjunction with pagination. Default 0. * @type int $number Number of users to limit the query for. Can be used in * conjunction with pagination. Value -1 (all) is supported, but * should be used with caution on larger sites. * Default -1 (all users). * @type int $paged When used with number, defines the page of results to return. * Default 1. * @type bool $count_total Whether to count the total number of users found. If pagination * is not needed, setting this to false can improve performance. * Default true. * @type string|string[] $fields Which fields to return. Single or all fields (string), or array * of fields. Accepts: * - 'ID' * - 'display_name' * - 'user_login' * - 'user_nicename' * - 'user_email' * - 'user_url' * - 'user_registered' * - 'user_pass' * - 'user_activation_key' * - 'user_status' * - 'spam' (only available on multisite installs) * - 'deleted' (only available on multisite installs) * - 'all' for all fields and loads user meta. * - 'all_with_meta' Deprecated. Use 'all'. * Default 'all'. * @type string $who Deprecated, use `$capability` instead. * Type of users to query. Accepts 'authors'. * Default empty (all users). * @type bool|string[] $has_published_posts Pass an array of post types to filter results to users who have * published posts in those post types. `true` is an alias for all * public post types. * @type string $nicename The user nicename. Default empty. * @type string[] $nicename__in An array of nicenames to include. Users matching one of these * nicenames will be included in results. Default empty array. * @type string[] $nicename__not_in An array of nicenames to exclude. Users matching one of these * nicenames will not be included in results. Default empty array. * @type string $login The user login. Default empty. * @type string[] $login__in An array of logins to include. Users matching one of these * logins will be included in results. Default empty array. * @type string[] $login__not_in An array of logins to exclude. Users matching one of these * logins will not be included in results. Default empty array. * @type bool $cache_results Whether to cache user information. Default true. * } */ public function prepare_query( $query = array() ) { global $wpdb, $wp_roles;
/** * Fires before the WP_User_Query has been parsed. * * The passed WP_User_Query object contains the query variables, * not yet passed into SQL. * * @since 4.0.0 * * @param WP_User_Query $query Current instance of WP_User_Query (passed by reference). */ do_action_ref_array( 'pre_get_users', array( &$this ) );
// Ensure that query vars are filled after 'pre_get_users'. $qv =& $this->query_vars; $qv = $this->fill_query_vars( $qv );
// Parse and sanitize 'include', for use by 'orderby' as well as 'include' below. if ( ! empty( $qv['include'] ) ) { $include = wp_parse_id_list( $qv['include'] ); } else { $include = false; }
// Prevent extra meta query. $qv['blog_id'] = 0; $blog_id = 0;
if ( empty( $this->meta_query->queries ) ) { $this->meta_query->queries = array( $who_query ); } else { // Append the cap query to the original queries and reparse the query. $this->meta_query->queries = array( 'relation' => 'AND', array( $this->meta_query->queries, $who_query ), ); }
if ( empty( $this->meta_query->queries ) ) { $this->meta_query->queries[] = $capabilities_clauses; } else { // Append the cap query to the original queries and reparse the query. $this->meta_query->queries = array( 'relation' => 'AND', array( $this->meta_query->queries, array( $capabilities_clauses ) ), ); }
// If there are no specific roles named, make sure the user is a member of the site. if ( empty( $role_queries ) ) { $role_queries[] = array( 'key' => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities', 'compare' => 'EXISTS', ); }
// Specify that role queries should be joined with AND. $role_queries['relation'] = 'AND';
if ( empty( $this->meta_query->queries ) ) { $this->meta_query->queries = $role_queries; } else { // Append the cap query to the original queries and reparse the query. $this->meta_query->queries = array( 'relation' => 'AND', array( $this->meta_query->queries, $role_queries ), ); }
if ( empty( $qv['orderby'] ) ) { // Default order is by 'user_login'. $ordersby = array( 'user_login' => $order ); } elseif ( is_array( $qv['orderby'] ) ) { $ordersby = $qv['orderby']; } else { // 'orderby' values may be a comma- or space-separated list. $ordersby = preg_split( '/[,\s]+/', $qv['orderby'] ); }
$orderby_array = array(); foreach ( $ordersby as $_key => $_value ) { if ( ! $_value ) { continue; }
if ( is_int( $_key ) ) { // Integer key means this is a flat array of 'orderby' fields. $_orderby = $_value; $_order = $order; } else { // Non-integer key means this the key is the field and the value is ASC/DESC. $_orderby = $_key; $_order = $_value; }
/** * Filters the columns to search in a WP_User_Query search. * * The default columns depend on the search term, and include 'ID', 'user_login', * 'user_email', 'user_url', 'user_nicename', and 'display_name'. * * @since 3.6.0 * * @param string[] $search_columns Array of column names to be searched. * @param string $search Text being searched. * @param WP_User_Query $query The current WP_User_Query instance. */ $search_columns = apply_filters( 'user_search_columns', $search_columns, $search, $this );
if ( ! empty( $include ) ) { // Sanitized earlier. $ids = implode( ',', $include ); $this->query_where .= " AND $wpdb->users.ID IN ($ids)"; } elseif ( ! empty( $qv['exclude'] ) ) { $ids = implode( ',', wp_parse_id_list( $qv['exclude'] ) ); $this->query_where .= " AND $wpdb->users.ID NOT IN ($ids)"; }
// Date queries are allowed for the user_registered field. if ( ! empty( $qv['date_query'] ) && is_array( $qv['date_query'] ) ) { $date_query = new WP_Date_Query( $qv['date_query'], 'user_registered' ); $this->query_where .= $date_query->get_sql(); }
/** * Fires after the WP_User_Query has been parsed, and before * the query is executed. * * The passed WP_User_Query object contains SQL parts formed * from parsing the given query. * * @since 3.1.0 * * @param WP_User_Query $query Current instance of WP_User_Query (passed by reference). */ do_action_ref_array( 'pre_user_query', array( &$this ) ); }
/** * Executes the query, with the current variables. * * @since 3.1.0 * * @global wpdb $wpdb WordPress database abstraction object. */ public function query() { global $wpdb;
if ( ! did_action( 'plugins_loaded' ) ) { _doing_it_wrong( 'WP_User_Query::query', sprintf( /* translators: %s: plugins_loaded */ __( 'User queries should not be run before the %s hook.' ), '<code>plugins_loaded</code>' ), '6.1.1' ); }
$qv =& $this->query_vars;
// Do not cache results if more than 3 fields are requested. if ( is_array( $qv['fields'] ) && count( $qv['fields'] ) > 3 ) { $qv['cache_results'] = false; }
/** * Filters the users array before the query takes place. * * Return a non-null value to bypass WordPress' 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.1.0 * * @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 $query The WP_User_Query instance (passed by reference). */ $this->results = apply_filters_ref_array( 'users_pre_query', array( null, &$this ) );
if ( null === $this->results ) { // Beginning of the string is on a new line to prevent leading whitespace. See https://core.trac.wordpress.org/ticket/56841. $this->request = "SELECT {$this->query_fields} {$this->query_from} {$this->query_where} {$this->query_orderby} {$this->query_limit}"; $cache_value = false; $cache_key = $this->generate_cache_key( $qv, $this->request ); $cache_group = 'user-queries'; if ( $qv['cache_results'] ) { $cache_value = wp_cache_get( $cache_key, $cache_group ); } if ( false !== $cache_value ) { $this->results = $cache_value['user_data']; $this->total_users = $cache_value['total_users']; } else {
/** * Used internally to generate an SQL string for searching across multiple columns. * * @since 3.1.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $search Search string. * @param string[] $columns Array of columns to search. * @param bool $wild Whether to allow wildcard searches. Default is false for Network Admin, true for single site. * Single site allows leading and trailing wildcards, Network Admin only trailing. * @return string */ protected function get_search_sql( $search, $columns, $wild = false ) { global $wpdb;
return ' AND (' . implode( ' OR ', $searches ) . ')'; }
/** * Returns the list of users. * * @since 3.1.0 * * @return array Array of results. */ public function get_results() { return $this->results; }
/** * Returns the total number of users for the current query. * * @since 3.1.0 * * @return int Number of total users. */ public function get_total() { return $this->total_users; }
/** * Parses and sanitizes 'orderby' keys passed to the user query. * * @since 4.2.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $orderby Alias for the field to order by. * @return string Value to used in the ORDER clause, if `$orderby` is valid. */ protected function parse_orderby( $orderby ) { global $wpdb;
/** * Makes private properties readable for backward compatibility. * * @since 4.0.0 * @since 6.4.0 Getting a dynamic property is deprecated. * * @param string $name Property to get. * @return mixed Property. */ public function __get( $name ) { if ( in_array( $name, $this->compat_fields, true ) ) { return $this->$name; }
wp_trigger_error( __METHOD__, "The property `{$name}` is not declared. Getting a dynamic property is " . 'deprecated since version 6.4.0! Instead, declare the property on the class.', E_USER_DEPRECATED ); return null; }
/** * Makes private properties settable for backward compatibility. * * @since 4.0.0 * @since 6.4.0 Setting a dynamic property is deprecated. * * @param string $name Property to check if set. * @param mixed $value Property value. */ public function __set( $name, $value ) { if ( in_array( $name, $this->compat_fields, true ) ) { $this->$name = $value; return; }
wp_trigger_error( __METHOD__, "The property `{$name}` is not declared. Setting a dynamic property is " . 'deprecated since version 6.4.0! Instead, declare the property on the class.', E_USER_DEPRECATED ); }
/** * Makes private properties checkable for backward compatibility. * * @since 4.0.0 * @since 6.4.0 Checking a dynamic property is deprecated. * * @param string $name Property to check if set. * @return bool Whether the property is set. */ public function __isset( $name ) { if ( in_array( $name, $this->compat_fields, true ) ) { return isset( $this->$name ); }
wp_trigger_error( __METHOD__, "The property `{$name}` is not declared. Checking `isset()` on a dynamic property " . 'is deprecated since version 6.4.0! Instead, declare the property on the class.', E_USER_DEPRECATED ); return false; }
/** * Makes private properties un-settable for backward compatibility. * * @since 4.0.0 * @since 6.4.0 Unsetting a dynamic property is deprecated. * * @param string $name Property to unset. */ public function __unset( $name ) { if ( in_array( $name, $this->compat_fields, true ) ) { unset( $this->$name ); return; }
wp_trigger_error( __METHOD__, "A property `{$name}` is not declared. Unsetting a dynamic property is " . 'deprecated since version 6.4.0! Instead, declare the property on the class.', E_USER_DEPRECATED ); }
/** * Makes private/protected methods readable for backward compatibility. * * @since 4.0.0 * * @param string $name Method to call. * @param array $arguments Arguments to pass when calling. * @return mixed Return value of the callback, false otherwise. */ public function __call( $name, $arguments ) { if ( 'get_search_sql' === $name ) { return $this->get_search_sql( ...$arguments ); } return false; } }