137 * @since 4.7.0 Added 'nicename', 'nicename__in', 'nicename__not_in', 'login', 'login__in', |
140 * @since 4.7.0 Added 'nicename', 'nicename__in', 'nicename__not_in', 'login', 'login__in', |
138 * and 'login__not_in' parameters. |
141 * and 'login__not_in' parameters. |
139 * @since 5.1.0 Introduced the 'meta_compare_key' parameter. |
142 * @since 5.1.0 Introduced the 'meta_compare_key' parameter. |
140 * @since 5.3.0 Introduced the 'meta_type_key' parameter. |
143 * @since 5.3.0 Introduced the 'meta_type_key' parameter. |
141 * @since 5.9.0 Added 'capability', 'capability__in', and 'capability__not_in' parameters. |
144 * @since 5.9.0 Added 'capability', 'capability__in', and 'capability__not_in' parameters. |
142 * |
145 * Deprecated the 'who' parameter. |
143 * @global wpdb $wpdb WordPress database abstraction object. |
146 * @since 6.3.0 Added 'cache_results' parameter. |
144 * @global int $blog_id |
147 * |
|
148 * @global wpdb $wpdb WordPress database abstraction object. |
|
149 * @global WP_Roles $wp_roles WordPress role management object. |
145 * |
150 * |
146 * @param string|array $query { |
151 * @param string|array $query { |
147 * Optional. Array or string of Query parameters. |
152 * Optional. Array or string of query parameters. |
148 * |
153 * |
149 * @type int $blog_id The site ID. Default is the current site. |
154 * @type int $blog_id The site ID. Default is the current site. |
150 * @type string|string[] $role An array or a comma-separated list of role names that users must match |
155 * @type string|string[] $role An array or a comma-separated list of role names that users |
151 * to be included in results. Note that this is an inclusive list: users |
156 * must match to be included in results. Note that this is |
152 * must match *each* role. Default empty. |
157 * an inclusive list: users must match *each* role. Default empty. |
153 * @type string[] $role__in An array of role names. Matched users must have at least one of these |
158 * @type string[] $role__in An array of role names. Matched users must have at least one |
154 * roles. Default empty array. |
159 * of these roles. Default empty array. |
155 * @type string[] $role__not_in An array of role names to exclude. Users matching one or more of these |
160 * @type string[] $role__not_in An array of role names to exclude. Users matching one or more |
156 * roles will not be included in results. Default empty array. |
161 * of these roles will not be included in results. Default empty array. |
157 * @type string|string[] $meta_key Meta key or keys to filter by. |
162 * @type string|string[] $meta_key Meta key or keys to filter by. |
158 * @type string|string[] $meta_value Meta value or values to filter by. |
163 * @type string|string[] $meta_value Meta value or values to filter by. |
159 * @type string $meta_compare MySQL operator used for comparing the meta value. |
164 * @type string $meta_compare MySQL operator used for comparing the meta value. |
160 * See WP_Meta_Query::__construct for accepted values and default value. |
165 * See WP_Meta_Query::__construct() for accepted values and default value. |
161 * @type string $meta_compare_key MySQL operator used for comparing the meta key. |
166 * @type string $meta_compare_key MySQL operator used for comparing the meta key. |
162 * See WP_Meta_Query::__construct for accepted values and default value. |
167 * See WP_Meta_Query::__construct() for accepted values and default value. |
163 * @type string $meta_type MySQL data type that the meta_value column will be CAST to for comparisons. |
168 * @type string $meta_type MySQL data type that the meta_value column will be CAST to for comparisons. |
164 * See WP_Meta_Query::__construct for accepted values and default value. |
169 * See WP_Meta_Query::__construct() for accepted values and default value. |
165 * @type string $meta_type_key MySQL data type that the meta_key column will be CAST to for comparisons. |
170 * @type string $meta_type_key MySQL data type that the meta_key column will be CAST to for comparisons. |
166 * See WP_Meta_Query::__construct for accepted values and default value. |
171 * See WP_Meta_Query::__construct() for accepted values and default value. |
167 * @type array $meta_query An associative array of WP_Meta_Query arguments. |
172 * @type array $meta_query An associative array of WP_Meta_Query arguments. |
168 * See WP_Meta_Query::__construct for accepted values. |
173 * See WP_Meta_Query::__construct() for accepted values. |
169 * @type string|string[] $capability An array or a comma-separated list of capability names that users must match |
174 * @type string|string[] $capability An array or a comma-separated list of capability names that users |
170 * to be included in results. Note that this is an inclusive list: users |
175 * must match to be included in results. Note that this is |
171 * must match *each* capability. |
176 * an inclusive list: users must match *each* capability. |
172 * Does NOT work for capabilities not in the database or filtered via {@see 'map_meta_cap'}. |
177 * Does NOT work for capabilities not in the database or filtered |
173 * Default empty. |
178 * via {@see 'map_meta_cap'}. Default empty. |
174 * @type string[] $capability__in An array of capability names. Matched users must have at least one of these |
179 * @type string[] $capability__in An array of capability names. Matched users must have at least one |
175 * capabilities. |
180 * of these capabilities. |
176 * Does NOT work for capabilities not in the database or filtered via {@see 'map_meta_cap'}. |
181 * Does NOT work for capabilities not in the database or filtered |
177 * Default empty array. |
182 * via {@see 'map_meta_cap'}. Default empty array. |
178 * @type string[] $capability__not_in An array of capability names to exclude. Users matching one or more of these |
183 * @type string[] $capability__not_in An array of capability names to exclude. Users matching one or more |
179 * capabilities will not be included in results. |
184 * of these capabilities will not be included in results. |
180 * Does NOT work for capabilities not in the database or filtered via {@see 'map_meta_cap'}. |
185 * Does NOT work for capabilities not in the database or filtered |
181 * Default empty array. |
186 * via {@see 'map_meta_cap'}. Default empty array. |
182 * @type int[] $include An array of user IDs to include. Default empty array. |
187 * @type int[] $include An array of user IDs to include. Default empty array. |
183 * @type int[] $exclude An array of user IDs to exclude. Default empty array. |
188 * @type int[] $exclude An array of user IDs to exclude. Default empty array. |
184 * @type string $search Search keyword. Searches for possible string matches on columns. |
189 * @type string $search Search keyword. Searches for possible string matches on columns. |
185 * When `$search_columns` is left empty, it tries to determine which |
190 * When `$search_columns` is left empty, it tries to determine which |
186 * column to search in based on search string. Default empty. |
191 * column to search in based on search string. Default empty. |
710 $search_columns = array(); |
713 $search_columns = array(); |
711 if ( $qv['search_columns'] ) { |
714 if ( $qv['search_columns'] ) { |
712 $search_columns = array_intersect( $qv['search_columns'], array( 'ID', 'user_login', 'user_email', 'user_url', 'user_nicename', 'display_name' ) ); |
715 $search_columns = array_intersect( $qv['search_columns'], array( 'ID', 'user_login', 'user_email', 'user_url', 'user_nicename', 'display_name' ) ); |
713 } |
716 } |
714 if ( ! $search_columns ) { |
717 if ( ! $search_columns ) { |
715 if ( false !== strpos( $search, '@' ) ) { |
718 if ( str_contains( $search, '@' ) ) { |
716 $search_columns = array( 'user_email' ); |
719 $search_columns = array( 'user_email' ); |
717 } elseif ( is_numeric( $search ) ) { |
720 } elseif ( is_numeric( $search ) ) { |
718 $search_columns = array( 'user_login', 'ID' ); |
721 $search_columns = array( 'user_login', 'ID' ); |
719 } elseif ( preg_match( '|^https?://|', $search ) && ! ( is_multisite() && wp_is_large_network( 'users' ) ) ) { |
722 } elseif ( preg_match( '|^https?://|', $search ) && ! ( is_multisite() && wp_is_large_network( 'users' ) ) ) { |
720 $search_columns = array( 'user_url' ); |
723 $search_columns = array( 'user_url' ); |
798 * @param WP_User_Query $query The WP_User_Query instance (passed by reference). |
818 * @param WP_User_Query $query The WP_User_Query instance (passed by reference). |
799 */ |
819 */ |
800 $this->results = apply_filters_ref_array( 'users_pre_query', array( null, &$this ) ); |
820 $this->results = apply_filters_ref_array( 'users_pre_query', array( null, &$this ) ); |
801 |
821 |
802 if ( null === $this->results ) { |
822 if ( null === $this->results ) { |
803 $this->request = " |
823 // Beginning of the string is on a new line to prevent leading whitespace. See https://core.trac.wordpress.org/ticket/56841. |
804 SELECT {$this->query_fields} |
824 $this->request = |
805 {$this->query_from} |
825 "SELECT {$this->query_fields} |
806 {$this->query_where} |
826 {$this->query_from} |
807 {$this->query_orderby} |
827 {$this->query_where} |
808 {$this->query_limit} |
828 {$this->query_orderby} |
809 "; |
829 {$this->query_limit}"; |
810 |
830 $cache_value = false; |
811 if ( is_array( $qv['fields'] ) || 'all' === $qv['fields'] ) { |
831 $cache_key = $this->generate_cache_key( $qv, $this->request ); |
812 $this->results = $wpdb->get_results( $this->request ); |
832 $cache_group = 'user-queries'; |
|
833 if ( $qv['cache_results'] ) { |
|
834 $cache_value = wp_cache_get( $cache_key, $cache_group ); |
|
835 } |
|
836 if ( false !== $cache_value ) { |
|
837 $this->results = $cache_value['user_data']; |
|
838 $this->total_users = $cache_value['total_users']; |
813 } else { |
839 } else { |
814 $this->results = $wpdb->get_col( $this->request ); |
840 |
815 } |
841 if ( is_array( $qv['fields'] ) ) { |
816 |
842 $this->results = $wpdb->get_results( $this->request ); |
817 if ( isset( $qv['count_total'] ) && $qv['count_total'] ) { |
843 } else { |
818 /** |
844 $this->results = $wpdb->get_col( $this->request ); |
819 * Filters SELECT FOUND_ROWS() query for the current WP_User_Query instance. |
845 } |
820 * |
846 |
821 * @since 3.2.0 |
847 if ( isset( $qv['count_total'] ) && $qv['count_total'] ) { |
822 * @since 5.1.0 Added the `$this` parameter. |
848 /** |
823 * |
849 * Filters SELECT FOUND_ROWS() query for the current WP_User_Query instance. |
824 * @global wpdb $wpdb WordPress database abstraction object. |
850 * |
825 * |
851 * @since 3.2.0 |
826 * @param string $sql The SELECT FOUND_ROWS() query for the current WP_User_Query. |
852 * @since 5.1.0 Added the `$this` parameter. |
827 * @param WP_User_Query $query The current WP_User_Query instance. |
853 * |
828 */ |
854 * @global wpdb $wpdb WordPress database abstraction object. |
829 $found_users_query = apply_filters( 'found_users_query', 'SELECT FOUND_ROWS()', $this ); |
855 * |
830 |
856 * @param string $sql The SELECT FOUND_ROWS() query for the current WP_User_Query. |
831 $this->total_users = (int) $wpdb->get_var( $found_users_query ); |
857 * @param WP_User_Query $query The current WP_User_Query instance. |
|
858 */ |
|
859 $found_users_query = apply_filters( 'found_users_query', 'SELECT FOUND_ROWS()', $this ); |
|
860 |
|
861 $this->total_users = (int) $wpdb->get_var( $found_users_query ); |
|
862 } |
|
863 |
|
864 if ( $qv['cache_results'] ) { |
|
865 $cache_value = array( |
|
866 'user_data' => $this->results, |
|
867 'total_users' => $this->total_users, |
|
868 ); |
|
869 wp_cache_add( $cache_key, $cache_value, $cache_group ); |
|
870 } |
832 } |
871 } |
833 } |
872 } |
834 |
873 |
835 if ( ! $this->results ) { |
874 if ( ! $this->results ) { |
836 return; |
875 return; |
840 isset( $this->results[0]->ID ) |
879 isset( $this->results[0]->ID ) |
841 ) { |
880 ) { |
842 foreach ( $this->results as $result ) { |
881 foreach ( $this->results as $result ) { |
843 $result->id = $result->ID; |
882 $result->id = $result->ID; |
844 } |
883 } |
845 } elseif ( 'all_with_meta' === $qv['fields'] ) { |
884 } elseif ( 'all_with_meta' === $qv['fields'] || 'all' === $qv['fields'] ) { |
846 cache_users( $this->results ); |
885 if ( function_exists( 'cache_users' ) ) { |
|
886 cache_users( $this->results ); |
|
887 } |
847 |
888 |
848 $r = array(); |
889 $r = array(); |
849 foreach ( $this->results as $userid ) { |
890 foreach ( $this->results as $userid ) { |
850 $r[ $userid ] = new WP_User( $userid, '', $qv['blog_id'] ); |
891 if ( 'all_with_meta' === $qv['fields'] ) { |
|
892 $r[ $userid ] = new WP_User( $userid, '', $qv['blog_id'] ); |
|
893 } else { |
|
894 $r[] = new WP_User( $userid, '', $qv['blog_id'] ); |
|
895 } |
851 } |
896 } |
852 |
897 |
853 $this->results = $r; |
898 $this->results = $r; |
854 } elseif ( 'all' === $qv['fields'] ) { |
|
855 foreach ( $this->results as $key => $user ) { |
|
856 $this->results[ $key ] = new WP_User( $user, '', $qv['blog_id'] ); |
|
857 } |
|
858 } |
899 } |
859 } |
900 } |
860 |
901 |
861 /** |
902 /** |
862 * Retrieves query variable. |
903 * Retrieves query variable. |
968 $this->query_from .= " LEFT OUTER JOIN ( |
1009 $this->query_from .= " LEFT OUTER JOIN ( |
969 SELECT post_author, COUNT(*) as post_count |
1010 SELECT post_author, COUNT(*) as post_count |
970 FROM $wpdb->posts |
1011 FROM $wpdb->posts |
971 $where |
1012 $where |
972 GROUP BY post_author |
1013 GROUP BY post_author |
973 ) p ON ({$wpdb->users}.ID = p.post_author) |
1014 ) p ON ({$wpdb->users}.ID = p.post_author)"; |
974 "; |
|
975 $_orderby = 'post_count'; |
1015 $_orderby = 'post_count'; |
976 } elseif ( 'ID' === $orderby || 'id' === $orderby ) { |
1016 } elseif ( 'ID' === $orderby || 'id' === $orderby ) { |
977 $_orderby = 'ID'; |
1017 $_orderby = 'ID'; |
978 } elseif ( 'meta_value' === $orderby || $this->get( 'meta_key' ) == $orderby ) { |
1018 } elseif ( 'meta_value' === $orderby || $this->get( 'meta_key' ) === $orderby ) { |
979 $_orderby = "$wpdb->usermeta.meta_value"; |
1019 $_orderby = "$wpdb->usermeta.meta_value"; |
980 } elseif ( 'meta_value_num' === $orderby ) { |
1020 } elseif ( 'meta_value_num' === $orderby ) { |
981 $_orderby = "$wpdb->usermeta.meta_value+0"; |
1021 $_orderby = "$wpdb->usermeta.meta_value+0"; |
982 } elseif ( 'include' === $orderby && ! empty( $this->query_vars['include'] ) ) { |
1022 } elseif ( 'include' === $orderby && ! empty( $this->query_vars['include'] ) ) { |
983 $include = wp_parse_id_list( $this->query_vars['include'] ); |
1023 $include = wp_parse_id_list( $this->query_vars['include'] ); |
998 |
1038 |
999 return $_orderby; |
1039 return $_orderby; |
1000 } |
1040 } |
1001 |
1041 |
1002 /** |
1042 /** |
|
1043 * Generate cache key. |
|
1044 * |
|
1045 * @since 6.3.0 |
|
1046 * |
|
1047 * @global wpdb $wpdb WordPress database abstraction object. |
|
1048 * |
|
1049 * @param array $args Query arguments. |
|
1050 * @param string $sql SQL statement. |
|
1051 * @return string Cache key. |
|
1052 */ |
|
1053 protected function generate_cache_key( array $args, $sql ) { |
|
1054 global $wpdb; |
|
1055 |
|
1056 // Replace wpdb placeholder in the SQL statement used by the cache key. |
|
1057 $sql = $wpdb->remove_placeholder_escape( $sql ); |
|
1058 |
|
1059 $key = md5( $sql ); |
|
1060 $last_changed = wp_cache_get_last_changed( 'users' ); |
|
1061 |
|
1062 if ( empty( $args['orderby'] ) ) { |
|
1063 // Default order is by 'user_login'. |
|
1064 $ordersby = array( 'user_login' => '' ); |
|
1065 } elseif ( is_array( $args['orderby'] ) ) { |
|
1066 $ordersby = $args['orderby']; |
|
1067 } else { |
|
1068 // 'orderby' values may be a comma- or space-separated list. |
|
1069 $ordersby = preg_split( '/[,\s]+/', $args['orderby'] ); |
|
1070 } |
|
1071 |
|
1072 $blog_id = 0; |
|
1073 if ( isset( $args['blog_id'] ) ) { |
|
1074 $blog_id = absint( $args['blog_id'] ); |
|
1075 } |
|
1076 |
|
1077 if ( $args['has_published_posts'] || in_array( 'post_count', $ordersby, true ) ) { |
|
1078 $switch = $blog_id && get_current_blog_id() !== $blog_id; |
|
1079 if ( $switch ) { |
|
1080 switch_to_blog( $blog_id ); |
|
1081 } |
|
1082 |
|
1083 $last_changed .= wp_cache_get_last_changed( 'posts' ); |
|
1084 |
|
1085 if ( $switch ) { |
|
1086 restore_current_blog(); |
|
1087 } |
|
1088 } |
|
1089 |
|
1090 return "get_users:$key:$last_changed"; |
|
1091 } |
|
1092 |
|
1093 /** |
1003 * Parses an 'order' query variable and casts it to ASC or DESC as necessary. |
1094 * Parses an 'order' query variable and casts it to ASC or DESC as necessary. |
1004 * |
1095 * |
1005 * @since 4.2.0 |
1096 * @since 4.2.0 |
1006 * |
1097 * |
1007 * @param string $order The 'order' query variable. |
1098 * @param string $order The 'order' query variable. |
1021 |
1112 |
1022 /** |
1113 /** |
1023 * Makes private properties readable for backward compatibility. |
1114 * Makes private properties readable for backward compatibility. |
1024 * |
1115 * |
1025 * @since 4.0.0 |
1116 * @since 4.0.0 |
|
1117 * @since 6.4.0 Getting a dynamic property is deprecated. |
1026 * |
1118 * |
1027 * @param string $name Property to get. |
1119 * @param string $name Property to get. |
1028 * @return mixed Property. |
1120 * @return mixed Property. |
1029 */ |
1121 */ |
1030 public function __get( $name ) { |
1122 public function __get( $name ) { |
1031 if ( in_array( $name, $this->compat_fields, true ) ) { |
1123 if ( in_array( $name, $this->compat_fields, true ) ) { |
1032 return $this->$name; |
1124 return $this->$name; |
1033 } |
1125 } |
|
1126 |
|
1127 wp_trigger_error( |
|
1128 __METHOD__, |
|
1129 "The property `{$name}` is not declared. Getting a dynamic property is " . |
|
1130 'deprecated since version 6.4.0! Instead, declare the property on the class.', |
|
1131 E_USER_DEPRECATED |
|
1132 ); |
|
1133 return null; |
1034 } |
1134 } |
1035 |
1135 |
1036 /** |
1136 /** |
1037 * Makes private properties settable for backward compatibility. |
1137 * Makes private properties settable for backward compatibility. |
1038 * |
1138 * |
1039 * @since 4.0.0 |
1139 * @since 4.0.0 |
|
1140 * @since 6.4.0 Setting a dynamic property is deprecated. |
1040 * |
1141 * |
1041 * @param string $name Property to check if set. |
1142 * @param string $name Property to check if set. |
1042 * @param mixed $value Property value. |
1143 * @param mixed $value Property value. |
1043 * @return mixed Newly-set property. |
|
1044 */ |
1144 */ |
1045 public function __set( $name, $value ) { |
1145 public function __set( $name, $value ) { |
1046 if ( in_array( $name, $this->compat_fields, true ) ) { |
1146 if ( in_array( $name, $this->compat_fields, true ) ) { |
1047 return $this->$name = $value; |
1147 $this->$name = $value; |
1048 } |
1148 return; |
|
1149 } |
|
1150 |
|
1151 wp_trigger_error( |
|
1152 __METHOD__, |
|
1153 "The property `{$name}` is not declared. Setting a dynamic property is " . |
|
1154 'deprecated since version 6.4.0! Instead, declare the property on the class.', |
|
1155 E_USER_DEPRECATED |
|
1156 ); |
1049 } |
1157 } |
1050 |
1158 |
1051 /** |
1159 /** |
1052 * Makes private properties checkable for backward compatibility. |
1160 * Makes private properties checkable for backward compatibility. |
1053 * |
1161 * |
1054 * @since 4.0.0 |
1162 * @since 4.0.0 |
|
1163 * @since 6.4.0 Checking a dynamic property is deprecated. |
1055 * |
1164 * |
1056 * @param string $name Property to check if set. |
1165 * @param string $name Property to check if set. |
1057 * @return bool Whether the property is set. |
1166 * @return bool Whether the property is set. |
1058 */ |
1167 */ |
1059 public function __isset( $name ) { |
1168 public function __isset( $name ) { |
1060 if ( in_array( $name, $this->compat_fields, true ) ) { |
1169 if ( in_array( $name, $this->compat_fields, true ) ) { |
1061 return isset( $this->$name ); |
1170 return isset( $this->$name ); |
1062 } |
1171 } |
|
1172 |
|
1173 wp_trigger_error( |
|
1174 __METHOD__, |
|
1175 "The property `{$name}` is not declared. Checking `isset()` on a dynamic property " . |
|
1176 'is deprecated since version 6.4.0! Instead, declare the property on the class.', |
|
1177 E_USER_DEPRECATED |
|
1178 ); |
|
1179 return false; |
1063 } |
1180 } |
1064 |
1181 |
1065 /** |
1182 /** |
1066 * Makes private properties un-settable for backward compatibility. |
1183 * Makes private properties un-settable for backward compatibility. |
1067 * |
1184 * |
1068 * @since 4.0.0 |
1185 * @since 4.0.0 |
|
1186 * @since 6.4.0 Unsetting a dynamic property is deprecated. |
1069 * |
1187 * |
1070 * @param string $name Property to unset. |
1188 * @param string $name Property to unset. |
1071 */ |
1189 */ |
1072 public function __unset( $name ) { |
1190 public function __unset( $name ) { |
1073 if ( in_array( $name, $this->compat_fields, true ) ) { |
1191 if ( in_array( $name, $this->compat_fields, true ) ) { |
1074 unset( $this->$name ); |
1192 unset( $this->$name ); |
1075 } |
1193 return; |
|
1194 } |
|
1195 |
|
1196 wp_trigger_error( |
|
1197 __METHOD__, |
|
1198 "A property `{$name}` is not declared. Unsetting a dynamic property is " . |
|
1199 'deprecated since version 6.4.0! Instead, declare the property on the class.', |
|
1200 E_USER_DEPRECATED |
|
1201 ); |
1076 } |
1202 } |
1077 |
1203 |
1078 /** |
1204 /** |
1079 * Makes private/protected methods readable for backward compatibility. |
1205 * Makes private/protected methods readable for backward compatibility. |
1080 * |
1206 * |