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