/wp-includes/class-wp-user-query.php

  1. <?php 
  2. /** 
  3. * User API: WP_User_Query class 
  4. * 
  5. * @package WordPress 
  6. * @subpackage Users 
  7. * @since 4.4.0 
  8. */ 
  9.  
  10. /** 
  11. * Core class used for querying users. 
  12. * 
  13. * @since 3.1.0 
  14. * 
  15. * @see WP_User_Query::prepare_query() for information on accepted arguments. 
  16. */ 
  17. class WP_User_Query { 
  18.  
  19. /** 
  20. * Query vars, after parsing 
  21. * 
  22. * @since 3.5.0 
  23. * @access public 
  24. * @var array 
  25. */ 
  26. public $query_vars = array(); 
  27.  
  28. /** 
  29. * List of found user ids 
  30. * 
  31. * @since 3.1.0 
  32. * @access private 
  33. * @var array 
  34. */ 
  35. private $results; 
  36.  
  37. /** 
  38. * Total number of found users for the current query 
  39. * 
  40. * @since 3.1.0 
  41. * @access private 
  42. * @var int 
  43. */ 
  44. private $total_users = 0; 
  45.  
  46. /** 
  47. * Metadata query container. 
  48. * 
  49. * @since 4.2.0 
  50. * @access public 
  51. * @var WP_Meta_Query 
  52. */ 
  53. public $meta_query = false; 
  54.  
  55. /** 
  56. * The SQL query used to fetch matching users. 
  57. * 
  58. * @since 4.4.0 
  59. * @access public 
  60. * @var string 
  61. */ 
  62. public $request; 
  63.  
  64. private $compat_fields = array( 'results', 'total_users' ); 
  65.  
  66. // SQL clauses 
  67. public $query_fields; 
  68. public $query_from; 
  69. public $query_where; 
  70. public $query_orderby; 
  71. public $query_limit; 
  72.  
  73. /** 
  74. * PHP5 constructor. 
  75. * 
  76. * @since 3.1.0 
  77. * 
  78. * @param null|string|array $query Optional. The query variables. 
  79. */ 
  80. public function __construct( $query = null ) { 
  81. if ( ! empty( $query ) ) { 
  82. $this->prepare_query( $query ); 
  83. $this->query(); 
  84.  
  85. /** 
  86. * Fills in missing query variables with default values. 
  87. * 
  88. * @since 4.4.0 
  89. * @access public 
  90. * 
  91. * @param array $args Query vars, as passed to `WP_User_Query`. 
  92. * @return array Complete query variables with undefined ones filled in with defaults. 
  93. */ 
  94. public static function fill_query_vars( $args ) { 
  95. $defaults = array( 
  96. 'blog_id' => get_current_blog_id(),  
  97. 'role' => '',  
  98. 'role__in' => array(),  
  99. 'role__not_in' => array(),  
  100. 'meta_key' => '',  
  101. 'meta_value' => '',  
  102. 'meta_compare' => '',  
  103. 'include' => array(),  
  104. 'exclude' => array(),  
  105. 'search' => '',  
  106. 'search_columns' => array(),  
  107. 'orderby' => 'login',  
  108. 'order' => 'ASC',  
  109. 'offset' => '',  
  110. 'number' => '',  
  111. 'paged' => 1,  
  112. 'count_total' => true,  
  113. 'fields' => 'all',  
  114. 'who' => '',  
  115. 'has_published_posts' => null,  
  116. 'nicename' => '',  
  117. 'nicename__in' => array(),  
  118. 'nicename__not_in' => array(),  
  119. 'login' => '',  
  120. 'login__in' => array(),  
  121. 'login__not_in' => array() 
  122. ); 
  123.  
  124. return wp_parse_args( $args, $defaults ); 
  125.  
  126. /** 
  127. * Prepare the query variables. 
  128. * 
  129. * @since 3.1.0 
  130. * @since 4.1.0 Added the ability to order by the `include` value. 
  131. * @since 4.2.0 Added 'meta_value_num' support for `$orderby` parameter. Added multi-dimensional array syntax 
  132. * for `$orderby` parameter. 
  133. * @since 4.3.0 Added 'has_published_posts' parameter. 
  134. * @since 4.4.0 Added 'paged', 'role__in', and 'role__not_in' parameters. The 'role' parameter was updated to 
  135. * permit an array or comma-separated list of values. The 'number' parameter was updated to support 
  136. * querying for all users with using -1. 
  137. * @since 4.7.0 Added 'nicename', 'nicename__in', 'nicename__not_in', 'login', 'login__in',  
  138. * and 'login__not_in' parameters. 
  139. * 
  140. * @access public 
  141. * 
  142. * @global wpdb $wpdb WordPress database abstraction object. 
  143. * @global int $blog_id 
  144. * 
  145. * @param string|array $query { 
  146. * Optional. Array or string of Query parameters. 
  147. * 
  148. * @type int $blog_id The site ID. Default is the current site. 
  149. * @type string|array $role An array or a comma-separated list of role names that users must match 
  150. * to be included in results. Note that this is an inclusive list: users 
  151. * must match *each* role. Default empty. 
  152. * @type array $role__in An array of role names. Matched users must have at least one of these 
  153. * roles. Default empty array. 
  154. * @type array $role__not_in An array of role names to exclude. Users matching one or more of these 
  155. * roles will not be included in results. Default empty array. 
  156. * @type string $meta_key User meta key. Default empty. 
  157. * @type string $meta_value User meta value. Default empty. 
  158. * @type string $meta_compare Comparison operator to test the `$meta_value`. Accepts '=', '!=',  
  159. * '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN',  
  160. * 'BETWEEN', 'NOT BETWEEN', 'EXISTS', 'NOT EXISTS', 'REGEXP',  
  161. * 'NOT REGEXP', or 'RLIKE'. Default '='. 
  162. * @type array $include An array of user IDs to include. Default empty array. 
  163. * @type array $exclude An array of user IDs to exclude. Default empty array. 
  164. * @type string $search Search keyword. Searches for possible string matches on columns. 
  165. * When `$search_columns` is left empty, it tries to determine which 
  166. * column to search in based on search string. Default empty. 
  167. * @type array $search_columns Array of column names to be searched. Accepts 'ID', 'login',  
  168. * 'nicename', 'email', 'url'. Default empty array. 
  169. * @type string|array $orderby Field(s) to sort the retrieved users by. May be a single value,  
  170. * an array of values, or a multi-dimensional array with fields as 
  171. * keys and orders ('ASC' or 'DESC') as values. Accepted values are 
  172. * 'ID', 'display_name' (or 'name'), 'include', 'user_login' 
  173. * (or 'login'), 'login__in', 'user_nicename' (or 'nicename'),  
  174. * 'nicename__in', 'user_email (or 'email'), 'user_url' (or 'url'),  
  175. * 'user_registered' (or 'registered'), 'post_count', 'meta_value',  
  176. * 'meta_value_num', the value of `$meta_key`, or an array key of 
  177. * `$meta_query`. To use 'meta_value' or 'meta_value_num', `$meta_key` 
  178. * must be also be defined. Default 'user_login'. 
  179. * @type string $order Designates ascending or descending order of users. Order values 
  180. * passed as part of an `$orderby` array take precedence over this 
  181. * parameter. Accepts 'ASC', 'DESC'. Default 'ASC'. 
  182. * @type int $offset Number of users to offset in retrieved results. Can be used in 
  183. * conjunction with pagination. Default 0. 
  184. * @type int $number Number of users to limit the query for. Can be used in 
  185. * conjunction with pagination. Value -1 (all) is supported, but 
  186. * should be used with caution on larger sites. 
  187. * Default empty (all users). 
  188. * @type int $paged When used with number, defines the page of results to return. 
  189. * Default 1. 
  190. * @type bool $count_total Whether to count the total number of users found. If pagination 
  191. * is not needed, setting this to false can improve performance. 
  192. * Default true. 
  193. * @type string|array $fields Which fields to return. Single or all fields (string), or array 
  194. * of fields. Accepts 'ID', 'display_name', 'user_login',  
  195. * 'user_nicename', 'user_email', 'user_url', 'user_registered'. 
  196. * Use 'all' for all fields and 'all_with_meta' to include 
  197. * meta fields. Default 'all'. 
  198. * @type string $who Type of users to query. Accepts 'authors'. 
  199. * Default empty (all users). 
  200. * @type bool|array $has_published_posts Pass an array of post types to filter results to users who have 
  201. * published posts in those post types. `true` is an alias for all 
  202. * public post types. 
  203. * @type string $nicename The user nicename. Default empty. 
  204. * @type array $nicename__in An array of nicenames to include. Users matching one of these 
  205. * nicenames will be included in results. Default empty array. 
  206. * @type array $nicename__not_in An array of nicenames to exclude. Users matching one of these 
  207. * nicenames will not be included in results. Default empty array. 
  208. * @type string $login The user login. Default empty. 
  209. * @type array $login__in An array of logins to include. Users matching one of these 
  210. * logins will be included in results. Default empty array. 
  211. * @type array $login__not_in An array of logins to exclude. Users matching one of these 
  212. * logins will not be included in results. Default empty array. 
  213. * } 
  214. */ 
  215. public function prepare_query( $query = array() ) { 
  216. global $wpdb; 
  217.  
  218. if ( empty( $this->query_vars ) || ! empty( $query ) ) { 
  219. $this->query_limit = null; 
  220. $this->query_vars = $this->fill_query_vars( $query ); 
  221.  
  222. /** 
  223. * Fires before the WP_User_Query has been parsed. 
  224. * 
  225. * The passed WP_User_Query object contains the query variables, not 
  226. * yet passed into SQL. 
  227. * 
  228. * @since 4.0.0 
  229. * 
  230. * @param WP_User_Query $this The current WP_User_Query instance,  
  231. * passed by reference. 
  232. */ 
  233. do_action( 'pre_get_users', $this ); 
  234.  
  235. // Ensure that query vars are filled after 'pre_get_users'. 
  236. $qv =& $this->query_vars; 
  237. $qv = $this->fill_query_vars( $qv ); 
  238.  
  239. if ( is_array( $qv['fields'] ) ) { 
  240. $qv['fields'] = array_unique( $qv['fields'] ); 
  241.  
  242. $this->query_fields = array(); 
  243. foreach ( $qv['fields'] as $field ) { 
  244. $field = 'ID' === $field ? 'ID' : sanitize_key( $field ); 
  245. $this->query_fields[] = "$wpdb->users.$field"; 
  246. $this->query_fields = implode( ', ', $this->query_fields ); 
  247. } elseif ( 'all' == $qv['fields'] ) { 
  248. $this->query_fields = "$wpdb->users.*"; 
  249. } else { 
  250. $this->query_fields = "$wpdb->users.ID"; 
  251.  
  252. if ( isset( $qv['count_total'] ) && $qv['count_total'] ) 
  253. $this->query_fields = 'SQL_CALC_FOUND_ROWS ' . $this->query_fields; 
  254.  
  255. $this->query_from = "FROM $wpdb->users"; 
  256. $this->query_where = "WHERE 1=1"; 
  257.  
  258. // Parse and sanitize 'include', for use by 'orderby' as well as 'include' below. 
  259. if ( ! empty( $qv['include'] ) ) { 
  260. $include = wp_parse_id_list( $qv['include'] ); 
  261. } else { 
  262. $include = false; 
  263.  
  264. $blog_id = 0; 
  265. if ( isset( $qv['blog_id'] ) ) { 
  266. $blog_id = absint( $qv['blog_id'] ); 
  267.  
  268. if ( $qv['has_published_posts'] && $blog_id ) { 
  269. if ( true === $qv['has_published_posts'] ) { 
  270. $post_types = get_post_types( array( 'public' => true ) ); 
  271. } else { 
  272. $post_types = (array) $qv['has_published_posts']; 
  273.  
  274. foreach ( $post_types as &$post_type ) { 
  275. $post_type = $wpdb->prepare( '%s', $post_type ); 
  276.  
  277. $posts_table = $wpdb->get_blog_prefix( $blog_id ) . 'posts'; 
  278. $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 ) . " ) )"; 
  279.  
  280. // nicename 
  281. if ( '' !== $qv['nicename']) { 
  282. $this->query_where .= $wpdb->prepare( ' AND user_nicename = %s', $qv['nicename'] ); 
  283.  
  284. if ( ! empty( $qv['nicename__in'] ) ) { 
  285. $sanitized_nicename__in = array_map( 'esc_sql', $qv['nicename__in'] ); 
  286. $nicename__in = implode( "', '", $sanitized_nicename__in ); 
  287. $this->query_where .= " AND user_nicename IN ( '$nicename__in' )"; 
  288.  
  289. if ( ! empty( $qv['nicename__not_in'] ) ) { 
  290. $sanitized_nicename__not_in = array_map( 'esc_sql', $qv['nicename__not_in'] ); 
  291. $nicename__not_in = implode( "', '", $sanitized_nicename__not_in ); 
  292. $this->query_where .= " AND user_nicename NOT IN ( '$nicename__not_in' )"; 
  293.  
  294. // login 
  295. if ( '' !== $qv['login']) { 
  296. $this->query_where .= $wpdb->prepare( ' AND user_login = %s', $qv['login'] ); 
  297.  
  298. if ( ! empty( $qv['login__in'] ) ) { 
  299. $sanitized_login__in = array_map( 'esc_sql', $qv['login__in'] ); 
  300. $login__in = implode( "', '", $sanitized_login__in ); 
  301. $this->query_where .= " AND user_login IN ( '$login__in' )"; 
  302.  
  303. if ( ! empty( $qv['login__not_in'] ) ) { 
  304. $sanitized_login__not_in = array_map( 'esc_sql', $qv['login__not_in'] ); 
  305. $login__not_in = implode( "', '", $sanitized_login__not_in ); 
  306. $this->query_where .= " AND user_login NOT IN ( '$login__not_in' )"; 
  307.  
  308. // Meta query. 
  309. $this->meta_query = new WP_Meta_Query(); 
  310. $this->meta_query->parse_query_vars( $qv ); 
  311.  
  312. if ( isset( $qv['who'] ) && 'authors' == $qv['who'] && $blog_id ) { 
  313. $who_query = array( 
  314. 'key' => $wpdb->get_blog_prefix( $blog_id ) . 'user_level',  
  315. 'value' => 0,  
  316. 'compare' => '!=',  
  317. ); 
  318.  
  319. // Prevent extra meta query. 
  320. $qv['blog_id'] = $blog_id = 0; 
  321.  
  322. if ( empty( $this->meta_query->queries ) ) { 
  323. $this->meta_query->queries = array( $who_query ); 
  324. } else { 
  325. // Append the cap query to the original queries and reparse the query. 
  326. $this->meta_query->queries = array( 
  327. 'relation' => 'AND',  
  328. array( $this->meta_query->queries, $who_query ),  
  329. ); 
  330.  
  331. $this->meta_query->parse_query_vars( $this->meta_query->queries ); 
  332.  
  333. $roles = array(); 
  334. if ( isset( $qv['role'] ) ) { 
  335. if ( is_array( $qv['role'] ) ) { 
  336. $roles = $qv['role']; 
  337. } elseif ( is_string( $qv['role'] ) && ! empty( $qv['role'] ) ) { 
  338. $roles = array_map( 'trim', explode( ', ', $qv['role'] ) ); 
  339.  
  340. $role__in = array(); 
  341. if ( isset( $qv['role__in'] ) ) { 
  342. $role__in = (array) $qv['role__in']; 
  343.  
  344. $role__not_in = array(); 
  345. if ( isset( $qv['role__not_in'] ) ) { 
  346. $role__not_in = (array) $qv['role__not_in']; 
  347.  
  348. if ( $blog_id && ( ! empty( $roles ) || ! empty( $role__in ) || ! empty( $role__not_in ) || is_multisite() ) ) { 
  349. $role_queries = array(); 
  350.  
  351. $roles_clauses = array( 'relation' => 'AND' ); 
  352. if ( ! empty( $roles ) ) { 
  353. foreach ( $roles as $role ) { 
  354. $roles_clauses[] = array( 
  355. 'key' => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',  
  356. 'value' => '"' . $role . '"',  
  357. 'compare' => 'LIKE',  
  358. ); 
  359.  
  360. $role_queries[] = $roles_clauses; 
  361.  
  362. $role__in_clauses = array( 'relation' => 'OR' ); 
  363. if ( ! empty( $role__in ) ) { 
  364. foreach ( $role__in as $role ) { 
  365. $role__in_clauses[] = array( 
  366. 'key' => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',  
  367. 'value' => '"' . $role . '"',  
  368. 'compare' => 'LIKE',  
  369. ); 
  370.  
  371. $role_queries[] = $role__in_clauses; 
  372.  
  373. $role__not_in_clauses = array( 'relation' => 'AND' ); 
  374. if ( ! empty( $role__not_in ) ) { 
  375. foreach ( $role__not_in as $role ) { 
  376. $role__not_in_clauses[] = array( 
  377. 'key' => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',  
  378. 'value' => '"' . $role . '"',  
  379. 'compare' => 'NOT LIKE',  
  380. ); 
  381.  
  382. $role_queries[] = $role__not_in_clauses; 
  383.  
  384. // If there are no specific roles named, make sure the user is a member of the site. 
  385. if ( empty( $role_queries ) ) { 
  386. $role_queries[] = array( 
  387. 'key' => $wpdb->get_blog_prefix( $blog_id ) . 'capabilities',  
  388. 'compare' => 'EXISTS',  
  389. ); 
  390.  
  391. // Specify that role queries should be joined with AND. 
  392. $role_queries['relation'] = 'AND'; 
  393.  
  394. if ( empty( $this->meta_query->queries ) ) { 
  395. $this->meta_query->queries = $role_queries; 
  396. } else { 
  397. // Append the cap query to the original queries and reparse the query. 
  398. $this->meta_query->queries = array( 
  399. 'relation' => 'AND',  
  400. array( $this->meta_query->queries, $role_queries ),  
  401. ); 
  402.  
  403. $this->meta_query->parse_query_vars( $this->meta_query->queries ); 
  404.  
  405. if ( ! empty( $this->meta_query->queries ) ) { 
  406. $clauses = $this->meta_query->get_sql( 'user', $wpdb->users, 'ID', $this ); 
  407. $this->query_from .= $clauses['join']; 
  408. $this->query_where .= $clauses['where']; 
  409.  
  410. if ( $this->meta_query->has_or_relation() ) { 
  411. $this->query_fields = 'DISTINCT ' . $this->query_fields; 
  412.  
  413. // sorting 
  414. $qv['order'] = isset( $qv['order'] ) ? strtoupper( $qv['order'] ) : ''; 
  415. $order = $this->parse_order( $qv['order'] ); 
  416.  
  417. if ( empty( $qv['orderby'] ) ) { 
  418. // Default order is by 'user_login'. 
  419. $ordersby = array( 'user_login' => $order ); 
  420. } elseif ( is_array( $qv['orderby'] ) ) { 
  421. $ordersby = $qv['orderby']; 
  422. } else { 
  423. // 'orderby' values may be a comma- or space-separated list. 
  424. $ordersby = preg_split( '/[, \s]+/', $qv['orderby'] ); 
  425.  
  426. $orderby_array = array(); 
  427. foreach ( $ordersby as $_key => $_value ) { 
  428. if ( ! $_value ) { 
  429. continue; 
  430.  
  431. if ( is_int( $_key ) ) { 
  432. // Integer key means this is a flat array of 'orderby' fields. 
  433. $_orderby = $_value; 
  434. $_order = $order; 
  435. } else { 
  436. // Non-integer key means this the key is the field and the value is ASC/DESC. 
  437. $_orderby = $_key; 
  438. $_order = $_value; 
  439.  
  440. $parsed = $this->parse_orderby( $_orderby ); 
  441.  
  442. if ( ! $parsed ) { 
  443. continue; 
  444.  
  445. if ( 'nicename__in' === $_orderby || 'login__in' === $_orderby ) { 
  446. $orderby_array[] = $parsed; 
  447. } else { 
  448. $orderby_array[] = $parsed . ' ' . $this->parse_order( $_order ); 
  449.  
  450. // If no valid clauses were found, order by user_login. 
  451. if ( empty( $orderby_array ) ) { 
  452. $orderby_array[] = "user_login $order"; 
  453.  
  454. $this->query_orderby = 'ORDER BY ' . implode( ', ', $orderby_array ); 
  455.  
  456. // limit 
  457. if ( isset( $qv['number'] ) && $qv['number'] > 0 ) { 
  458. if ( $qv['offset'] ) { 
  459. $this->query_limit = $wpdb->prepare("LIMIT %d, %d", $qv['offset'], $qv['number']); 
  460. } else { 
  461. $this->query_limit = $wpdb->prepare( "LIMIT %d, %d", $qv['number'] * ( $qv['paged'] - 1 ), $qv['number'] ); 
  462.  
  463. $search = ''; 
  464. if ( isset( $qv['search'] ) ) 
  465. $search = trim( $qv['search'] ); 
  466.  
  467. if ( $search ) { 
  468. $leading_wild = ( ltrim($search, '*') != $search ); 
  469. $trailing_wild = ( rtrim($search, '*') != $search ); 
  470. if ( $leading_wild && $trailing_wild ) 
  471. $wild = 'both'; 
  472. elseif ( $leading_wild ) 
  473. $wild = 'leading'; 
  474. elseif ( $trailing_wild ) 
  475. $wild = 'trailing'; 
  476. else 
  477. $wild = false; 
  478. if ( $wild ) 
  479. $search = trim($search, '*'); 
  480.  
  481. $search_columns = array(); 
  482. if ( $qv['search_columns'] ) 
  483. $search_columns = array_intersect( $qv['search_columns'], array( 'ID', 'user_login', 'user_email', 'user_url', 'user_nicename' ) ); 
  484. if ( ! $search_columns ) { 
  485. if ( false !== strpos( $search, '@') ) 
  486. $search_columns = array('user_email'); 
  487. elseif ( is_numeric($search) ) 
  488. $search_columns = array('user_login', 'ID'); 
  489. elseif ( preg_match('|^https?://|', $search) && ! ( is_multisite() && wp_is_large_network( 'users' ) ) ) 
  490. $search_columns = array('user_url'); 
  491. else 
  492. $search_columns = array('user_login', 'user_url', 'user_email', 'user_nicename', 'display_name'); 
  493.  
  494. /** 
  495. * Filters the columns to search in a WP_User_Query search. 
  496. * 
  497. * The default columns depend on the search term, and include 'user_email',  
  498. * 'user_login', 'ID', 'user_url', 'display_name', and 'user_nicename'. 
  499. * 
  500. * @since 3.6.0 
  501. * 
  502. * @param array $search_columns Array of column names to be searched. 
  503. * @param string $search Text being searched. 
  504. * @param WP_User_Query $this The current WP_User_Query instance. 
  505. */ 
  506. $search_columns = apply_filters( 'user_search_columns', $search_columns, $search, $this ); 
  507.  
  508. $this->query_where .= $this->get_search_sql( $search, $search_columns, $wild ); 
  509.  
  510. if ( ! empty( $include ) ) { 
  511. // Sanitized earlier. 
  512. $ids = implode( ', ', $include ); 
  513. $this->query_where .= " AND $wpdb->users.ID IN ($ids)"; 
  514. } elseif ( ! empty( $qv['exclude'] ) ) { 
  515. $ids = implode( ', ', wp_parse_id_list( $qv['exclude'] ) ); 
  516. $this->query_where .= " AND $wpdb->users.ID NOT IN ($ids)"; 
  517.  
  518. // Date queries are allowed for the user_registered field. 
  519. if ( ! empty( $qv['date_query'] ) && is_array( $qv['date_query'] ) ) { 
  520. $date_query = new WP_Date_Query( $qv['date_query'], 'user_registered' ); 
  521. $this->query_where .= $date_query->get_sql(); 
  522.  
  523. /** 
  524. * Fires after the WP_User_Query has been parsed, and before 
  525. * the query is executed. 
  526. * 
  527. * The passed WP_User_Query object contains SQL parts formed 
  528. * from parsing the given query. 
  529. * 
  530. * @since 3.1.0 
  531. * 
  532. * @param WP_User_Query $this The current WP_User_Query instance,  
  533. * passed by reference. 
  534. */ 
  535. do_action_ref_array( 'pre_user_query', array( &$this ) ); 
  536.  
  537. /** 
  538. * Execute the query, with the current variables. 
  539. * 
  540. * @since 3.1.0 
  541. * 
  542. * @global wpdb $wpdb WordPress database abstraction object. 
  543. */ 
  544. public function query() { 
  545. global $wpdb; 
  546.  
  547. $qv =& $this->query_vars; 
  548.  
  549. $this->request = "SELECT $this->query_fields $this->query_from $this->query_where $this->query_orderby $this->query_limit"; 
  550.  
  551. if ( is_array( $qv['fields'] ) || 'all' == $qv['fields'] ) { 
  552. $this->results = $wpdb->get_results( $this->request ); 
  553. } else { 
  554. $this->results = $wpdb->get_col( $this->request ); 
  555.  
  556. /** 
  557. * Filters SELECT FOUND_ROWS() query for the current WP_User_Query instance. 
  558. * 
  559. * @since 3.2.0 
  560. * 
  561. * @global wpdb $wpdb WordPress database abstraction object. 
  562. * 
  563. * @param string $sql The SELECT FOUND_ROWS() query for the current WP_User_Query. 
  564. */ 
  565. if ( isset( $qv['count_total'] ) && $qv['count_total'] ) 
  566. $this->total_users = $wpdb->get_var( apply_filters( 'found_users_query', 'SELECT FOUND_ROWS()' ) ); 
  567.  
  568. if ( !$this->results ) 
  569. return; 
  570.  
  571. if ( 'all_with_meta' == $qv['fields'] ) { 
  572. cache_users( $this->results ); 
  573.  
  574. $r = array(); 
  575. foreach ( $this->results as $userid ) 
  576. $r[ $userid ] = new WP_User( $userid, '', $qv['blog_id'] ); 
  577.  
  578. $this->results = $r; 
  579. } elseif ( 'all' == $qv['fields'] ) { 
  580. foreach ( $this->results as $key => $user ) { 
  581. $this->results[ $key ] = new WP_User( $user, '', $qv['blog_id'] ); 
  582.  
  583. /** 
  584. * Retrieve query variable. 
  585. * 
  586. * @since 3.5.0 
  587. * @access public 
  588. * 
  589. * @param string $query_var Query variable key. 
  590. * @return mixed 
  591. */ 
  592. public function get( $query_var ) { 
  593. if ( isset( $this->query_vars[$query_var] ) ) 
  594. return $this->query_vars[$query_var]; 
  595.  
  596. return null; 
  597.  
  598. /** 
  599. * Set query variable. 
  600. * 
  601. * @since 3.5.0 
  602. * @access public 
  603. * 
  604. * @param string $query_var Query variable key. 
  605. * @param mixed $value Query variable value. 
  606. */ 
  607. public function set( $query_var, $value ) { 
  608. $this->query_vars[$query_var] = $value; 
  609.  
  610. /** 
  611. * Used internally to generate an SQL string for searching across multiple columns 
  612. * 
  613. * @access protected 
  614. * @since 3.1.0 
  615. * 
  616. * @global wpdb $wpdb WordPress database abstraction object. 
  617. * 
  618. * @param string $string 
  619. * @param array $cols 
  620. * @param bool $wild Whether to allow wildcard searches. Default is false for Network Admin, true for single site. 
  621. * Single site allows leading and trailing wildcards, Network Admin only trailing. 
  622. * @return string 
  623. */ 
  624. protected function get_search_sql( $string, $cols, $wild = false ) { 
  625. global $wpdb; 
  626.  
  627. $searches = array(); 
  628. $leading_wild = ( 'leading' == $wild || 'both' == $wild ) ? '%' : ''; 
  629. $trailing_wild = ( 'trailing' == $wild || 'both' == $wild ) ? '%' : ''; 
  630. $like = $leading_wild . $wpdb->esc_like( $string ) . $trailing_wild; 
  631.  
  632. foreach ( $cols as $col ) { 
  633. if ( 'ID' == $col ) { 
  634. $searches[] = $wpdb->prepare( "$col = %s", $string ); 
  635. } else { 
  636. $searches[] = $wpdb->prepare( "$col LIKE %s", $like ); 
  637.  
  638. return ' AND (' . implode(' OR ', $searches) . ')'; 
  639.  
  640. /** 
  641. * Return the list of users. 
  642. * 
  643. * @since 3.1.0 
  644. * @access public 
  645. * 
  646. * @return array Array of results. 
  647. */ 
  648. public function get_results() { 
  649. return $this->results; 
  650.  
  651. /** 
  652. * Return the total number of users for the current query. 
  653. * 
  654. * @since 3.1.0 
  655. * @access public 
  656. * 
  657. * @return int Number of total users. 
  658. */ 
  659. public function get_total() { 
  660. return $this->total_users; 
  661.  
  662. /** 
  663. * Parse and sanitize 'orderby' keys passed to the user query. 
  664. * 
  665. * @since 4.2.0 
  666. * @access protected 
  667. * 
  668. * @global wpdb $wpdb WordPress database abstraction object. 
  669. * 
  670. * @param string $orderby Alias for the field to order by. 
  671. * @return string Value to used in the ORDER clause, if `$orderby` is valid. 
  672. */ 
  673. protected function parse_orderby( $orderby ) { 
  674. global $wpdb; 
  675.  
  676. $meta_query_clauses = $this->meta_query->get_clauses(); 
  677.  
  678. $_orderby = ''; 
  679. if ( in_array( $orderby, array( 'login', 'nicename', 'email', 'url', 'registered' ) ) ) { 
  680. $_orderby = 'user_' . $orderby; 
  681. } elseif ( in_array( $orderby, array( 'user_login', 'user_nicename', 'user_email', 'user_url', 'user_registered' ) ) ) { 
  682. $_orderby = $orderby; 
  683. } elseif ( 'name' == $orderby || 'display_name' == $orderby ) { 
  684. $_orderby = 'display_name'; 
  685. } elseif ( 'post_count' == $orderby ) { 
  686. // todo: avoid the JOIN 
  687. $where = get_posts_by_author_sql( 'post' ); 
  688. $this->query_from .= " LEFT OUTER JOIN ( 
  689. SELECT post_author, COUNT(*) as post_count 
  690. FROM $wpdb->posts 
  691. $where 
  692. GROUP BY post_author 
  693. ) p ON ({$wpdb->users}.ID = p.post_author) 
  694. "; 
  695. $_orderby = 'post_count'; 
  696. } elseif ( 'ID' == $orderby || 'id' == $orderby ) { 
  697. $_orderby = 'ID'; 
  698. } elseif ( 'meta_value' == $orderby || $this->get( 'meta_key' ) == $orderby ) { 
  699. $_orderby = "$wpdb->usermeta.meta_value"; 
  700. } elseif ( 'meta_value_num' == $orderby ) { 
  701. $_orderby = "$wpdb->usermeta.meta_value+0"; 
  702. } elseif ( 'include' === $orderby && ! empty( $this->query_vars['include'] ) ) { 
  703. $include = wp_parse_id_list( $this->query_vars['include'] ); 
  704. $include_sql = implode( ', ', $include ); 
  705. $_orderby = "FIELD( $wpdb->users.ID, $include_sql )"; 
  706. } elseif ( 'nicename__in' === $orderby ) { 
  707. $sanitized_nicename__in = array_map( 'esc_sql', $this->query_vars['nicename__in'] ); 
  708. $nicename__in = implode( "', '", $sanitized_nicename__in ); 
  709. $_orderby = "FIELD( user_nicename, '$nicename__in' )"; 
  710. } elseif ( 'login__in' === $orderby ) { 
  711. $sanitized_login__in = array_map( 'esc_sql', $this->query_vars['login__in'] ); 
  712. $login__in = implode( "', '", $sanitized_login__in ); 
  713. $_orderby = "FIELD( user_login, '$login__in' )"; 
  714. } elseif ( isset( $meta_query_clauses[ $orderby ] ) ) { 
  715. $meta_clause = $meta_query_clauses[ $orderby ]; 
  716. $_orderby = sprintf( "CAST(%s.meta_value AS %s)", esc_sql( $meta_clause['alias'] ), esc_sql( $meta_clause['cast'] ) ); 
  717.  
  718. return $_orderby; 
  719.  
  720. /** 
  721. * Parse an 'order' query variable and cast it to ASC or DESC as necessary. 
  722. * 
  723. * @since 4.2.0 
  724. * @access protected 
  725. * 
  726. * @param string $order The 'order' query variable. 
  727. * @return string The sanitized 'order' query variable. 
  728. */ 
  729. protected function parse_order( $order ) { 
  730. if ( ! is_string( $order ) || empty( $order ) ) { 
  731. return 'DESC'; 
  732.  
  733. if ( 'ASC' === strtoupper( $order ) ) { 
  734. return 'ASC'; 
  735. } else { 
  736. return 'DESC'; 
  737.  
  738. /** 
  739. * Make private properties readable for backward compatibility. 
  740. * 
  741. * @since 4.0.0 
  742. * @access public 
  743. * 
  744. * @param string $name Property to get. 
  745. * @return mixed Property. 
  746. */ 
  747. public function __get( $name ) { 
  748. if ( in_array( $name, $this->compat_fields ) ) { 
  749. return $this->$name; 
  750.  
  751. /** 
  752. * Make private properties settable for backward compatibility. 
  753. * 
  754. * @since 4.0.0 
  755. * @access public 
  756. * 
  757. * @param string $name Property to check if set. 
  758. * @param mixed $value Property value. 
  759. * @return mixed Newly-set property. 
  760. */ 
  761. public function __set( $name, $value ) { 
  762. if ( in_array( $name, $this->compat_fields ) ) { 
  763. return $this->$name = $value; 
  764.  
  765. /** 
  766. * Make private properties checkable for backward compatibility. 
  767. * 
  768. * @since 4.0.0 
  769. * @access public 
  770. * 
  771. * @param string $name Property to check if set. 
  772. * @return bool Whether the property is set. 
  773. */ 
  774. public function __isset( $name ) { 
  775. if ( in_array( $name, $this->compat_fields ) ) { 
  776. return isset( $this->$name ); 
  777.  
  778. /** 
  779. * Make private properties un-settable for backward compatibility. 
  780. * 
  781. * @since 4.0.0 
  782. * @access public 
  783. * 
  784. * @param string $name Property to unset. 
  785. */ 
  786. public function __unset( $name ) { 
  787. if ( in_array( $name, $this->compat_fields ) ) { 
  788. unset( $this->$name ); 
  789.  
  790. /** 
  791. * Make private/protected methods readable for backward compatibility. 
  792. * 
  793. * @since 4.0.0 
  794. * @access public 
  795. * 
  796. * @param callable $name Method to call. 
  797. * @param array $arguments Arguments to pass when calling. 
  798. * @return mixed Return value of the callback, false otherwise. 
  799. */ 
  800. public function __call( $name, $arguments ) { 
  801. if ( 'get_search_sql' === $name ) { 
  802. return call_user_func_array( array( $this, $name ), $arguments ); 
  803. return false; 
.