Issues (2010)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

wp-includes/query.php (50 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * WordPress Query API
4
 *
5
 * The query API attempts to get which part of WordPress the user is on. It
6
 * also provides functionality for getting URL query information.
7
 *
8
 * @link https://codex.wordpress.org/The_Loop More information on The Loop.
9
 *
10
 * @package WordPress
11
 * @subpackage Query
12
 */
13
14
/**
15
 * Retrieve variable in the WP_Query class.
16
 *
17
 * @since 1.5.0
18
 * @since 3.9.0 The `$default` argument was introduced.
19
 *
20
 * @global WP_Query $wp_query Global WP_Query instance.
21
 *
22
 * @param string $var       The variable key to retrieve.
23
 * @param mixed  $default   Optional. Value to return if the query variable is not set. Default empty.
24
 * @return mixed Contents of the query variable.
25
 */
26
function get_query_var( $var, $default = '' ) {
27
	global $wp_query;
28
	return $wp_query->get( $var, $default );
29
}
30
31
/**
32
 * Retrieve the currently-queried object.
33
 *
34
 * Wrapper for WP_Query::get_queried_object().
35
 *
36
 * @since 3.1.0
37
 * @access public
38
 *
39
 * @global WP_Query $wp_query Global WP_Query instance.
40
 *
41
 * @return object Queried object.
42
 */
43
function get_queried_object() {
44
	global $wp_query;
45
	return $wp_query->get_queried_object();
46
}
47
48
/**
49
 * Retrieve ID of the current queried object.
50
 *
51
 * Wrapper for WP_Query::get_queried_object_id().
52
 *
53
 * @since 3.1.0
54
 *
55
 * @global WP_Query $wp_query Global WP_Query instance.
56
 *
57
 * @return int ID of the queried object.
58
 */
59
function get_queried_object_id() {
60
	global $wp_query;
61
	return $wp_query->get_queried_object_id();
62
}
63
64
/**
65
 * Set query variable.
66
 *
67
 * @since 2.2.0
68
 *
69
 * @global WP_Query $wp_query Global WP_Query instance.
70
 *
71
 * @param string $var   Query variable key.
72
 * @param mixed  $value Query variable value.
73
 */
74
function set_query_var( $var, $value ) {
75
	global $wp_query;
76
	$wp_query->set( $var, $value );
77
}
78
79
/**
80
 * Sets up The Loop with query parameters.
81
 *
82
 * Note: This function will completely override the main query and isn't intended for use
83
 * by plugins or themes. Its overly-simplistic approach to modifying the main query can be
84
 * problematic and should be avoided wherever possible. In most cases, there are better,
85
 * more performant options for modifying the main query such as via the {@see 'pre_get_posts'}
86
 * action within WP_Query.
87
 *
88
 * This must not be used within the WordPress Loop.
89
 *
90
 * @since 1.5.0
91
 *
92
 * @global WP_Query $wp_query Global WP_Query instance.
93
 *
94
 * @param array|string $query Array or string of WP_Query arguments.
95
 * @return array List of post objects.
96
 */
97
function query_posts($query) {
98
	$GLOBALS['wp_query'] = new WP_Query();
99
	return $GLOBALS['wp_query']->query($query);
0 ignored issues
show
It seems like $query defined by parameter $query on line 97 can also be of type array; however, WP_Query::query() does only seem to accept string, maybe add an additional type check?

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.

Loading history...
100
}
101
102
/**
103
 * Destroys the previous query and sets up a new query.
104
 *
105
 * This should be used after query_posts() and before another query_posts().
106
 * This will remove obscure bugs that occur when the previous WP_Query object
107
 * is not destroyed properly before another is set up.
108
 *
109
 * @since 2.3.0
110
 *
111
 * @global WP_Query $wp_query     Global WP_Query instance.
112
 * @global WP_Query $wp_the_query Copy of the global WP_Query instance created during wp_reset_query().
113
 */
114
function wp_reset_query() {
115
	$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
116
	wp_reset_postdata();
117
}
118
119
/**
120
 * After looping through a separate query, this function restores
121
 * the $post global to the current post in the main query.
122
 *
123
 * @since 3.0.0
124
 *
125
 * @global WP_Query $wp_query Global WP_Query instance.
126
 */
127
function wp_reset_postdata() {
128
	global $wp_query;
129
130
	if ( isset( $wp_query ) ) {
131
		$wp_query->reset_postdata();
132
	}
133
}
134
135
/*
136
 * Query type checks.
137
 */
138
139
/**
140
 * Is the query for an existing archive page?
141
 *
142
 * Month, Year, Category, Author, Post Type archive...
143
 *
144
 * @since 1.5.0
145
 *
146
 * @global WP_Query $wp_query Global WP_Query instance.
147
 *
148
 * @return bool
149
 */
150 View Code Duplication
function is_archive() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
151
	global $wp_query;
152
153
	if ( ! isset( $wp_query ) ) {
154
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
155
		return false;
156
	}
157
158
	return $wp_query->is_archive();
159
}
160
161
/**
162
 * Is the query for an existing post type archive page?
163
 *
164
 * @since 3.1.0
165
 *
166
 * @global WP_Query $wp_query Global WP_Query instance.
167
 *
168
 * @param string|array $post_types Optional. Post type or array of posts types to check against.
169
 * @return bool
170
 */
171 View Code Duplication
function is_post_type_archive( $post_types = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
172
	global $wp_query;
173
174
	if ( ! isset( $wp_query ) ) {
175
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
176
		return false;
177
	}
178
179
	return $wp_query->is_post_type_archive( $post_types );
180
}
181
182
/**
183
 * Is the query for an existing attachment page?
184
 *
185
 * @since 2.0.0
186
 *
187
 * @global WP_Query $wp_query Global WP_Query instance.
188
 *
189
 * @param int|string|array|object $attachment Attachment ID, title, slug, or array of such.
190
 * @return bool
191
 */
192 View Code Duplication
function is_attachment( $attachment = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
193
	global $wp_query;
194
195
	if ( ! isset( $wp_query ) ) {
196
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
197
		return false;
198
	}
199
200
	return $wp_query->is_attachment( $attachment );
201
}
202
203
/**
204
 * Is the query for an existing author archive page?
205
 *
206
 * If the $author parameter is specified, this function will additionally
207
 * check if the query is for one of the authors specified.
208
 *
209
 * @since 1.5.0
210
 *
211
 * @global WP_Query $wp_query Global WP_Query instance.
212
 *
213
 * @param mixed $author Optional. User ID, nickname, nicename, or array of User IDs, nicknames, and nicenames
214
 * @return bool
215
 */
216 View Code Duplication
function is_author( $author = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
217
	global $wp_query;
218
219
	if ( ! isset( $wp_query ) ) {
220
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
221
		return false;
222
	}
223
224
	return $wp_query->is_author( $author );
225
}
226
227
/**
228
 * Is the query for an existing category archive page?
229
 *
230
 * If the $category parameter is specified, this function will additionally
231
 * check if the query is for one of the categories specified.
232
 *
233
 * @since 1.5.0
234
 *
235
 * @global WP_Query $wp_query Global WP_Query instance.
236
 *
237
 * @param mixed $category Optional. Category ID, name, slug, or array of Category IDs, names, and slugs.
238
 * @return bool
239
 */
240 View Code Duplication
function is_category( $category = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
241
	global $wp_query;
242
243
	if ( ! isset( $wp_query ) ) {
244
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
245
		return false;
246
	}
247
248
	return $wp_query->is_category( $category );
249
}
250
251
/**
252
 * Is the query for an existing tag archive page?
253
 *
254
 * If the $tag parameter is specified, this function will additionally
255
 * check if the query is for one of the tags specified.
256
 *
257
 * @since 2.3.0
258
 *
259
 * @global WP_Query $wp_query Global WP_Query instance.
260
 *
261
 * @param mixed $tag Optional. Tag ID, name, slug, or array of Tag IDs, names, and slugs.
262
 * @return bool
263
 */
264 View Code Duplication
function is_tag( $tag = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
265
	global $wp_query;
266
267
	if ( ! isset( $wp_query ) ) {
268
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
269
		return false;
270
	}
271
272
	return $wp_query->is_tag( $tag );
273
}
274
275
/**
276
 * Is the query for an existing custom taxonomy archive page?
277
 *
278
 * If the $taxonomy parameter is specified, this function will additionally
279
 * check if the query is for that specific $taxonomy.
280
 *
281
 * If the $term parameter is specified in addition to the $taxonomy parameter,
282
 * this function will additionally check if the query is for one of the terms
283
 * specified.
284
 *
285
 * @since 2.5.0
286
 *
287
 * @global WP_Query $wp_query Global WP_Query instance.
288
 *
289
 * @param string|array     $taxonomy Optional. Taxonomy slug or slugs.
290
 * @param int|string|array $term     Optional. Term ID, name, slug or array of Term IDs, names, and slugs.
291
 * @return bool True for custom taxonomy archive pages, false for built-in taxonomies (category and tag archives).
292
 */
293 View Code Duplication
function is_tax( $taxonomy = '', $term = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
294
	global $wp_query;
295
296
	if ( ! isset( $wp_query ) ) {
297
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
298
		return false;
299
	}
300
301
	return $wp_query->is_tax( $taxonomy, $term );
302
}
303
304
/**
305
 * Is the query for an existing date archive?
306
 *
307
 * @since 1.5.0
308
 *
309
 * @global WP_Query $wp_query Global WP_Query instance.
310
 *
311
 * @return bool
312
 */
313 View Code Duplication
function is_date() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
314
	global $wp_query;
315
316
	if ( ! isset( $wp_query ) ) {
317
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
318
		return false;
319
	}
320
321
	return $wp_query->is_date();
322
}
323
324
/**
325
 * Is the query for an existing day archive?
326
 *
327
 * @since 1.5.0
328
 *
329
 * @global WP_Query $wp_query Global WP_Query instance.
330
 *
331
 * @return bool
332
 */
333 View Code Duplication
function is_day() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
334
	global $wp_query;
335
336
	if ( ! isset( $wp_query ) ) {
337
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
338
		return false;
339
	}
340
341
	return $wp_query->is_day();
342
}
343
344
/**
345
 * Is the query for a feed?
346
 *
347
 * @since 1.5.0
348
 *
349
 * @global WP_Query $wp_query Global WP_Query instance.
350
 *
351
 * @param string|array $feeds Optional feed types to check.
352
 * @return bool
353
 */
354 View Code Duplication
function is_feed( $feeds = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
355
	global $wp_query;
356
357
	if ( ! isset( $wp_query ) ) {
358
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
359
		return false;
360
	}
361
362
	return $wp_query->is_feed( $feeds );
363
}
364
365
/**
366
 * Is the query for a comments feed?
367
 *
368
 * @since 3.0.0
369
 *
370
 * @global WP_Query $wp_query Global WP_Query instance.
371
 *
372
 * @return bool
373
 */
374 View Code Duplication
function is_comment_feed() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
375
	global $wp_query;
376
377
	if ( ! isset( $wp_query ) ) {
378
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
379
		return false;
380
	}
381
382
	return $wp_query->is_comment_feed();
383
}
384
385
/**
386
 * Is the query for the front page of the site?
387
 *
388
 * This is for what is displayed at your site's main URL.
389
 *
390
 * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_on_front'.
391
 *
392
 * If you set a static page for the front page of your site, this function will return
393
 * true when viewing that page.
394
 *
395
 * Otherwise the same as @see is_home()
396
 *
397
 * @since 2.5.0
398
 *
399
 * @global WP_Query $wp_query Global WP_Query instance.
400
 *
401
 * @return bool True, if front of site.
402
 */
403 View Code Duplication
function is_front_page() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
404
	global $wp_query;
405
406
	if ( ! isset( $wp_query ) ) {
407
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
408
		return false;
409
	}
410
411
	return $wp_query->is_front_page();
412
}
413
414
/**
415
 * Determines if the query is for the blog homepage.
416
 *
417
 * The blog homepage is the page that shows the time-based blog content of the site.
418
 *
419
 * is_home() is dependent on the site's "Front page displays" Reading Settings 'show_on_front'
420
 * and 'page_for_posts'.
421
 *
422
 * If a static page is set for the front page of the site, this function will return true only
423
 * on the page you set as the "Posts page".
424
 *
425
 * @since 1.5.0
426
 *
427
 * @see is_front_page()
428
 * @global WP_Query $wp_query Global WP_Query instance.
429
 *
430
 * @return bool True if blog view homepage, otherwise false.
431
 */
432 View Code Duplication
function is_home() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
433
	global $wp_query;
434
435
	if ( ! isset( $wp_query ) ) {
436
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
437
		return false;
438
	}
439
440
	return $wp_query->is_home();
441
}
442
443
/**
444
 * Is the query for an existing month archive?
445
 *
446
 * @since 1.5.0
447
 *
448
 * @global WP_Query $wp_query Global WP_Query instance.
449
 *
450
 * @return bool
451
 */
452 View Code Duplication
function is_month() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
453
	global $wp_query;
454
455
	if ( ! isset( $wp_query ) ) {
456
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
457
		return false;
458
	}
459
460
	return $wp_query->is_month();
461
}
462
463
/**
464
 * Is the query for an existing single page?
465
 *
466
 * If the $page parameter is specified, this function will additionally
467
 * check if the query is for one of the pages specified.
468
 *
469
 * @see is_single()
470
 * @see is_singular()
471
 *
472
 * @since 1.5.0
473
 *
474
 * @global WP_Query $wp_query Global WP_Query instance.
475
 *
476
 * @param int|string|array $page Optional. Page ID, title, slug, or array of such. Default empty.
477
 * @return bool Whether the query is for an existing single page.
478
 */
479 View Code Duplication
function is_page( $page = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
480
	global $wp_query;
481
482
	if ( ! isset( $wp_query ) ) {
483
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
484
		return false;
485
	}
486
487
	return $wp_query->is_page( $page );
488
}
489
490
/**
491
 * Is the query for paged result and not for the first page?
492
 *
493
 * @since 1.5.0
494
 *
495
 * @global WP_Query $wp_query Global WP_Query instance.
496
 *
497
 * @return bool
498
 */
499 View Code Duplication
function is_paged() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
500
	global $wp_query;
501
502
	if ( ! isset( $wp_query ) ) {
503
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
504
		return false;
505
	}
506
507
	return $wp_query->is_paged();
508
}
509
510
/**
511
 * Is the query for a post or page preview?
512
 *
513
 * @since 2.0.0
514
 *
515
 * @global WP_Query $wp_query Global WP_Query instance.
516
 *
517
 * @return bool
518
 */
519 View Code Duplication
function is_preview() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
520
	global $wp_query;
521
522
	if ( ! isset( $wp_query ) ) {
523
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
524
		return false;
525
	}
526
527
	return $wp_query->is_preview();
528
}
529
530
/**
531
 * Is the query for the robots file?
532
 *
533
 * @since 2.1.0
534
 *
535
 * @global WP_Query $wp_query Global WP_Query instance.
536
 *
537
 * @return bool
538
 */
539 View Code Duplication
function is_robots() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
540
	global $wp_query;
541
542
	if ( ! isset( $wp_query ) ) {
543
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
544
		return false;
545
	}
546
547
	return $wp_query->is_robots();
548
}
549
550
/**
551
 * Is the query for a search?
552
 *
553
 * @since 1.5.0
554
 *
555
 * @global WP_Query $wp_query Global WP_Query instance.
556
 *
557
 * @return bool
558
 */
559 View Code Duplication
function is_search() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
560
	global $wp_query;
561
562
	if ( ! isset( $wp_query ) ) {
563
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
564
		return false;
565
	}
566
567
	return $wp_query->is_search();
568
}
569
570
/**
571
 * Is the query for an existing single post?
572
 *
573
 * Works for any post type, except attachments and pages
574
 *
575
 * If the $post parameter is specified, this function will additionally
576
 * check if the query is for one of the Posts specified.
577
 *
578
 * @see is_page()
579
 * @see is_singular()
580
 *
581
 * @since 1.5.0
582
 *
583
 * @global WP_Query $wp_query Global WP_Query instance.
584
 *
585
 * @param int|string|array $post Optional. Post ID, title, slug, or array of such. Default empty.
586
 * @return bool Whether the query is for an existing single post.
587
 */
588 View Code Duplication
function is_single( $post = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
589
	global $wp_query;
590
591
	if ( ! isset( $wp_query ) ) {
592
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
593
		return false;
594
	}
595
596
	return $wp_query->is_single( $post );
597
}
598
599
/**
600
 * Is the query for an existing single post of any post type (post, attachment, page, ... )?
601
 *
602
 * If the $post_types parameter is specified, this function will additionally
603
 * check if the query is for one of the Posts Types specified.
604
 *
605
 * @see is_page()
606
 * @see is_single()
607
 *
608
 * @since 1.5.0
609
 *
610
 * @global WP_Query $wp_query Global WP_Query instance.
611
 *
612
 * @param string|array $post_types Optional. Post type or array of post types. Default empty.
613
 * @return bool Whether the query is for an existing single post of any of the given post types.
614
 */
615 View Code Duplication
function is_singular( $post_types = '' ) {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
616
	global $wp_query;
617
618
	if ( ! isset( $wp_query ) ) {
619
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
620
		return false;
621
	}
622
623
	return $wp_query->is_singular( $post_types );
624
}
625
626
/**
627
 * Is the query for a specific time?
628
 *
629
 * @since 1.5.0
630
 *
631
 * @global WP_Query $wp_query Global WP_Query instance.
632
 *
633
 * @return bool
634
 */
635 View Code Duplication
function is_time() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
636
	global $wp_query;
637
638
	if ( ! isset( $wp_query ) ) {
639
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
640
		return false;
641
	}
642
643
	return $wp_query->is_time();
644
}
645
646
/**
647
 * Is the query for a trackback endpoint call?
648
 *
649
 * @since 1.5.0
650
 *
651
 * @global WP_Query $wp_query Global WP_Query instance.
652
 *
653
 * @return bool
654
 */
655 View Code Duplication
function is_trackback() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
656
	global $wp_query;
657
658
	if ( ! isset( $wp_query ) ) {
659
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
660
		return false;
661
	}
662
663
	return $wp_query->is_trackback();
664
}
665
666
/**
667
 * Is the query for an existing year archive?
668
 *
669
 * @since 1.5.0
670
 *
671
 * @global WP_Query $wp_query Global WP_Query instance.
672
 *
673
 * @return bool
674
 */
675 View Code Duplication
function is_year() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
676
	global $wp_query;
677
678
	if ( ! isset( $wp_query ) ) {
679
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
680
		return false;
681
	}
682
683
	return $wp_query->is_year();
684
}
685
686
/**
687
 * Is the query a 404 (returns no results)?
688
 *
689
 * @since 1.5.0
690
 *
691
 * @global WP_Query $wp_query Global WP_Query instance.
692
 *
693
 * @return bool
694
 */
695 View Code Duplication
function is_404() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
696
	global $wp_query;
697
698
	if ( ! isset( $wp_query ) ) {
699
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
700
		return false;
701
	}
702
703
	return $wp_query->is_404();
704
}
705
706
/**
707
 * Is the query for an embedded post?
708
 *
709
 * @since 4.4.0
710
 *
711
 * @global WP_Query $wp_query Global WP_Query instance.
712
 *
713
 * @return bool Whether we're in an embedded post or not.
714
 */
715 View Code Duplication
function is_embed() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
716
	global $wp_query;
717
718
	if ( ! isset( $wp_query ) ) {
719
		_doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1.0' );
720
		return false;
721
	}
722
723
	return $wp_query->is_embed();
724
}
725
726
/**
727
 * Is the query the main query?
728
 *
729
 * @since 3.3.0
730
 *
731
 * @global WP_Query $wp_query Global WP_Query instance.
732
 *
733
 * @return bool
734
 */
735
function is_main_query() {
736
	if ( 'pre_get_posts' === current_filter() ) {
737
		$message = sprintf(
738
			/* translators: 1: pre_get_posts 2: WP_Query->is_main_query() 3: is_main_query() 4: link to codex is_main_query() page. */
739
			__( 'In %1$s, use the %2$s method, not the %3$s function. See %4$s.' ),
740
			'<code>pre_get_posts</code>',
741
			'<code>WP_Query->is_main_query()</code>',
742
			'<code>is_main_query()</code>',
743
			__( 'https://codex.wordpress.org/Function_Reference/is_main_query' )
744
		);
745
		_doing_it_wrong( __FUNCTION__, $message, '3.7.0' );
746
	}
747
748
	global $wp_query;
749
	return $wp_query->is_main_query();
750
}
751
752
/*
753
 * The Loop. Post loop control.
754
 */
755
756
/**
757
 * Whether current WordPress query has results to loop over.
758
 *
759
 * @since 1.5.0
760
 *
761
 * @global WP_Query $wp_query Global WP_Query instance.
762
 *
763
 * @return bool
764
 */
765
function have_posts() {
766
	global $wp_query;
767
	return $wp_query->have_posts();
768
}
769
770
/**
771
 * Whether the caller is in the Loop.
772
 *
773
 * @since 2.0.0
774
 *
775
 * @global WP_Query $wp_query Global WP_Query instance.
776
 *
777
 * @return bool True if caller is within loop, false if loop hasn't started or ended.
778
 */
779
function in_the_loop() {
780
	global $wp_query;
781
	return $wp_query->in_the_loop;
782
}
783
784
/**
785
 * Rewind the loop posts.
786
 *
787
 * @since 1.5.0
788
 *
789
 * @global WP_Query $wp_query Global WP_Query instance.
790
 */
791
function rewind_posts() {
792
	global $wp_query;
793
	$wp_query->rewind_posts();
794
}
795
796
/**
797
 * Iterate the post index in the loop.
798
 *
799
 * @since 1.5.0
800
 *
801
 * @global WP_Query $wp_query Global WP_Query instance.
802
 */
803
function the_post() {
804
	global $wp_query;
805
	$wp_query->the_post();
806
}
807
808
/*
809
 * Comments loop.
810
 */
811
812
/**
813
 * Whether there are comments to loop over.
814
 *
815
 * @since 2.2.0
816
 *
817
 * @global WP_Query $wp_query Global WP_Query instance.
818
 *
819
 * @return bool
820
 */
821
function have_comments() {
822
	global $wp_query;
823
	return $wp_query->have_comments();
824
}
825
826
/**
827
 * Iterate comment index in the comment loop.
828
 *
829
 * @since 2.2.0
830
 *
831
 * @global WP_Query $wp_query Global WP_Query instance.
832
 *
833
 * @return object
834
 */
835
function the_comment() {
836
	global $wp_query;
837
	return $wp_query->the_comment();
838
}
839
840
/*
841
 * WP_Query
842
 */
843
844
/**
845
 * The WordPress Query class.
846
 *
847
 * @link https://codex.wordpress.org/Function_Reference/WP_Query Codex page.
848
 *
849
 * @since 1.5.0
850
 * @since 4.5.0 Removed the `$comments_popup` property.
851
 */
852
class WP_Query {
853
854
	/**
855
	 * Query vars set by the user
856
	 *
857
	 * @since 1.5.0
858
	 * @access public
859
	 * @var array
860
	 */
861
	public $query;
862
863
	/**
864
	 * Query vars, after parsing
865
	 *
866
	 * @since 1.5.0
867
	 * @access public
868
	 * @var array
869
	 */
870
	public $query_vars = array();
871
872
	/**
873
	 * Taxonomy query, as passed to get_tax_sql()
874
	 *
875
	 * @since 3.1.0
876
	 * @access public
877
	 * @var object WP_Tax_Query
878
	 */
879
	public $tax_query;
880
881
	/**
882
	 * Metadata query container
883
	 *
884
	 * @since 3.2.0
885
	 * @access public
886
	 * @var object WP_Meta_Query
887
	 */
888
	public $meta_query = false;
889
890
	/**
891
	 * Date query container
892
	 *
893
	 * @since 3.7.0
894
	 * @access public
895
	 * @var object WP_Date_Query
896
	 */
897
	public $date_query = false;
898
899
	/**
900
	 * Holds the data for a single object that is queried.
901
	 *
902
	 * Holds the contents of a post, page, category, attachment.
903
	 *
904
	 * @since 1.5.0
905
	 * @access public
906
	 * @var object|array
907
	 */
908
	public $queried_object;
909
910
	/**
911
	 * The ID of the queried object.
912
	 *
913
	 * @since 1.5.0
914
	 * @access public
915
	 * @var int
916
	 */
917
	public $queried_object_id;
918
919
	/**
920
	 * Get post database query.
921
	 *
922
	 * @since 2.0.1
923
	 * @access public
924
	 * @var string
925
	 */
926
	public $request;
927
928
	/**
929
	 * List of posts.
930
	 *
931
	 * @since 1.5.0
932
	 * @access public
933
	 * @var array
934
	 */
935
	public $posts;
936
937
	/**
938
	 * The amount of posts for the current query.
939
	 *
940
	 * @since 1.5.0
941
	 * @access public
942
	 * @var int
943
	 */
944
	public $post_count = 0;
945
946
	/**
947
	 * Index of the current item in the loop.
948
	 *
949
	 * @since 1.5.0
950
	 * @access public
951
	 * @var int
952
	 */
953
	public $current_post = -1;
954
955
	/**
956
	 * Whether the loop has started and the caller is in the loop.
957
	 *
958
	 * @since 2.0.0
959
	 * @access public
960
	 * @var bool
961
	 */
962
	public $in_the_loop = false;
963
964
	/**
965
	 * The current post.
966
	 *
967
	 * @since 1.5.0
968
	 * @access public
969
	 * @var WP_Post
970
	 */
971
	public $post;
972
973
	/**
974
	 * The list of comments for current post.
975
	 *
976
	 * @since 2.2.0
977
	 * @access public
978
	 * @var array
979
	 */
980
	public $comments;
981
982
	/**
983
	 * The amount of comments for the posts.
984
	 *
985
	 * @since 2.2.0
986
	 * @access public
987
	 * @var int
988
	 */
989
	public $comment_count = 0;
990
991
	/**
992
	 * The index of the comment in the comment loop.
993
	 *
994
	 * @since 2.2.0
995
	 * @access public
996
	 * @var int
997
	 */
998
	public $current_comment = -1;
999
1000
	/**
1001
	 * Current comment ID.
1002
	 *
1003
	 * @since 2.2.0
1004
	 * @access public
1005
	 * @var int
1006
	 */
1007
	public $comment;
1008
1009
	/**
1010
	 * The amount of found posts for the current query.
1011
	 *
1012
	 * If limit clause was not used, equals $post_count.
1013
	 *
1014
	 * @since 2.1.0
1015
	 * @access public
1016
	 * @var int
1017
	 */
1018
	public $found_posts = 0;
1019
1020
	/**
1021
	 * The amount of pages.
1022
	 *
1023
	 * @since 2.1.0
1024
	 * @access public
1025
	 * @var int
1026
	 */
1027
	public $max_num_pages = 0;
1028
1029
	/**
1030
	 * The amount of comment pages.
1031
	 *
1032
	 * @since 2.7.0
1033
	 * @access public
1034
	 * @var int
1035
	 */
1036
	public $max_num_comment_pages = 0;
1037
1038
	/**
1039
	 * Set if query is single post.
1040
	 *
1041
	 * @since 1.5.0
1042
	 * @access public
1043
	 * @var bool
1044
	 */
1045
	public $is_single = false;
1046
1047
	/**
1048
	 * Set if query is preview of blog.
1049
	 *
1050
	 * @since 2.0.0
1051
	 * @access public
1052
	 * @var bool
1053
	 */
1054
	public $is_preview = false;
1055
1056
	/**
1057
	 * Set if query returns a page.
1058
	 *
1059
	 * @since 1.5.0
1060
	 * @access public
1061
	 * @var bool
1062
	 */
1063
	public $is_page = false;
1064
1065
	/**
1066
	 * Set if query is an archive list.
1067
	 *
1068
	 * @since 1.5.0
1069
	 * @access public
1070
	 * @var bool
1071
	 */
1072
	public $is_archive = false;
1073
1074
	/**
1075
	 * Set if query is part of a date.
1076
	 *
1077
	 * @since 1.5.0
1078
	 * @access public
1079
	 * @var bool
1080
	 */
1081
	public $is_date = false;
1082
1083
	/**
1084
	 * Set if query contains a year.
1085
	 *
1086
	 * @since 1.5.0
1087
	 * @access public
1088
	 * @var bool
1089
	 */
1090
	public $is_year = false;
1091
1092
	/**
1093
	 * Set if query contains a month.
1094
	 *
1095
	 * @since 1.5.0
1096
	 * @access public
1097
	 * @var bool
1098
	 */
1099
	public $is_month = false;
1100
1101
	/**
1102
	 * Set if query contains a day.
1103
	 *
1104
	 * @since 1.5.0
1105
	 * @access public
1106
	 * @var bool
1107
	 */
1108
	public $is_day = false;
1109
1110
	/**
1111
	 * Set if query contains time.
1112
	 *
1113
	 * @since 1.5.0
1114
	 * @access public
1115
	 * @var bool
1116
	 */
1117
	public $is_time = false;
1118
1119
	/**
1120
	 * Set if query contains an author.
1121
	 *
1122
	 * @since 1.5.0
1123
	 * @access public
1124
	 * @var bool
1125
	 */
1126
	public $is_author = false;
1127
1128
	/**
1129
	 * Set if query contains category.
1130
	 *
1131
	 * @since 1.5.0
1132
	 * @access public
1133
	 * @var bool
1134
	 */
1135
	public $is_category = false;
1136
1137
	/**
1138
	 * Set if query contains tag.
1139
	 *
1140
	 * @since 2.3.0
1141
	 * @access public
1142
	 * @var bool
1143
	 */
1144
	public $is_tag = false;
1145
1146
	/**
1147
	 * Set if query contains taxonomy.
1148
	 *
1149
	 * @since 2.5.0
1150
	 * @access public
1151
	 * @var bool
1152
	 */
1153
	public $is_tax = false;
1154
1155
	/**
1156
	 * Set if query was part of a search result.
1157
	 *
1158
	 * @since 1.5.0
1159
	 * @access public
1160
	 * @var bool
1161
	 */
1162
	public $is_search = false;
1163
1164
	/**
1165
	 * Set if query is feed display.
1166
	 *
1167
	 * @since 1.5.0
1168
	 * @access public
1169
	 * @var bool
1170
	 */
1171
	public $is_feed = false;
1172
1173
	/**
1174
	 * Set if query is comment feed display.
1175
	 *
1176
	 * @since 2.2.0
1177
	 * @access public
1178
	 * @var bool
1179
	 */
1180
	public $is_comment_feed = false;
1181
1182
	/**
1183
	 * Set if query is trackback.
1184
	 *
1185
	 * @since 1.5.0
1186
	 * @access public
1187
	 * @var bool
1188
	 */
1189
	public $is_trackback = false;
1190
1191
	/**
1192
	 * Set if query is blog homepage.
1193
	 *
1194
	 * @since 1.5.0
1195
	 * @access public
1196
	 * @var bool
1197
	 */
1198
	public $is_home = false;
1199
1200
	/**
1201
	 * Set if query couldn't found anything.
1202
	 *
1203
	 * @since 1.5.0
1204
	 * @access public
1205
	 * @var bool
1206
	 */
1207
	public $is_404 = false;
1208
1209
	/**
1210
	 * Set if query is embed.
1211
	 *
1212
	 * @since 4.4.0
1213
	 * @access public
1214
	 * @var bool
1215
	 */
1216
	public $is_embed = false;
1217
1218
	/**
1219
	 * Set if query is paged
1220
	 *
1221
	 * @since 1.5.0
1222
	 * @access public
1223
	 * @var bool
1224
	 */
1225
	public $is_paged = false;
1226
1227
	/**
1228
	 * Set if query is part of administration page.
1229
	 *
1230
	 * @since 1.5.0
1231
	 * @access public
1232
	 * @var bool
1233
	 */
1234
	public $is_admin = false;
1235
1236
	/**
1237
	 * Set if query is an attachment.
1238
	 *
1239
	 * @since 2.0.0
1240
	 * @access public
1241
	 * @var bool
1242
	 */
1243
	public $is_attachment = false;
1244
1245
	/**
1246
	 * Set if is single, is a page, or is an attachment.
1247
	 *
1248
	 * @since 2.1.0
1249
	 * @access public
1250
	 * @var bool
1251
	 */
1252
	public $is_singular = false;
1253
1254
	/**
1255
	 * Set if query is for robots.
1256
	 *
1257
	 * @since 2.1.0
1258
	 * @access public
1259
	 * @var bool
1260
	 */
1261
	public $is_robots = false;
1262
1263
	/**
1264
	 * Set if query contains posts.
1265
	 *
1266
	 * Basically, the homepage if the option isn't set for the static homepage.
1267
	 *
1268
	 * @since 2.1.0
1269
	 * @access public
1270
	 * @var bool
1271
	 */
1272
	public $is_posts_page = false;
1273
1274
	/**
1275
	 * Set if query is for a post type archive.
1276
	 *
1277
	 * @since 3.1.0
1278
	 * @access public
1279
	 * @var bool
1280
	 */
1281
	public $is_post_type_archive = false;
1282
1283
	/**
1284
	 * Stores the ->query_vars state like md5(serialize( $this->query_vars ) ) so we know
1285
	 * whether we have to re-parse because something has changed
1286
	 *
1287
	 * @since 3.1.0
1288
	 * @access private
1289
	 * @var bool|string
1290
	 */
1291
	private $query_vars_hash = false;
1292
1293
	/**
1294
	 * Whether query vars have changed since the initial parse_query() call. Used to catch modifications to query vars made
1295
	 * via pre_get_posts hooks.
1296
	 *
1297
	 * @since 3.1.1
1298
	 * @access private
1299
	 */
1300
	private $query_vars_changed = true;
1301
1302
	/**
1303
	 * Set if post thumbnails are cached
1304
	 *
1305
	 * @since 3.2.0
1306
	 * @access public
1307
	 * @var bool
1308
	 */
1309
	 public $thumbnails_cached = false;
1310
1311
	/**
1312
	 * Cached list of search stopwords.
1313
	 *
1314
	 * @since 3.7.0
1315
	 * @var array
1316
	 */
1317
	private $stopwords;
1318
1319
	private $compat_fields = array( 'query_vars_hash', 'query_vars_changed' );
1320
1321
	private $compat_methods = array( 'init_query_flags', 'parse_tax_query' );
1322
1323
	/**
1324
	 * @since 4.7.0
1325
	 * @access protected
1326
	 * @var wpdb
1327
	 */
1328
	protected $db;
1329
1330
	/**
1331
	 * Resets query flags to false.
1332
	 *
1333
	 * The query flags are what page info WordPress was able to figure out.
1334
	 *
1335
	 * @since 2.0.0
1336
	 * @access private
1337
	 */
1338
	private function init_query_flags() {
1339
		$this->is_single = false;
1340
		$this->is_preview = false;
1341
		$this->is_page = false;
1342
		$this->is_archive = false;
1343
		$this->is_date = false;
1344
		$this->is_year = false;
1345
		$this->is_month = false;
1346
		$this->is_day = false;
1347
		$this->is_time = false;
1348
		$this->is_author = false;
1349
		$this->is_category = false;
1350
		$this->is_tag = false;
1351
		$this->is_tax = false;
1352
		$this->is_search = false;
1353
		$this->is_feed = false;
1354
		$this->is_comment_feed = false;
1355
		$this->is_trackback = false;
1356
		$this->is_home = false;
1357
		$this->is_404 = false;
1358
		$this->is_paged = false;
1359
		$this->is_admin = false;
1360
		$this->is_attachment = false;
1361
		$this->is_singular = false;
1362
		$this->is_robots = false;
1363
		$this->is_posts_page = false;
1364
		$this->is_post_type_archive = false;
1365
	}
1366
1367
	/**
1368
	 * Initiates object properties and sets default values.
1369
	 *
1370
	 * @since 1.5.0
1371
	 * @access public
1372
	 */
1373
	public function init() {
1374
		unset($this->posts);
1375
		unset($this->query);
1376
		$this->query_vars = array();
1377
		unset($this->queried_object);
1378
		unset($this->queried_object_id);
1379
		$this->post_count = 0;
1380
		$this->current_post = -1;
1381
		$this->in_the_loop = false;
1382
		unset( $this->request );
1383
		unset( $this->post );
1384
		unset( $this->comments );
1385
		unset( $this->comment );
1386
		$this->comment_count = 0;
1387
		$this->current_comment = -1;
1388
		$this->found_posts = 0;
1389
		$this->max_num_pages = 0;
1390
		$this->max_num_comment_pages = 0;
1391
1392
		$this->init_query_flags();
1393
	}
1394
1395
	/**
1396
	 * Reparse the query vars.
1397
	 *
1398
	 * @since 1.5.0
1399
	 * @access public
1400
	 */
1401
	public function parse_query_vars() {
1402
		$this->parse_query();
1403
	}
1404
1405
	/**
1406
	 * Fills in the query variables, which do not exist within the parameter.
1407
	 *
1408
	 * @since 2.1.0
1409
	 * @since 4.4.0 Removed the `comments_popup` public query variable.
1410
	 * @access public
1411
	 *
1412
	 * @param array $array Defined query variables.
1413
	 * @return array Complete query variables with undefined ones filled in empty.
1414
	 */
1415
	public function fill_query_vars($array) {
1416
		$keys = array(
1417
			'error'
1418
			, 'm'
1419
			, 'p'
1420
			, 'post_parent'
1421
			, 'subpost'
1422
			, 'subpost_id'
1423
			, 'attachment'
1424
			, 'attachment_id'
1425
			, 'name'
1426
			, 'static'
1427
			, 'pagename'
1428
			, 'page_id'
1429
			, 'second'
1430
			, 'minute'
1431
			, 'hour'
1432
			, 'day'
1433
			, 'monthnum'
1434
			, 'year'
1435
			, 'w'
1436
			, 'category_name'
1437
			, 'tag'
1438
			, 'cat'
1439
			, 'tag_id'
1440
			, 'author'
1441
			, 'author_name'
1442
			, 'feed'
1443
			, 'tb'
1444
			, 'paged'
1445
			, 'meta_key'
1446
			, 'meta_value'
1447
			, 'preview'
1448
			, 's'
1449
			, 'sentence'
1450
			, 'title'
1451
			, 'fields'
1452
			, 'menu_order'
1453
			, 'embed'
1454
		);
1455
1456
		foreach ( $keys as $key ) {
1457
			if ( !isset($array[$key]) )
1458
				$array[$key] = '';
1459
		}
1460
1461
		$array_keys = array( 'category__in', 'category__not_in', 'category__and', 'post__in', 'post__not_in', 'post_name__in',
1462
			'tag__in', 'tag__not_in', 'tag__and', 'tag_slug__in', 'tag_slug__and', 'post_parent__in', 'post_parent__not_in',
1463
			'author__in', 'author__not_in' );
1464
1465
		foreach ( $array_keys as $key ) {
1466
			if ( !isset($array[$key]) )
1467
				$array[$key] = array();
1468
		}
1469
		return $array;
1470
	}
1471
1472
	/**
1473
	 * Parse a query string and set query type booleans.
1474
	 *
1475
	 * @since 1.5.0
1476
	 * @since 4.2.0 Introduced the ability to order by specific clauses of a `$meta_query`, by passing the clause's
1477
	 *              array key to `$orderby`.
1478
	 * @since 4.4.0 Introduced `$post_name__in` and `$title` parameters. `$s` was updated to support excluded
1479
	 *              search terms, by prepending a hyphen.
1480
	 * @since 4.5.0 Removed the `$comments_popup` parameter.
1481
	 *              Introduced the `$comment_status` and `$ping_status` parameters.
1482
	 *              Introduced `RAND(x)` syntax for `$orderby`, which allows an integer seed value to random sorts.
1483
	 * @since 4.6.0 Added 'post_name__in' support for `$orderby`. Introduced the `$lazy_load_term_meta` argument.
1484
	 * @access public
1485
	 *
1486
	 * @param string|array $query {
1487
	 *     Optional. Array or string of Query parameters.
1488
	 *
1489
	 *     @type int          $attachment_id           Attachment post ID. Used for 'attachment' post_type.
1490
	 *     @type int|string   $author                  Author ID, or comma-separated list of IDs.
1491
	 *     @type string       $author_name             User 'user_nicename'.
1492
	 *     @type array        $author__in              An array of author IDs to query from.
1493
	 *     @type array        $author__not_in          An array of author IDs not to query from.
1494
	 *     @type bool         $cache_results           Whether to cache post information. Default true.
1495
	 *     @type int|string   $cat                     Category ID or comma-separated list of IDs (this or any children).
1496
	 *     @type array        $category__and           An array of category IDs (AND in).
1497
	 *     @type array        $category__in            An array of category IDs (OR in, no children).
1498
	 *     @type array        $category__not_in        An array of category IDs (NOT in).
1499
	 *     @type string       $category_name           Use category slug (not name, this or any children).
1500
	 *     @type string       $comment_status          Comment status.
1501
	 *     @type int          $comments_per_page       The number of comments to return per page.
1502
	 *                                                 Default 'comments_per_page' option.
1503
	 *     @type array        $date_query              An associative array of WP_Date_Query arguments.
1504
	 *                                                 See WP_Date_Query::__construct().
1505
	 *     @type int          $day                     Day of the month. Default empty. Accepts numbers 1-31.
1506
	 *     @type bool         $exact                   Whether to search by exact keyword. Default false.
1507
	 *     @type string|array $fields                  Which fields to return. Single field or all fields (string),
1508
	 *                                                 or array of fields. 'id=>parent' uses 'id' and 'post_parent'.
1509
	 *                                                 Default all fields. Accepts 'ids', 'id=>parent'.
1510
	 *     @type int          $hour                    Hour of the day. Default empty. Accepts numbers 0-23.
1511
	 *     @type int|bool     $ignore_sticky_posts     Whether to ignore sticky posts or not. Setting this to false
1512
	 *                                                 excludes stickies from 'post__in'. Accepts 1|true, 0|false.
1513
	 *                                                 Default 0|false.
1514
	 *     @type int          $m                       Combination YearMonth. Accepts any four-digit year and month
1515
	 *                                                 numbers 1-12. Default empty.
1516
	 *     @type string       $meta_compare            Comparison operator to test the 'meta_value'.
1517
	 *     @type string       $meta_key                Custom field key.
1518
	 *     @type array        $meta_query              An associative array of WP_Meta_Query arguments. See WP_Meta_Query.
1519
	 *     @type string       $meta_value              Custom field value.
1520
	 *     @type int          $meta_value_num          Custom field value number.
1521
	 *     @type int          $menu_order              The menu order of the posts.
1522
	 *     @type int          $monthnum                The two-digit month. Default empty. Accepts numbers 1-12.
1523
	 *     @type string       $name                    Post slug.
1524
	 *     @type bool         $nopaging                Show all posts (true) or paginate (false). Default false.
1525
	 *     @type bool         $no_found_rows           Whether to skip counting the total rows found. Enabling can improve
1526
	 *                                                 performance. Default false.
1527
	 *     @type int          $offset                  The number of posts to offset before retrieval.
1528
	 *     @type string       $order                   Designates ascending or descending order of posts. Default 'DESC'.
1529
	 *                                                 Accepts 'ASC', 'DESC'.
1530
	 *     @type string|array $orderby                 Sort retrieved posts by parameter. One or more options may be
1531
	 *                                                 passed. To use 'meta_value', or 'meta_value_num',
1532
	 *                                                 'meta_key=keyname' must be also be defined. To sort by a
1533
	 *                                                 specific `$meta_query` clause, use that clause's array key.
1534
	 *                                                 Default 'date'. Accepts 'none', 'name', 'author', 'date',
1535
	 *                                                 'title', 'modified', 'menu_order', 'parent', 'ID', 'rand',
1536
	 *                                                 'RAND(x)' (where 'x' is an integer seed value),
1537
	 *                                                 'comment_count', 'meta_value', 'meta_value_num', 'post__in',
1538
	 *                                                 'post_name__in', 'post_parent__in', and the array keys
1539
	 *                                                 of `$meta_query`.
1540
	 *     @type int          $p                       Post ID.
1541
	 *     @type int          $page                    Show the number of posts that would show up on page X of a
1542
	 *                                                 static front page.
1543
	 *     @type int          $paged                   The number of the current page.
1544
	 *     @type int          $page_id                 Page ID.
1545
	 *     @type string       $pagename                Page slug.
1546
	 *     @type string       $perm                    Show posts if user has the appropriate capability.
1547
	 *     @type string       $ping_status             Ping status.
1548
	 *     @type array        $post__in                An array of post IDs to retrieve, sticky posts will be included
1549
	 *     @type string       $post_mime_type          The mime type of the post. Used for 'attachment' post_type.
1550
	 *     @type array        $post__not_in            An array of post IDs not to retrieve. Note: a string of comma-
1551
	 *                                                 separated IDs will NOT work.
1552
	 *     @type int          $post_parent             Page ID to retrieve child pages for. Use 0 to only retrieve
1553
	 *                                                 top-level pages.
1554
	 *     @type array        $post_parent__in         An array containing parent page IDs to query child pages from.
1555
	 *     @type array        $post_parent__not_in     An array containing parent page IDs not to query child pages from.
1556
	 *     @type string|array $post_type               A post type slug (string) or array of post type slugs.
1557
	 *                                                 Default 'any' if using 'tax_query'.
1558
	 *     @type string|array $post_status             A post status (string) or array of post statuses.
1559
	 *     @type int          $posts_per_page          The number of posts to query for. Use -1 to request all posts.
1560
	 *     @type int          $posts_per_archive_page  The number of posts to query for by archive page. Overrides
1561
	 *                                                 'posts_per_page' when is_archive(), or is_search() are true.
1562
	 *     @type array        $post_name__in           An array of post slugs that results must match.
1563
	 *     @type string       $s                       Search keyword(s). Prepending a term with a hyphen will
1564
	 *                                                 exclude posts matching that term. Eg, 'pillow -sofa' will
1565
	 *                                                 return posts containing 'pillow' but not 'sofa'.
1566
	 *     @type int          $second                  Second of the minute. Default empty. Accepts numbers 0-60.
1567
	 *     @type bool         $sentence                Whether to search by phrase. Default false.
1568
	 *     @type bool         $suppress_filters        Whether to suppress filters. Default false.
1569
	 *     @type string       $tag                     Tag slug. Comma-separated (either), Plus-separated (all).
1570
	 *     @type array        $tag__and                An array of tag ids (AND in).
1571
	 *     @type array        $tag__in                 An array of tag ids (OR in).
1572
	 *     @type array        $tag__not_in             An array of tag ids (NOT in).
1573
	 *     @type int          $tag_id                  Tag id or comma-separated list of IDs.
1574
	 *     @type array        $tag_slug__and           An array of tag slugs (AND in).
1575
	 *     @type array        $tag_slug__in            An array of tag slugs (OR in). unless 'ignore_sticky_posts' is
1576
	 *                                                 true. Note: a string of comma-separated IDs will NOT work.
1577
	 *     @type array        $tax_query               An associative array of WP_Tax_Query arguments.
1578
	 *                                                 See WP_Tax_Query->queries.
1579
	 *     @type string       $title                   Post title.
1580
	 *     @type bool         $update_post_meta_cache  Whether to update the post meta cache. Default true.
1581
	 *     @type bool         $update_post_term_cache  Whether to update the post term cache. Default true.
1582
	 *     @type bool         $lazy_load_term_meta     Whether to lazy-load term meta. Setting to false will
1583
	 *                                                 disable cache priming for term meta, so that each
1584
	 *                                                 get_term_meta() call will hit the database.
1585
	 *                                                 Defaults to the value of `$update_post_term_cache`.
1586
	 *     @type int          $w                       The week number of the year. Default empty. Accepts numbers 0-53.
1587
	 *     @type int          $year                    The four-digit year. Default empty. Accepts any four-digit year.
1588
	 * }
1589
	 */
1590
	public function parse_query( $query =  '' ) {
1591
		if ( ! empty( $query ) ) {
1592
			$this->init();
1593
			$this->query = $this->query_vars = wp_parse_args( $query );
1594
		} elseif ( ! isset( $this->query ) ) {
1595
			$this->query = $this->query_vars;
1596
		}
1597
1598
		$this->query_vars = $this->fill_query_vars($this->query_vars);
1599
		$qv = &$this->query_vars;
1600
		$this->query_vars_changed = true;
1601
1602
		if ( ! empty($qv['robots']) )
1603
			$this->is_robots = true;
1604
1605
		if ( ! is_scalar( $qv['p'] ) || $qv['p'] < 0 ) {
1606
			$qv['p'] = 0;
1607
			$qv['error'] = '404';
1608
		} else {
1609
			$qv['p'] = intval( $qv['p'] );
1610
		}
1611
1612
		$qv['page_id'] =  absint($qv['page_id']);
1613
		$qv['year'] = absint($qv['year']);
1614
		$qv['monthnum'] = absint($qv['monthnum']);
1615
		$qv['day'] = absint($qv['day']);
1616
		$qv['w'] = absint($qv['w']);
1617
		$qv['m'] = is_scalar( $qv['m'] ) ? preg_replace( '|[^0-9]|', '', $qv['m'] ) : '';
1618
		$qv['paged'] = absint($qv['paged']);
1619
		$qv['cat'] = preg_replace( '|[^0-9,-]|', '', $qv['cat'] ); // comma separated list of positive or negative integers
1620
		$qv['author'] = preg_replace( '|[^0-9,-]|', '', $qv['author'] ); // comma separated list of positive or negative integers
1621
		$qv['pagename'] = trim( $qv['pagename'] );
1622
		$qv['name'] = trim( $qv['name'] );
1623
		$qv['title'] = trim( $qv['title'] );
1624
		if ( '' !== $qv['hour'] ) $qv['hour'] = absint($qv['hour']);
1625
		if ( '' !== $qv['minute'] ) $qv['minute'] = absint($qv['minute']);
1626
		if ( '' !== $qv['second'] ) $qv['second'] = absint($qv['second']);
1627
		if ( '' !== $qv['menu_order'] ) $qv['menu_order'] = absint($qv['menu_order']);
1628
1629
		// Fairly insane upper bound for search string lengths.
1630
		if ( ! is_scalar( $qv['s'] ) || ( ! empty( $qv['s'] ) && strlen( $qv['s'] ) > 1600 ) ) {
1631
			$qv['s'] = '';
1632
		}
1633
1634
		// Compat. Map subpost to attachment.
1635
		if ( '' != $qv['subpost'] )
1636
			$qv['attachment'] = $qv['subpost'];
1637
		if ( '' != $qv['subpost_id'] )
1638
			$qv['attachment_id'] = $qv['subpost_id'];
1639
1640
		$qv['attachment_id'] = absint($qv['attachment_id']);
1641
1642
		if ( ('' != $qv['attachment']) || !empty($qv['attachment_id']) ) {
1643
			$this->is_single = true;
1644
			$this->is_attachment = true;
1645
		} elseif ( '' != $qv['name'] ) {
1646
			$this->is_single = true;
1647
		} elseif ( $qv['p'] ) {
1648
			$this->is_single = true;
1649
		} elseif ( ('' !== $qv['hour']) && ('' !== $qv['minute']) &&('' !== $qv['second']) && ('' != $qv['year']) && ('' != $qv['monthnum']) && ('' != $qv['day']) ) {
1650
			// If year, month, day, hour, minute, and second are set, a single
1651
			// post is being queried.
1652
			$this->is_single = true;
1653
		} elseif ( '' != $qv['static'] || '' != $qv['pagename'] || !empty($qv['page_id']) ) {
1654
			$this->is_page = true;
1655
			$this->is_single = false;
1656
		} else {
1657
			// Look for archive queries. Dates, categories, authors, search, post type archives.
1658
1659
			if ( isset( $this->query['s'] ) ) {
1660
				$this->is_search = true;
1661
			}
1662
1663
			if ( '' !== $qv['second'] ) {
1664
				$this->is_time = true;
1665
				$this->is_date = true;
1666
			}
1667
1668 View Code Duplication
			if ( '' !== $qv['minute'] ) {
1669
				$this->is_time = true;
1670
				$this->is_date = true;
1671
			}
1672
1673 View Code Duplication
			if ( '' !== $qv['hour'] ) {
1674
				$this->is_time = true;
1675
				$this->is_date = true;
1676
			}
1677
1678
			if ( $qv['day'] ) {
1679
				if ( ! $this->is_date ) {
1680
					$date = sprintf( '%04d-%02d-%02d', $qv['year'], $qv['monthnum'], $qv['day'] );
1681
					if ( $qv['monthnum'] && $qv['year'] && ! wp_checkdate( $qv['monthnum'], $qv['day'], $qv['year'], $date ) ) {
1682
						$qv['error'] = '404';
1683
					} else {
1684
						$this->is_day = true;
1685
						$this->is_date = true;
1686
					}
1687
				}
1688
			}
1689
1690
			if ( $qv['monthnum'] ) {
1691
				if ( ! $this->is_date ) {
1692
					if ( 12 < $qv['monthnum'] ) {
1693
						$qv['error'] = '404';
1694
					} else {
1695
						$this->is_month = true;
1696
						$this->is_date = true;
1697
					}
1698
				}
1699
			}
1700
1701
			if ( $qv['year'] ) {
1702
				if ( ! $this->is_date ) {
1703
					$this->is_year = true;
1704
					$this->is_date = true;
1705
				}
1706
			}
1707
1708
			if ( $qv['m'] ) {
1709
				$this->is_date = true;
1710
				if ( strlen($qv['m']) > 9 ) {
1711
					$this->is_time = true;
1712
				} elseif ( strlen( $qv['m'] ) > 7 ) {
1713
					$this->is_day = true;
1714
				} elseif ( strlen( $qv['m'] ) > 5 ) {
1715
					$this->is_month = true;
1716
				} else {
1717
					$this->is_year = true;
1718
				}
1719
			}
1720
1721
			if ( '' != $qv['w'] ) {
1722
				$this->is_date = true;
1723
			}
1724
1725
			$this->query_vars_hash = false;
1726
			$this->parse_tax_query( $qv );
1727
1728
			foreach ( $this->tax_query->queries as $tax_query ) {
1729
				if ( ! is_array( $tax_query ) ) {
1730
					continue;
1731
				}
1732
1733
				if ( isset( $tax_query['operator'] ) && 'NOT IN' != $tax_query['operator'] ) {
1734
					switch ( $tax_query['taxonomy'] ) {
1735
						case 'category':
1736
							$this->is_category = true;
1737
							break;
1738
						case 'post_tag':
1739
							$this->is_tag = true;
1740
							break;
1741
						default:
1742
							$this->is_tax = true;
1743
					}
1744
				}
1745
			}
1746
			unset( $tax_query );
1747
1748
			if ( empty($qv['author']) || ($qv['author'] == '0') ) {
1749
				$this->is_author = false;
1750
			} else {
1751
				$this->is_author = true;
1752
			}
1753
1754
			if ( '' != $qv['author_name'] )
1755
				$this->is_author = true;
1756
1757
			if ( !empty( $qv['post_type'] ) && ! is_array( $qv['post_type'] ) ) {
1758
				$post_type_obj = get_post_type_object( $qv['post_type'] );
1759
				if ( ! empty( $post_type_obj->has_archive ) )
1760
					$this->is_post_type_archive = true;
1761
			}
1762
1763
			if ( $this->is_post_type_archive || $this->is_date || $this->is_author || $this->is_category || $this->is_tag || $this->is_tax )
1764
				$this->is_archive = true;
1765
		}
1766
1767
		if ( '' != $qv['feed'] )
1768
			$this->is_feed = true;
1769
1770
		if ( '' != $qv['embed'] ) {
1771
			$this->is_embed = true;
1772
		}
1773
1774
		if ( '' != $qv['tb'] )
1775
			$this->is_trackback = true;
1776
1777
		if ( '' != $qv['paged'] && ( intval($qv['paged']) > 1 ) )
1778
			$this->is_paged = true;
1779
1780
		// if we're previewing inside the write screen
1781
		if ( '' != $qv['preview'] )
1782
			$this->is_preview = true;
1783
1784
		if ( is_admin() )
1785
			$this->is_admin = true;
1786
1787
		if ( false !== strpos($qv['feed'], 'comments-') ) {
1788
			$qv['feed'] = str_replace('comments-', '', $qv['feed']);
1789
			$qv['withcomments'] = 1;
1790
		}
1791
1792
		$this->is_singular = $this->is_single || $this->is_page || $this->is_attachment;
1793
1794
		if ( $this->is_feed && ( !empty($qv['withcomments']) || ( empty($qv['withoutcomments']) && $this->is_singular ) ) )
1795
			$this->is_comment_feed = true;
1796
1797
		if ( !( $this->is_singular || $this->is_archive || $this->is_search || $this->is_feed || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) || $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_robots ) )
1798
			$this->is_home = true;
1799
1800
		// Correct is_* for page_on_front and page_for_posts
1801
		if ( $this->is_home && 'page' == get_option('show_on_front') && get_option('page_on_front') ) {
1802
			$_query = wp_parse_args($this->query);
1803
			// pagename can be set and empty depending on matched rewrite rules. Ignore an empty pagename.
1804
			if ( isset($_query['pagename']) && '' == $_query['pagename'] )
1805
				unset($_query['pagename']);
1806
1807
			unset( $_query['embed'] );
1808
1809
			if ( empty($_query) || !array_diff( array_keys($_query), array('preview', 'page', 'paged', 'cpage') ) ) {
1810
				$this->is_page = true;
1811
				$this->is_home = false;
1812
				$qv['page_id'] = get_option('page_on_front');
1813
				// Correct <!--nextpage--> for page_on_front
1814
				if ( !empty($qv['paged']) ) {
1815
					$qv['page'] = $qv['paged'];
1816
					unset($qv['paged']);
1817
				}
1818
			}
1819
		}
1820
1821
		if ( '' != $qv['pagename'] ) {
1822
			$this->queried_object = get_page_by_path( $qv['pagename'] );
1823
1824
			if ( $this->queried_object && 'attachment' == $this->queried_object->post_type ) {
1825
				if ( preg_match( "/^[^%]*%(?:postname)%/", get_option( 'permalink_structure' ) ) ) {
1826
					// See if we also have a post with the same slug
1827
					$post = get_page_by_path( $qv['pagename'], OBJECT, 'post' );
1828
					if ( $post ) {
1829
						$this->queried_object = $post;
1830
						$this->is_page = false;
1831
						$this->is_single = true;
1832
					}
1833
				}
1834
			}
1835
1836
			if ( ! empty( $this->queried_object ) ) {
1837
				$this->queried_object_id = (int) $this->queried_object->ID;
1838
			} else {
1839
				unset( $this->queried_object );
1840
			}
1841
1842
			if  ( 'page' == get_option('show_on_front') && isset($this->queried_object_id) && $this->queried_object_id == get_option('page_for_posts') ) {
1843
				$this->is_page = false;
1844
				$this->is_home = true;
1845
				$this->is_posts_page = true;
1846
			}
1847
		}
1848
1849
		if ( $qv['page_id'] ) {
1850
			if  ( 'page' == get_option('show_on_front') && $qv['page_id'] == get_option('page_for_posts') ) {
1851
				$this->is_page = false;
1852
				$this->is_home = true;
1853
				$this->is_posts_page = true;
1854
			}
1855
		}
1856
1857
		if ( !empty($qv['post_type']) ) {
1858
			if ( is_array($qv['post_type']) )
1859
				$qv['post_type'] = array_map('sanitize_key', $qv['post_type']);
1860
			else
1861
				$qv['post_type'] = sanitize_key($qv['post_type']);
1862
		}
1863
1864 View Code Duplication
		if ( ! empty( $qv['post_status'] ) ) {
1865
			if ( is_array( $qv['post_status'] ) )
1866
				$qv['post_status'] = array_map('sanitize_key', $qv['post_status']);
1867
			else
1868
				$qv['post_status'] = preg_replace('|[^a-z0-9_,-]|', '', $qv['post_status']);
1869
		}
1870
1871
		if ( $this->is_posts_page && ( ! isset($qv['withcomments']) || ! $qv['withcomments'] ) )
1872
			$this->is_comment_feed = false;
1873
1874
		$this->is_singular = $this->is_single || $this->is_page || $this->is_attachment;
1875
		// Done correcting is_* for page_on_front and page_for_posts
1876
1877
		if ( '404' == $qv['error'] )
1878
			$this->set_404();
1879
1880
		$this->is_embed = $this->is_embed && ( $this->is_singular || $this->is_404 );
1881
1882
		$this->query_vars_hash = md5( serialize( $this->query_vars ) );
1883
		$this->query_vars_changed = false;
1884
1885
		/**
1886
		 * Fires after the main query vars have been parsed.
1887
		 *
1888
		 * @since 1.5.0
1889
		 *
1890
		 * @param WP_Query &$this The WP_Query instance (passed by reference).
1891
		 */
1892
		do_action_ref_array( 'parse_query', array( &$this ) );
1893
	}
1894
1895
	/**
1896
	 * Parses various taxonomy related query vars.
1897
	 *
1898
	 * For BC, this method is not marked as protected. See [28987].
1899
	 *
1900
	 * @access protected
1901
	 * @since 3.1.0
1902
	 *
1903
	 * @param array $q The query variables. Passed by reference.
1904
	 */
1905
	public function parse_tax_query( &$q ) {
1906
		if ( ! empty( $q['tax_query'] ) && is_array( $q['tax_query'] ) ) {
1907
			$tax_query = $q['tax_query'];
1908
		} else {
1909
			$tax_query = array();
1910
		}
1911
1912
		if ( !empty($q['taxonomy']) && !empty($q['term']) ) {
1913
			$tax_query[] = array(
1914
				'taxonomy' => $q['taxonomy'],
1915
				'terms' => array( $q['term'] ),
1916
				'field' => 'slug',
1917
			);
1918
		}
1919
1920
		foreach ( get_taxonomies( array() , 'objects' ) as $taxonomy => $t ) {
1921
			if ( 'post_tag' == $taxonomy )
1922
				continue;	// Handled further down in the $q['tag'] block
1923
1924
			if ( $t->query_var && !empty( $q[$t->query_var] ) ) {
1925
				$tax_query_defaults = array(
1926
					'taxonomy' => $taxonomy,
1927
					'field' => 'slug',
1928
				);
1929
1930
 				if ( isset( $t->rewrite['hierarchical'] ) && $t->rewrite['hierarchical'] ) {
1931
					$q[$t->query_var] = wp_basename( $q[$t->query_var] );
1932
				}
1933
1934
				$term = $q[$t->query_var];
1935
1936
				if ( is_array( $term ) ) {
1937
					$term = implode( ',', $term );
1938
				}
1939
1940
				if ( strpos($term, '+') !== false ) {
1941
					$terms = preg_split( '/[+]+/', $term );
1942
					foreach ( $terms as $term ) {
1943
						$tax_query[] = array_merge( $tax_query_defaults, array(
1944
							'terms' => array( $term )
1945
						) );
1946
					}
1947
				} else {
1948
					$tax_query[] = array_merge( $tax_query_defaults, array(
1949
						'terms' => preg_split( '/[,]+/', $term )
1950
					) );
1951
				}
1952
			}
1953
		}
1954
1955
		// If querystring 'cat' is an array, implode it.
1956 View Code Duplication
		if ( is_array( $q['cat'] ) ) {
1957
			$q['cat'] = implode( ',', $q['cat'] );
1958
		}
1959
1960
		// Category stuff
1961
		if ( ! empty( $q['cat'] ) && ! $this->is_singular ) {
1962
			$cat_in = $cat_not_in = array();
1963
1964
			$cat_array = preg_split( '/[,\s]+/', urldecode( $q['cat'] ) );
1965
			$cat_array = array_map( 'intval', $cat_array );
1966
			$q['cat'] = implode( ',', $cat_array );
1967
1968
			foreach ( $cat_array as $cat ) {
1969
				if ( $cat > 0 )
1970
					$cat_in[] = $cat;
1971
				elseif ( $cat < 0 )
1972
					$cat_not_in[] = abs( $cat );
1973
			}
1974
1975 View Code Duplication
			if ( ! empty( $cat_in ) ) {
1976
				$tax_query[] = array(
1977
					'taxonomy' => 'category',
1978
					'terms' => $cat_in,
1979
					'field' => 'term_id',
1980
					'include_children' => true
1981
				);
1982
			}
1983
1984 View Code Duplication
			if ( ! empty( $cat_not_in ) ) {
1985
				$tax_query[] = array(
1986
					'taxonomy' => 'category',
1987
					'terms' => $cat_not_in,
1988
					'field' => 'term_id',
1989
					'operator' => 'NOT IN',
1990
					'include_children' => true
1991
				);
1992
			}
1993
			unset( $cat_array, $cat_in, $cat_not_in );
1994
		}
1995
1996
		if ( ! empty( $q['category__and'] ) && 1 === count( (array) $q['category__and'] ) ) {
1997
			$q['category__and'] = (array) $q['category__and'];
1998
			if ( ! isset( $q['category__in'] ) )
1999
				$q['category__in'] = array();
2000
			$q['category__in'][] = absint( reset( $q['category__and'] ) );
2001
			unset( $q['category__and'] );
2002
		}
2003
2004
		if ( ! empty( $q['category__in'] ) ) {
2005
			$q['category__in'] = array_map( 'absint', array_unique( (array) $q['category__in'] ) );
2006
			$tax_query[] = array(
2007
				'taxonomy' => 'category',
2008
				'terms' => $q['category__in'],
2009
				'field' => 'term_id',
2010
				'include_children' => false
2011
			);
2012
		}
2013
2014
		if ( ! empty($q['category__not_in']) ) {
2015
			$q['category__not_in'] = array_map( 'absint', array_unique( (array) $q['category__not_in'] ) );
2016
			$tax_query[] = array(
2017
				'taxonomy' => 'category',
2018
				'terms' => $q['category__not_in'],
2019
				'operator' => 'NOT IN',
2020
				'include_children' => false
2021
			);
2022
		}
2023
2024
		if ( ! empty($q['category__and']) ) {
2025
			$q['category__and'] = array_map( 'absint', array_unique( (array) $q['category__and'] ) );
2026
			$tax_query[] = array(
2027
				'taxonomy' => 'category',
2028
				'terms' => $q['category__and'],
2029
				'field' => 'term_id',
2030
				'operator' => 'AND',
2031
				'include_children' => false
2032
			);
2033
		}
2034
2035
		// If querystring 'tag' is array, implode it.
2036 View Code Duplication
		if ( is_array( $q['tag'] ) ) {
2037
			$q['tag'] = implode( ',', $q['tag'] );
2038
		}
2039
2040
		// Tag stuff
2041
		if ( '' != $q['tag'] && !$this->is_singular && $this->query_vars_changed ) {
2042
			if ( strpos($q['tag'], ',') !== false ) {
2043
				$tags = preg_split('/[,\r\n\t ]+/', $q['tag']);
2044 View Code Duplication
				foreach ( (array) $tags as $tag ) {
2045
					$tag = sanitize_term_field('slug', $tag, 0, 'post_tag', 'db');
2046
					$q['tag_slug__in'][] = $tag;
2047
				}
2048
			} elseif ( preg_match('/[+\r\n\t ]+/', $q['tag'] ) || ! empty( $q['cat'] ) ) {
2049
				$tags = preg_split('/[+\r\n\t ]+/', $q['tag']);
2050 View Code Duplication
				foreach ( (array) $tags as $tag ) {
2051
					$tag = sanitize_term_field('slug', $tag, 0, 'post_tag', 'db');
2052
					$q['tag_slug__and'][] = $tag;
2053
				}
2054
			} else {
2055
				$q['tag'] = sanitize_term_field('slug', $q['tag'], 0, 'post_tag', 'db');
2056
				$q['tag_slug__in'][] = $q['tag'];
2057
			}
2058
		}
2059
2060
		if ( !empty($q['tag_id']) ) {
2061
			$q['tag_id'] = absint( $q['tag_id'] );
2062
			$tax_query[] = array(
2063
				'taxonomy' => 'post_tag',
2064
				'terms' => $q['tag_id']
2065
			);
2066
		}
2067
2068 View Code Duplication
		if ( !empty($q['tag__in']) ) {
2069
			$q['tag__in'] = array_map('absint', array_unique( (array) $q['tag__in'] ) );
2070
			$tax_query[] = array(
2071
				'taxonomy' => 'post_tag',
2072
				'terms' => $q['tag__in']
2073
			);
2074
		}
2075
2076 View Code Duplication
		if ( !empty($q['tag__not_in']) ) {
2077
			$q['tag__not_in'] = array_map('absint', array_unique( (array) $q['tag__not_in'] ) );
2078
			$tax_query[] = array(
2079
				'taxonomy' => 'post_tag',
2080
				'terms' => $q['tag__not_in'],
2081
				'operator' => 'NOT IN'
2082
			);
2083
		}
2084
2085 View Code Duplication
		if ( !empty($q['tag__and']) ) {
2086
			$q['tag__and'] = array_map('absint', array_unique( (array) $q['tag__and'] ) );
2087
			$tax_query[] = array(
2088
				'taxonomy' => 'post_tag',
2089
				'terms' => $q['tag__and'],
2090
				'operator' => 'AND'
2091
			);
2092
		}
2093
2094 View Code Duplication
		if ( !empty($q['tag_slug__in']) ) {
2095
			$q['tag_slug__in'] = array_map('sanitize_title_for_query', array_unique( (array) $q['tag_slug__in'] ) );
2096
			$tax_query[] = array(
2097
				'taxonomy' => 'post_tag',
2098
				'terms' => $q['tag_slug__in'],
2099
				'field' => 'slug'
2100
			);
2101
		}
2102
2103
		if ( !empty($q['tag_slug__and']) ) {
2104
			$q['tag_slug__and'] = array_map('sanitize_title_for_query', array_unique( (array) $q['tag_slug__and'] ) );
2105
			$tax_query[] = array(
2106
				'taxonomy' => 'post_tag',
2107
				'terms' => $q['tag_slug__and'],
2108
				'field' => 'slug',
2109
				'operator' => 'AND'
2110
			);
2111
		}
2112
2113
		$this->tax_query = new WP_Tax_Query( $tax_query );
2114
2115
		/**
2116
		 * Fires after taxonomy-related query vars have been parsed.
2117
		 *
2118
		 * @since 3.7.0
2119
		 *
2120
		 * @param WP_Query $this The WP_Query instance.
2121
		 */
2122
		do_action( 'parse_tax_query', $this );
2123
	}
2124
2125
	/**
2126
	 * Generate SQL for the WHERE clause based on passed search terms.
2127
	 *
2128
	 * @since 3.7.0
2129
	 *
2130
	 * @param array $q Query variables.
2131
	 * @return string WHERE clause.
2132
	 */
2133
	protected function parse_search( &$q ) {
2134
		$search = '';
2135
2136
		// added slashes screw with quote grouping when done early, so done later
2137
		$q['s'] = stripslashes( $q['s'] );
2138
		if ( empty( $_GET['s'] ) && $this->is_main_query() )
2139
			$q['s'] = urldecode( $q['s'] );
2140
		// there are no line breaks in <input /> fields
2141
		$q['s'] = str_replace( array( "\r", "\n" ), '', $q['s'] );
2142
		$q['search_terms_count'] = 1;
2143
		if ( ! empty( $q['sentence'] ) ) {
2144
			$q['search_terms'] = array( $q['s'] );
2145
		} else {
2146
			if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $q['s'], $matches ) ) {
2147
				$q['search_terms_count'] = count( $matches[0] );
2148
				$q['search_terms'] = $this->parse_search_terms( $matches[0] );
2149
				// if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence
2150
				if ( empty( $q['search_terms'] ) || count( $q['search_terms'] ) > 9 )
2151
					$q['search_terms'] = array( $q['s'] );
2152
			} else {
2153
				$q['search_terms'] = array( $q['s'] );
2154
			}
2155
		}
2156
2157
		$n = ! empty( $q['exact'] ) ? '' : '%';
2158
		$searchand = '';
2159
		$q['search_orderby_title'] = array();
2160
		foreach ( $q['search_terms'] as $term ) {
2161
			// Terms prefixed with '-' should be excluded.
2162
			$include = '-' !== substr( $term, 0, 1 );
2163
			if ( $include ) {
2164
				$like_op  = 'LIKE';
2165
				$andor_op = 'OR';
2166
			} else {
2167
				$like_op  = 'NOT LIKE';
2168
				$andor_op = 'AND';
2169
				$term     = substr( $term, 1 );
2170
			}
2171
2172
			if ( $n && $include ) {
2173
				$like = '%' . $this->db->esc_like( $term ) . '%';
2174
				$q['search_orderby_title'][] = $this->db->prepare( "{$this->db->posts}.post_title LIKE %s", $like );
2175
			}
2176
2177
			$like = $n . $this->db->esc_like( $term ) . $n;
2178
			$search .= $this->db->prepare( "{$searchand}(({$this->db->posts}.post_title $like_op %s) $andor_op ({$this->db->posts}.post_excerpt $like_op %s) $andor_op ({$this->db->posts}.post_content $like_op %s))", $like, $like, $like );
2179
			$searchand = ' AND ';
2180
		}
2181
2182
		if ( ! empty( $search ) ) {
2183
			$search = " AND ({$search}) ";
2184
			if ( ! is_user_logged_in() ) {
2185
				$search .= " AND ({$this->db->posts}.post_password = '') ";
2186
			}
2187
		}
2188
2189
		return $search;
2190
	}
2191
2192
	/**
2193
	 * Check if the terms are suitable for searching.
2194
	 *
2195
	 * Uses an array of stopwords (terms) that are excluded from the separate
2196
	 * term matching when searching for posts. The list of English stopwords is
2197
	 * the approximate search engines list, and is translatable.
2198
	 *
2199
	 * @since 3.7.0
2200
	 *
2201
	 * @param array $terms Terms to check.
2202
	 * @return array Terms that are not stopwords.
2203
	 */
2204
	protected function parse_search_terms( $terms ) {
2205
		$strtolower = function_exists( 'mb_strtolower' ) ? 'mb_strtolower' : 'strtolower';
2206
		$checked = array();
2207
2208
		$stopwords = $this->get_search_stopwords();
2209
2210
		foreach ( $terms as $term ) {
2211
			// keep before/after spaces when term is for exact match
2212
			if ( preg_match( '/^".+"$/', $term ) )
2213
				$term = trim( $term, "\"'" );
2214
			else
2215
				$term = trim( $term, "\"' " );
2216
2217
			// Avoid single A-Z and single dashes.
2218
			if ( ! $term || ( 1 === strlen( $term ) && preg_match( '/^[a-z\-]$/i', $term ) ) )
2219
				continue;
2220
2221
			if ( in_array( call_user_func( $strtolower, $term ), $stopwords, true ) )
2222
				continue;
2223
2224
			$checked[] = $term;
2225
		}
2226
2227
		return $checked;
2228
	}
2229
2230
	/**
2231
	 * Retrieve stopwords used when parsing search terms.
2232
	 *
2233
	 * @since 3.7.0
2234
	 *
2235
	 * @return array Stopwords.
2236
	 */
2237
	protected function get_search_stopwords() {
2238
		if ( isset( $this->stopwords ) )
2239
			return $this->stopwords;
2240
2241
		/* translators: This is a comma-separated list of very common words that should be excluded from a search,
2242
		 * like a, an, and the. These are usually called "stopwords". You should not simply translate these individual
2243
		 * words into your language. Instead, look for and provide commonly accepted stopwords in your language.
2244
		 */
2245
		$words = explode( ',', _x( 'about,an,are,as,at,be,by,com,for,from,how,in,is,it,of,on,or,that,the,this,to,was,what,when,where,who,will,with,www',
2246
			'Comma-separated list of search stopwords in your language' ) );
2247
2248
		$stopwords = array();
2249
		foreach ( $words as $word ) {
2250
			$word = trim( $word, "\r\n\t " );
2251
			if ( $word )
2252
				$stopwords[] = $word;
2253
		}
2254
2255
		/**
2256
		 * Filters stopwords used when parsing search terms.
2257
		 *
2258
		 * @since 3.7.0
2259
		 *
2260
		 * @param array $stopwords Stopwords.
2261
		 */
2262
		$this->stopwords = apply_filters( 'wp_search_stopwords', $stopwords );
0 ignored issues
show
Documentation Bug introduced by
It seems like apply_filters('wp_search_stopwords', $stopwords) of type * is incompatible with the declared type array of property $stopwords.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
2263
		return $this->stopwords;
2264
	}
2265
2266
	/**
2267
	 * Generate SQL for the ORDER BY condition based on passed search terms.
2268
	 *
2269
	 * @param array $q Query variables.
2270
	 * @return string ORDER BY clause.
2271
	 */
2272
	protected function parse_search_order( &$q ) {
2273
		if ( $q['search_terms_count'] > 1 ) {
2274
			$num_terms = count( $q['search_orderby_title'] );
2275
2276
			// If the search terms contain negative queries, don't bother ordering by sentence matches.
2277
			$like = '';
2278
			if ( ! preg_match( '/(?:\s|^)\-/', $q['s'] ) ) {
2279
				$like = '%' . $this->db->esc_like( $q['s'] ) . '%';
2280
			}
2281
2282
			$search_orderby = '';
2283
2284
			// sentence match in 'post_title'
2285
			if ( $like ) {
2286
				$search_orderby .= $this->db->prepare( "WHEN {$this->db->posts}.post_title LIKE %s THEN 1 ", $like );
2287
			}
2288
2289
			// sanity limit, sort as sentence when more than 6 terms
2290
			// (few searches are longer than 6 terms and most titles are not)
2291
			if ( $num_terms < 7 ) {
2292
				// all words in title
2293
				$search_orderby .= 'WHEN ' . implode( ' AND ', $q['search_orderby_title'] ) . ' THEN 2 ';
2294
				// any word in title, not needed when $num_terms == 1
2295
				if ( $num_terms > 1 )
2296
					$search_orderby .= 'WHEN ' . implode( ' OR ', $q['search_orderby_title'] ) . ' THEN 3 ';
2297
			}
2298
2299
			// Sentence match in 'post_content' and 'post_excerpt'.
2300
			if ( $like ) {
2301
				$search_orderby .= $this->db->prepare( "WHEN {$this->db->posts}.post_excerpt LIKE %s THEN 4 ", $like );
2302
				$search_orderby .= $this->db->prepare( "WHEN {$this->db->posts}.post_content LIKE %s THEN 5 ", $like );
2303
			}
2304
2305
			if ( $search_orderby ) {
2306
				$search_orderby = '(CASE ' . $search_orderby . 'ELSE 6 END)';
2307
			}
2308
		} else {
2309
			// single word or sentence search
2310
			$search_orderby = reset( $q['search_orderby_title'] ) . ' DESC';
2311
		}
2312
2313
		return $search_orderby;
2314
	}
2315
2316
	/**
2317
	 * If the passed orderby value is allowed, convert the alias to a
2318
	 * properly-prefixed orderby value.
2319
	 *
2320
	 * @since 4.0.0
2321
	 * @access protected
2322
	 *
2323
	 * @param string $orderby Alias for the field to order by.
2324
	 * @return string|false Table-prefixed value to used in the ORDER clause. False otherwise.
2325
	 */
2326
	protected function parse_orderby( $orderby ) {
2327
		// Used to filter values.
2328
		$allowed_keys = array(
2329
			'post_name', 'post_author', 'post_date', 'post_title', 'post_modified',
2330
			'post_parent', 'post_type', 'name', 'author', 'date', 'title', 'modified',
2331
			'parent', 'type', 'ID', 'menu_order', 'comment_count', 'rand',
2332
		);
2333
2334
		$primary_meta_key = '';
2335
		$primary_meta_query = false;
2336
		$meta_clauses = $this->meta_query->get_clauses();
2337
		if ( ! empty( $meta_clauses ) ) {
2338
			$primary_meta_query = reset( $meta_clauses );
2339
2340
			if ( ! empty( $primary_meta_query['key'] ) ) {
2341
				$primary_meta_key = $primary_meta_query['key'];
2342
				$allowed_keys[] = $primary_meta_key;
2343
			}
2344
2345
			$allowed_keys[] = 'meta_value';
2346
			$allowed_keys[] = 'meta_value_num';
2347
			$allowed_keys   = array_merge( $allowed_keys, array_keys( $meta_clauses ) );
2348
		}
2349
2350
		// If RAND() contains a seed value, sanitize and add to allowed keys.
2351
		$rand_with_seed = false;
2352
		if ( preg_match( '/RAND\(([0-9]+)\)/i', $orderby, $matches ) ) {
2353
			$orderby = sprintf( 'RAND(%s)', intval( $matches[1] ) );
2354
			$allowed_keys[] = $orderby;
2355
			$rand_with_seed = true;
2356
		}
2357
2358
		if ( ! in_array( $orderby, $allowed_keys, true ) ) {
2359
			return false;
2360
		}
2361
2362
		switch ( $orderby ) {
2363
			case 'post_name':
2364
			case 'post_author':
2365
			case 'post_date':
2366
			case 'post_title':
2367
			case 'post_modified':
2368
			case 'post_parent':
2369
			case 'post_type':
2370
			case 'ID':
2371
			case 'menu_order':
2372
			case 'comment_count':
2373
				$orderby_clause = "{$this->db->posts}.{$orderby}";
2374
				break;
2375
			case 'rand':
2376
				$orderby_clause = 'RAND()';
2377
				break;
2378
			case $primary_meta_key:
2379 View Code Duplication
			case 'meta_value':
2380
				if ( ! empty( $primary_meta_query['type'] ) ) {
2381
					$orderby_clause = "CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})";
2382
				} else {
2383
					$orderby_clause = "{$primary_meta_query['alias']}.meta_value";
2384
				}
2385
				break;
2386
			case 'meta_value_num':
2387
				$orderby_clause = "{$primary_meta_query['alias']}.meta_value+0";
2388
				break;
2389
			default:
2390
				if ( array_key_exists( $orderby, $meta_clauses ) ) {
2391
					// $orderby corresponds to a meta_query clause.
2392
					$meta_clause = $meta_clauses[ $orderby ];
2393
					$orderby_clause = "CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})";
2394
				} elseif ( $rand_with_seed ) {
2395
					$orderby_clause = $orderby;
2396
				} else {
2397
					// Default: order by post field.
2398
					$orderby_clause = "{$this->db->posts}.post_" . sanitize_key( $orderby );
2399
				}
2400
2401
				break;
2402
		}
2403
2404
		return $orderby_clause;
2405
	}
2406
2407
	/**
2408
	 * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
2409
	 *
2410
	 * @since 4.0.0
2411
	 * @access protected
2412
	 *
2413
	 * @param string $order The 'order' query variable.
2414
	 * @return string The sanitized 'order' query variable.
2415
	 */
2416 View Code Duplication
	protected function parse_order( $order ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2417
		if ( ! is_string( $order ) || empty( $order ) ) {
2418
			return 'DESC';
2419
		}
2420
2421
		if ( 'ASC' === strtoupper( $order ) ) {
2422
			return 'ASC';
2423
		} else {
2424
			return 'DESC';
2425
		}
2426
	}
2427
2428
	/**
2429
	 * Sets the 404 property and saves whether query is feed.
2430
	 *
2431
	 * @since 2.0.0
2432
	 * @access public
2433
	 */
2434
	public function set_404() {
2435
		$is_feed = $this->is_feed;
2436
2437
		$this->init_query_flags();
2438
		$this->is_404 = true;
2439
2440
		$this->is_feed = $is_feed;
2441
	}
2442
2443
	/**
2444
	 * Retrieve query variable.
2445
	 *
2446
	 * @since 1.5.0
2447
	 * @since 3.9.0 The `$default` argument was introduced.
2448
	 *
2449
	 * @access public
2450
	 *
2451
	 * @param string $query_var Query variable key.
2452
	 * @param mixed  $default   Optional. Value to return if the query variable is not set. Default empty.
2453
	 * @return mixed Contents of the query variable.
2454
	 */
2455
	public function get( $query_var, $default = '' ) {
2456
		if ( isset( $this->query_vars[ $query_var ] ) ) {
2457
			return $this->query_vars[ $query_var ];
2458
		}
2459
2460
		return $default;
2461
	}
2462
2463
	/**
2464
	 * Set query variable.
2465
	 *
2466
	 * @since 1.5.0
2467
	 * @access public
2468
	 *
2469
	 * @param string $query_var Query variable key.
2470
	 * @param mixed  $value     Query variable value.
2471
	 */
2472
	public function set($query_var, $value) {
2473
		$this->query_vars[$query_var] = $value;
2474
	}
2475
2476
	/**
2477
	 * Retrieve the posts based on query variables.
2478
	 *
2479
	 * There are a few filters and actions that can be used to modify the post
2480
	 * database query.
2481
	 *
2482
	 * @since 1.5.0
2483
	 * @access public
2484
	 *
2485
	 * @return array List of posts.
2486
	 */
2487
	public function get_posts() {
2488
		$this->parse_query();
2489
2490
		/**
2491
		 * Fires after the query variable object is created, but before the actual query is run.
2492
		 *
2493
		 * Note: If using conditional tags, use the method versions within the passed instance
2494
		 * (e.g. $this->is_main_query() instead of is_main_query()). This is because the functions
2495
		 * like is_main_query() test against the global $wp_query instance, not the passed one.
2496
		 *
2497
		 * @since 2.0.0
2498
		 *
2499
		 * @param WP_Query &$this The WP_Query instance (passed by reference).
2500
		 */
2501
		do_action_ref_array( 'pre_get_posts', array( &$this ) );
2502
2503
		// Shorthand.
2504
		$q = &$this->query_vars;
2505
2506
		// Fill again in case pre_get_posts unset some vars.
2507
		$q = $this->fill_query_vars($q);
2508
2509
		// Parse meta query
2510
		$this->meta_query = new WP_Meta_Query();
2511
		$this->meta_query->parse_query_vars( $q );
2512
2513
		// Set a flag if a pre_get_posts hook changed the query vars.
2514
		$hash = md5( serialize( $this->query_vars ) );
2515
		if ( $hash != $this->query_vars_hash ) {
2516
			$this->query_vars_changed = true;
2517
			$this->query_vars_hash = $hash;
2518
		}
2519
		unset($hash);
2520
2521
		// First let's clear some variables
2522
		$distinct = '';
2523
		$whichauthor = '';
2524
		$whichmimetype = '';
2525
		$where = '';
2526
		$limits = '';
2527
		$join = '';
2528
		$search = '';
2529
		$groupby = '';
2530
		$post_status_join = false;
2531
		$page = 1;
2532
2533
		if ( isset( $q['caller_get_posts'] ) ) {
2534
			_deprecated_argument( 'WP_Query', '3.1.0', __( '"caller_get_posts" is deprecated. Use "ignore_sticky_posts" instead.' ) );
2535
			if ( !isset( $q['ignore_sticky_posts'] ) )
2536
				$q['ignore_sticky_posts'] = $q['caller_get_posts'];
2537
		}
2538
2539
		if ( !isset( $q['ignore_sticky_posts'] ) )
2540
			$q['ignore_sticky_posts'] = false;
2541
2542
		if ( !isset($q['suppress_filters']) )
2543
			$q['suppress_filters'] = false;
2544
2545
		if ( !isset($q['cache_results']) ) {
2546
			if ( wp_using_ext_object_cache() )
2547
				$q['cache_results'] = false;
2548
			else
2549
				$q['cache_results'] = true;
2550
		}
2551
2552
		if ( !isset($q['update_post_term_cache']) )
2553
			$q['update_post_term_cache'] = true;
2554
2555
		if ( ! isset( $q['lazy_load_term_meta'] ) ) {
2556
			$q['lazy_load_term_meta'] = $q['update_post_term_cache'];
2557
		}
2558
2559
		if ( !isset($q['update_post_meta_cache']) )
2560
			$q['update_post_meta_cache'] = true;
2561
2562
		if ( !isset($q['post_type']) ) {
2563
			if ( $this->is_search )
2564
				$q['post_type'] = 'any';
2565
			else
2566
				$q['post_type'] = '';
2567
		}
2568
		$post_type = $q['post_type'];
2569
		if ( empty( $q['posts_per_page'] ) ) {
2570
			$q['posts_per_page'] = get_option( 'posts_per_page' );
2571
		}
2572
		if ( isset($q['showposts']) && $q['showposts'] ) {
2573
			$q['showposts'] = (int) $q['showposts'];
2574
			$q['posts_per_page'] = $q['showposts'];
2575
		}
2576
		if ( (isset($q['posts_per_archive_page']) && $q['posts_per_archive_page'] != 0) && ($this->is_archive || $this->is_search) )
2577
			$q['posts_per_page'] = $q['posts_per_archive_page'];
2578
		if ( !isset($q['nopaging']) ) {
2579
			if ( $q['posts_per_page'] == -1 ) {
2580
				$q['nopaging'] = true;
2581
			} else {
2582
				$q['nopaging'] = false;
2583
			}
2584
		}
2585
2586
		if ( $this->is_feed ) {
2587
			// This overrides posts_per_page.
2588
			if ( ! empty( $q['posts_per_rss'] ) ) {
2589
				$q['posts_per_page'] = $q['posts_per_rss'];
2590
			} else {
2591
				$q['posts_per_page'] = get_option( 'posts_per_rss' );
2592
			}
2593
			$q['nopaging'] = false;
2594
		}
2595
		$q['posts_per_page'] = (int) $q['posts_per_page'];
2596
		if ( $q['posts_per_page'] < -1 )
2597
			$q['posts_per_page'] = abs($q['posts_per_page']);
2598
		elseif ( $q['posts_per_page'] == 0 )
2599
			$q['posts_per_page'] = 1;
2600
2601
		if ( !isset($q['comments_per_page']) || $q['comments_per_page'] == 0 )
2602
			$q['comments_per_page'] = get_option('comments_per_page');
2603
2604
		if ( $this->is_home && (empty($this->query) || $q['preview'] == 'true') && ( 'page' == get_option('show_on_front') ) && get_option('page_on_front') ) {
2605
			$this->is_page = true;
2606
			$this->is_home = false;
2607
			$q['page_id'] = get_option('page_on_front');
2608
		}
2609
2610
		if ( isset($q['page']) ) {
2611
			$q['page'] = trim($q['page'], '/');
2612
			$q['page'] = absint($q['page']);
2613
		}
2614
2615
		// If true, forcibly turns off SQL_CALC_FOUND_ROWS even when limits are present.
2616
		if ( isset($q['no_found_rows']) )
2617
			$q['no_found_rows'] = (bool) $q['no_found_rows'];
2618
		else
2619
			$q['no_found_rows'] = false;
2620
2621
		switch ( $q['fields'] ) {
2622
			case 'ids':
2623
				$fields = "{$this->db->posts}.ID";
2624
				break;
2625
			case 'id=>parent':
2626
				$fields = "{$this->db->posts}.ID, {$this->db->posts}.post_parent";
2627
				break;
2628
			default:
2629
				$fields = "{$this->db->posts}.*";
2630
		}
2631
2632
		if ( '' !== $q['menu_order'] ) {
2633
			$where .= " AND {$this->db->posts}.menu_order = " . $q['menu_order'];
2634
		}
2635
		// The "m" parameter is meant for months but accepts datetimes of varying specificity
2636
		if ( $q['m'] ) {
2637
			$where .= " AND YEAR({$this->db->posts}.post_date)=" . substr($q['m'], 0, 4);
2638 View Code Duplication
			if ( strlen($q['m']) > 5 ) {
2639
				$where .= " AND MONTH({$this->db->posts}.post_date)=" . substr($q['m'], 4, 2);
2640
			}
2641 View Code Duplication
			if ( strlen($q['m']) > 7 ) {
2642
				$where .= " AND DAYOFMONTH({$this->db->posts}.post_date)=" . substr($q['m'], 6, 2);
2643
			}
2644 View Code Duplication
			if ( strlen($q['m']) > 9 ) {
2645
				$where .= " AND HOUR({$this->db->posts}.post_date)=" . substr($q['m'], 8, 2);
2646
			}
2647 View Code Duplication
			if ( strlen($q['m']) > 11 ) {
2648
				$where .= " AND MINUTE({$this->db->posts}.post_date)=" . substr($q['m'], 10, 2);
2649
			}
2650 View Code Duplication
			if ( strlen($q['m']) > 13 ) {
2651
				$where .= " AND SECOND({$this->db->posts}.post_date)=" . substr($q['m'], 12, 2);
2652
			}
2653
		}
2654
2655
		// Handle the other individual date parameters
2656
		$date_parameters = array();
2657
2658
		if ( '' !== $q['hour'] )
2659
			$date_parameters['hour'] = $q['hour'];
2660
2661
		if ( '' !== $q['minute'] )
2662
			$date_parameters['minute'] = $q['minute'];
2663
2664
		if ( '' !== $q['second'] )
2665
			$date_parameters['second'] = $q['second'];
2666
2667
		if ( $q['year'] )
2668
			$date_parameters['year'] = $q['year'];
2669
2670
		if ( $q['monthnum'] )
2671
			$date_parameters['monthnum'] = $q['monthnum'];
2672
2673
		if ( $q['w'] )
2674
			$date_parameters['week'] = $q['w'];
2675
2676
		if ( $q['day'] )
2677
			$date_parameters['day'] = $q['day'];
2678
2679
		if ( $date_parameters ) {
2680
			$date_query = new WP_Date_Query( array( $date_parameters ) );
2681
			$where .= $date_query->get_sql();
2682
		}
2683
		unset( $date_parameters, $date_query );
2684
2685
		// Handle complex date queries
2686
		if ( ! empty( $q['date_query'] ) ) {
2687
			$this->date_query = new WP_Date_Query( $q['date_query'] );
2688
			$where .= $this->date_query->get_sql();
2689
		}
2690
2691
2692
		// If we've got a post_type AND it's not "any" post_type.
2693
		if ( !empty($q['post_type']) && 'any' != $q['post_type'] ) {
2694
			foreach ( (array)$q['post_type'] as $_post_type ) {
2695
				$ptype_obj = get_post_type_object($_post_type);
2696
				if ( !$ptype_obj || !$ptype_obj->query_var || empty($q[ $ptype_obj->query_var ]) )
2697
					continue;
2698
2699
				if ( ! $ptype_obj->hierarchical ) {
2700
					// Non-hierarchical post types can directly use 'name'.
2701
					$q['name'] = $q[ $ptype_obj->query_var ];
2702
				} else {
2703
					// Hierarchical post types will operate through 'pagename'.
2704
					$q['pagename'] = $q[ $ptype_obj->query_var ];
2705
					$q['name'] = '';
2706
				}
2707
2708
				// Only one request for a slug is possible, this is why name & pagename are overwritten above.
2709
				break;
2710
			} //end foreach
2711
			unset($ptype_obj);
2712
		}
2713
2714
		if ( '' !== $q['title'] ) {
2715
			$where .= $this->db->prepare( " AND {$this->db->posts}.post_title = %s", stripslashes( $q['title'] ) );
2716
		}
2717
2718
		// Parameters related to 'post_name'.
2719
		if ( '' != $q['name'] ) {
2720
			$q['name'] = sanitize_title_for_query( $q['name'] );
2721
			$where .= " AND {$this->db->posts}.post_name = '" . $q['name'] . "'";
2722
		} elseif ( '' != $q['pagename'] ) {
2723
			if ( isset($this->queried_object_id) ) {
2724
				$reqpage = $this->queried_object_id;
2725
			} else {
2726
				if ( 'page' != $q['post_type'] ) {
2727
					foreach ( (array)$q['post_type'] as $_post_type ) {
2728
						$ptype_obj = get_post_type_object($_post_type);
2729
						if ( !$ptype_obj || !$ptype_obj->hierarchical )
2730
							continue;
2731
2732
						$reqpage = get_page_by_path($q['pagename'], OBJECT, $_post_type);
2733
						if ( $reqpage )
2734
							break;
2735
					}
2736
					unset($ptype_obj);
2737
				} else {
2738
					$reqpage = get_page_by_path($q['pagename']);
2739
				}
2740
				if ( !empty($reqpage) )
2741
					$reqpage = $reqpage->ID;
2742
				else
2743
					$reqpage = 0;
2744
			}
2745
2746
			$page_for_posts = get_option('page_for_posts');
2747
			if  ( ('page' != get_option('show_on_front') ) || empty($page_for_posts) || ( $reqpage != $page_for_posts ) ) {
2748
				$q['pagename'] = sanitize_title_for_query( wp_basename( $q['pagename'] ) );
2749
				$q['name'] = $q['pagename'];
2750
				$where .= " AND ({$this->db->posts}.ID = '$reqpage')";
2751
				$reqpage_obj = get_post( $reqpage );
2752
				if ( is_object($reqpage_obj) && 'attachment' == $reqpage_obj->post_type ) {
2753
					$this->is_attachment = true;
2754
					$post_type = $q['post_type'] = 'attachment';
2755
					$this->is_page = true;
2756
					$q['attachment_id'] = $reqpage;
2757
				}
2758
			}
2759
		} elseif ( '' != $q['attachment'] ) {
2760
			$q['attachment'] = sanitize_title_for_query( wp_basename( $q['attachment'] ) );
2761
			$q['name'] = $q['attachment'];
2762
			$where .= " AND {$this->db->posts}.post_name = '" . $q['attachment'] . "'";
2763
		} elseif ( is_array( $q['post_name__in'] ) && ! empty( $q['post_name__in'] ) ) {
2764
			$q['post_name__in'] = array_map( 'sanitize_title_for_query', $q['post_name__in'] );
2765
			$post_name__in = "'" . implode( "','", $q['post_name__in'] ) . "'";
2766
			$where .= " AND {$this->db->posts}.post_name IN ($post_name__in)";
2767
		}
2768
2769
		// If an attachment is requested by number, let it supersede any post number.
2770
		if ( $q['attachment_id'] )
2771
			$q['p'] = absint($q['attachment_id']);
2772
2773
		// If a post number is specified, load that post
2774 View Code Duplication
		if ( $q['p'] ) {
2775
			$where .= " AND {$this->db->posts}.ID = " . $q['p'];
2776
		} elseif ( $q['post__in'] ) {
2777
			$post__in = implode(',', array_map( 'absint', $q['post__in'] ));
2778
			$where .= " AND {$this->db->posts}.ID IN ($post__in)";
2779
		} elseif ( $q['post__not_in'] ) {
2780
			$post__not_in = implode(',',  array_map( 'absint', $q['post__not_in'] ));
2781
			$where .= " AND {$this->db->posts}.ID NOT IN ($post__not_in)";
2782
		}
2783
2784 View Code Duplication
		if ( is_numeric( $q['post_parent'] ) ) {
2785
			$where .= $this->db->prepare( " AND {$this->db->posts}.post_parent = %d ", $q['post_parent'] );
2786
		} elseif ( $q['post_parent__in'] ) {
2787
			$post_parent__in = implode( ',', array_map( 'absint', $q['post_parent__in'] ) );
2788
			$where .= " AND {$this->db->posts}.post_parent IN ($post_parent__in)";
2789
		} elseif ( $q['post_parent__not_in'] ) {
2790
			$post_parent__not_in = implode( ',',  array_map( 'absint', $q['post_parent__not_in'] ) );
2791
			$where .= " AND {$this->db->posts}.post_parent NOT IN ($post_parent__not_in)";
2792
		}
2793
2794
		if ( $q['page_id'] ) {
2795
			if  ( ('page' != get_option('show_on_front') ) || ( $q['page_id'] != get_option('page_for_posts') ) ) {
2796
				$q['p'] = $q['page_id'];
2797
				$where = " AND {$this->db->posts}.ID = " . $q['page_id'];
2798
			}
2799
		}
2800
2801
		// If a search pattern is specified, load the posts that match.
2802
		if ( strlen( $q['s'] ) ) {
2803
			$search = $this->parse_search( $q );
2804
		}
2805
2806
		if ( ! $q['suppress_filters'] ) {
2807
			/**
2808
			 * Filters the search SQL that is used in the WHERE clause of WP_Query.
2809
			 *
2810
			 * @since 3.0.0
2811
			 *
2812
			 * @param string   $search Search SQL for WHERE clause.
2813
			 * @param WP_Query $this   The current WP_Query object.
2814
			 */
2815
			$search = apply_filters_ref_array( 'posts_search', array( $search, &$this ) );
2816
		}
2817
2818
		// Taxonomies
2819
		if ( !$this->is_singular ) {
2820
			$this->parse_tax_query( $q );
2821
2822
			$clauses = $this->tax_query->get_sql( $this->db->posts, 'ID' );
2823
2824
			$join .= $clauses['join'];
2825
			$where .= $clauses['where'];
2826
		}
2827
2828
		if ( $this->is_tax ) {
2829
			if ( empty($post_type) ) {
2830
				// Do a fully inclusive search for currently registered post types of queried taxonomies
2831
				$post_type = array();
2832
				$taxonomies = array_keys( $this->tax_query->queried_terms );
2833
				foreach ( get_post_types( array( 'exclude_from_search' => false ) ) as $pt ) {
2834
					$object_taxonomies = $pt === 'attachment' ? get_taxonomies_for_attachments() : get_object_taxonomies( $pt );
2835
					if ( array_intersect( $taxonomies, $object_taxonomies ) )
2836
						$post_type[] = $pt;
2837
				}
2838
				if ( ! $post_type )
2839
					$post_type = 'any';
2840
				elseif ( count( $post_type ) == 1 )
2841
					$post_type = $post_type[0];
2842
2843
				$post_status_join = true;
2844
			} elseif ( in_array('attachment', (array) $post_type) ) {
2845
				$post_status_join = true;
2846
			}
2847
		}
2848
2849
		/*
2850
		 * Ensure that 'taxonomy', 'term', 'term_id', 'cat', and
2851
		 * 'category_name' vars are set for backward compatibility.
2852
		 */
2853
		if ( ! empty( $this->tax_query->queried_terms ) ) {
2854
2855
			/*
2856
			 * Set 'taxonomy', 'term', and 'term_id' to the
2857
			 * first taxonomy other than 'post_tag' or 'category'.
2858
			 */
2859
			if ( ! isset( $q['taxonomy'] ) ) {
2860
				foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
2861
					if ( empty( $queried_items['terms'][0] ) ) {
2862
						continue;
2863
					}
2864
2865
					if ( ! in_array( $queried_taxonomy, array( 'category', 'post_tag' ) ) ) {
2866
						$q['taxonomy'] = $queried_taxonomy;
2867
2868
						if ( 'slug' === $queried_items['field'] ) {
2869
							$q['term'] = $queried_items['terms'][0];
2870
						} else {
2871
							$q['term_id'] = $queried_items['terms'][0];
2872
						}
2873
2874
						// Take the first one we find.
2875
						break;
2876
					}
2877
				}
2878
			}
2879
2880
			// 'cat', 'category_name', 'tag_id'
2881
			foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
2882
				if ( empty( $queried_items['terms'][0] ) ) {
2883
					continue;
2884
				}
2885
2886 View Code Duplication
				if ( 'category' === $queried_taxonomy ) {
2887
					$the_cat = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'category' );
2888
					if ( $the_cat ) {
2889
						$this->set( 'cat', $the_cat->term_id );
2890
						$this->set( 'category_name', $the_cat->slug );
2891
					}
2892
					unset( $the_cat );
2893
				}
2894
2895 View Code Duplication
				if ( 'post_tag' === $queried_taxonomy ) {
2896
					$the_tag = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'post_tag' );
2897
					if ( $the_tag ) {
2898
						$this->set( 'tag_id', $the_tag->term_id );
2899
					}
2900
					unset( $the_tag );
2901
				}
2902
			}
2903
		}
2904
2905
		if ( !empty( $this->tax_query->queries ) || !empty( $this->meta_query->queries ) ) {
2906
			$groupby = "{$this->db->posts}.ID";
2907
		}
2908
2909
		// Author/user stuff
2910
2911
		if ( ! empty( $q['author'] ) && $q['author'] != '0' ) {
2912
			$q['author'] = addslashes_gpc( '' . urldecode( $q['author'] ) );
2913
			$authors = array_unique( array_map( 'intval', preg_split( '/[,\s]+/', $q['author'] ) ) );
2914
			foreach ( $authors as $author ) {
2915
				$key = $author > 0 ? 'author__in' : 'author__not_in';
2916
				$q[$key][] = abs( $author );
2917
			}
2918
			$q['author'] = implode( ',', $authors );
2919
		}
2920
2921
		if ( ! empty( $q['author__not_in'] ) ) {
2922
			$author__not_in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__not_in'] ) ) );
2923
			$where .= " AND {$this->db->posts}.post_author NOT IN ($author__not_in) ";
2924
		} elseif ( ! empty( $q['author__in'] ) ) {
2925
			$author__in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__in'] ) ) );
2926
			$where .= " AND {$this->db->posts}.post_author IN ($author__in) ";
2927
		}
2928
2929
		// Author stuff for nice URLs
2930
2931
		if ( '' != $q['author_name'] ) {
2932
			if ( strpos($q['author_name'], '/') !== false ) {
2933
				$q['author_name'] = explode('/', $q['author_name']);
2934
				if ( $q['author_name'][ count($q['author_name'])-1 ] ) {
2935
					$q['author_name'] = $q['author_name'][count($q['author_name'])-1]; // no trailing slash
2936
				} else {
2937
					$q['author_name'] = $q['author_name'][count($q['author_name'])-2]; // there was a trailing slash
2938
				}
2939
			}
2940
			$q['author_name'] = sanitize_title_for_query( $q['author_name'] );
2941
			$q['author'] = get_user_by('slug', $q['author_name']);
2942
			if ( $q['author'] )
2943
				$q['author'] = $q['author']->ID;
2944
			$whichauthor .= " AND ({$this->db->posts}.post_author = " . absint($q['author']) . ')';
2945
		}
2946
2947
		// MIME-Type stuff for attachment browsing
2948
2949
		if ( isset( $q['post_mime_type'] ) && '' != $q['post_mime_type'] ) {
2950
			$whichmimetype = wp_post_mime_type_where( $q['post_mime_type'], $this->db->posts );
2951
		}
2952
		$where .= $search . $whichauthor . $whichmimetype;
2953
2954
		if ( ! empty( $this->meta_query->queries ) ) {
2955
			$clauses = $this->meta_query->get_sql( 'post', $this->db->posts, 'ID', $this );
2956
			$join   .= $clauses['join'];
2957
			$where  .= $clauses['where'];
2958
		}
2959
2960
		$rand = ( isset( $q['orderby'] ) && 'rand' === $q['orderby'] );
2961
		if ( ! isset( $q['order'] ) ) {
2962
			$q['order'] = $rand ? '' : 'DESC';
2963
		} else {
2964
			$q['order'] = $rand ? '' : $this->parse_order( $q['order'] );
2965
		}
2966
2967
		// Order by.
2968
		if ( empty( $q['orderby'] ) ) {
2969
			/*
2970
			 * Boolean false or empty array blanks out ORDER BY,
2971
			 * while leaving the value unset or otherwise empty sets the default.
2972
			 */
2973
			if ( isset( $q['orderby'] ) && ( is_array( $q['orderby'] ) || false === $q['orderby'] ) ) {
2974
				$orderby = '';
2975
			} else {
2976
				$orderby = "{$this->db->posts}.post_date " . $q['order'];
2977
			}
2978
		} elseif ( 'none' == $q['orderby'] ) {
2979
			$orderby = '';
2980 View Code Duplication
		} elseif ( $q['orderby'] == 'post__in' && ! empty( $post__in ) ) {
2981
			$orderby = "FIELD( {$this->db->posts}.ID, $post__in )";
2982
		} elseif ( $q['orderby'] == 'post_parent__in' && ! empty( $post_parent__in ) ) {
2983
			$orderby = "FIELD( {$this->db->posts}.post_parent, $post_parent__in )";
2984 View Code Duplication
		} elseif ( $q['orderby'] == 'post_name__in' && ! empty( $post_name__in ) ) {
2985
			$orderby = "FIELD( {$this->db->posts}.post_name, $post_name__in )";
2986
		} else {
2987
			$orderby_array = array();
2988
			if ( is_array( $q['orderby'] ) ) {
2989
				foreach ( $q['orderby'] as $_orderby => $order ) {
2990
					$orderby = addslashes_gpc( urldecode( $_orderby ) );
2991
					$parsed  = $this->parse_orderby( $orderby );
0 ignored issues
show
It seems like $orderby defined by addslashes_gpc(urldecode($_orderby)) on line 2990 can also be of type array; however, WP_Query::parse_orderby() does only seem to accept string, 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.

Loading history...
2992
2993
					if ( ! $parsed ) {
2994
						continue;
2995
					}
2996
2997
					$orderby_array[] = $parsed . ' ' . $this->parse_order( $order );
2998
				}
2999
				$orderby = implode( ', ', $orderby_array );
3000
3001
			} else {
3002
				$q['orderby'] = urldecode( $q['orderby'] );
3003
				$q['orderby'] = addslashes_gpc( $q['orderby'] );
3004
3005
				foreach ( explode( ' ', $q['orderby'] ) as $i => $orderby ) {
3006
					$parsed = $this->parse_orderby( $orderby );
3007
					// Only allow certain values for safety.
3008
					if ( ! $parsed ) {
3009
						continue;
3010
					}
3011
3012
					$orderby_array[] = $parsed;
3013
				}
3014
				$orderby = implode( ' ' . $q['order'] . ', ', $orderby_array );
3015
3016
				if ( empty( $orderby ) ) {
3017
					$orderby = "{$this->db->posts}.post_date " . $q['order'];
3018
				} elseif ( ! empty( $q['order'] ) ) {
3019
					$orderby .= " {$q['order']}";
3020
				}
3021
			}
3022
		}
3023
3024
		// Order search results by relevance only when another "orderby" is not specified in the query.
3025
		if ( ! empty( $q['s'] ) ) {
3026
			$search_orderby = '';
3027
			if ( ! empty( $q['search_orderby_title'] ) && ( empty( $q['orderby'] ) && ! $this->is_feed ) || ( isset( $q['orderby'] ) && 'relevance' === $q['orderby'] ) )
3028
				$search_orderby = $this->parse_search_order( $q );
3029
3030
			if ( ! $q['suppress_filters'] ) {
3031
				/**
3032
				 * Filters the ORDER BY used when ordering search results.
3033
				 *
3034
				 * @since 3.7.0
3035
				 *
3036
				 * @param string   $search_orderby The ORDER BY clause.
3037
				 * @param WP_Query $this           The current WP_Query instance.
3038
				 */
3039
				$search_orderby = apply_filters( 'posts_search_orderby', $search_orderby, $this );
3040
			}
3041
3042
			if ( $search_orderby )
3043
				$orderby = $orderby ? $search_orderby . ', ' . $orderby : $search_orderby;
3044
		}
3045
3046
		if ( is_array( $post_type ) && count( $post_type ) > 1 ) {
3047
			$post_type_cap = 'multiple_post_type';
3048
		} else {
3049
			if ( is_array( $post_type ) )
3050
				$post_type = reset( $post_type );
3051
			$post_type_object = get_post_type_object( $post_type );
3052
			if ( empty( $post_type_object ) )
3053
				$post_type_cap = $post_type;
3054
		}
3055
3056
		if ( isset( $q['post_password'] ) ) {
3057
			$where .= $this->db->prepare( " AND {$this->db->posts}.post_password = %s", $q['post_password'] );
3058
			if ( empty( $q['perm'] ) ) {
3059
				$q['perm'] = 'readable';
3060
			}
3061
		} elseif ( isset( $q['has_password'] ) ) {
3062
			$where .= sprintf( " AND {$this->db->posts}.post_password %s ''", $q['has_password'] ? '!=' : '=' );
3063
		}
3064
3065 View Code Duplication
		if ( ! empty( $q['comment_status'] ) ) {
3066
			$where .= $this->db->prepare( " AND {$this->db->posts}.comment_status = %s ", $q['comment_status'] );
3067
		}
3068
3069 View Code Duplication
		if ( ! empty( $q['ping_status'] ) )  {
3070
			$where .= $this->db->prepare( " AND {$this->db->posts}.ping_status = %s ", $q['ping_status'] );
3071
		}
3072
3073
		if ( 'any' == $post_type ) {
3074
			$in_search_post_types = get_post_types( array('exclude_from_search' => false) );
3075
			if ( empty( $in_search_post_types ) ) {
3076
				$where .= ' AND 1=0 ';
3077
			} else {
3078
				$where .= " AND {$this->db->posts}.post_type IN ('" . join("', '", $in_search_post_types ) . "')";
3079
			}
3080
		} elseif ( !empty( $post_type ) && is_array( $post_type ) ) {
3081
			$where .= " AND {$this->db->posts}.post_type IN ('" . join("', '", $post_type) . "')";
3082
		} elseif ( ! empty( $post_type ) ) {
3083
			$where .= " AND {$this->db->posts}.post_type = '$post_type'";
3084
			$post_type_object = get_post_type_object ( $post_type );
3085
		} elseif ( $this->is_attachment ) {
3086
			$where .= " AND {$this->db->posts}.post_type = 'attachment'";
3087
			$post_type_object = get_post_type_object ( 'attachment' );
3088
		} elseif ( $this->is_page ) {
3089
			$where .= " AND {$this->db->posts}.post_type = 'page'";
3090
			$post_type_object = get_post_type_object ( 'page' );
3091
		} else {
3092
			$where .= " AND {$this->db->posts}.post_type = 'post'";
3093
			$post_type_object = get_post_type_object ( 'post' );
3094
		}
3095
3096
		$edit_cap = 'edit_post';
3097
		$read_cap = 'read_post';
3098
3099
		if ( ! empty( $post_type_object ) ) {
3100
			$edit_others_cap = $post_type_object->cap->edit_others_posts;
3101
			$read_private_cap = $post_type_object->cap->read_private_posts;
3102
		} else {
3103
			$edit_others_cap = 'edit_others_' . $post_type_cap . 's';
0 ignored issues
show
The variable $post_type_cap does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3104
			$read_private_cap = 'read_private_' . $post_type_cap . 's';
3105
		}
3106
3107
		$user_id = get_current_user_id();
3108
3109
		$q_status = array();
3110
		if ( ! empty( $q['post_status'] ) ) {
3111
			$statuswheres = array();
3112
			$q_status = $q['post_status'];
3113
			if ( ! is_array( $q_status ) )
3114
				$q_status = explode(',', $q_status);
3115
			$r_status = array();
3116
			$p_status = array();
3117
			$e_status = array();
3118
			if ( in_array( 'any', $q_status ) ) {
3119
				foreach ( get_post_stati( array( 'exclude_from_search' => true ) ) as $status ) {
3120
					if ( ! in_array( $status, $q_status ) ) {
3121
						$e_status[] = "{$this->db->posts}.post_status <> '$status'";
3122
					}
3123
				}
3124
			} else {
3125
				foreach ( get_post_stati() as $status ) {
3126
					if ( in_array( $status, $q_status ) ) {
3127
						if ( 'private' == $status ) {
3128
							$p_status[] = "{$this->db->posts}.post_status = '$status'";
3129
						} else {
3130
							$r_status[] = "{$this->db->posts}.post_status = '$status'";
3131
						}
3132
					}
3133
				}
3134
			}
3135
3136
			if ( empty($q['perm'] ) || 'readable' != $q['perm'] ) {
3137
				$r_status = array_merge($r_status, $p_status);
3138
				unset($p_status);
3139
			}
3140
3141
			if ( !empty($e_status) ) {
3142
				$statuswheres[] = "(" . join( ' AND ', $e_status ) . ")";
3143
			}
3144 View Code Duplication
			if ( !empty($r_status) ) {
3145
				if ( !empty($q['perm'] ) && 'editable' == $q['perm'] && !current_user_can($edit_others_cap) ) {
3146
					$statuswheres[] = "({$this->db->posts}.post_author = $user_id " . "AND (" . join( ' OR ', $r_status ) . "))";
3147
				} else {
3148
					$statuswheres[] = "(" . join( ' OR ', $r_status ) . ")";
3149
				}
3150
			}
3151 View Code Duplication
			if ( !empty($p_status) ) {
3152
				if ( !empty($q['perm'] ) && 'readable' == $q['perm'] && !current_user_can($read_private_cap) ) {
3153
					$statuswheres[] = "({$this->db->posts}.post_author = $user_id " . "AND (" . join( ' OR ', $p_status ) . "))";
3154
				} else {
3155
					$statuswheres[] = "(" . join( ' OR ', $p_status ) . ")";
3156
				}
3157
			}
3158
			if ( $post_status_join ) {
3159
				$join .= " LEFT JOIN {$this->db->posts} AS p2 ON ({$this->db->posts}.post_parent = p2.ID) ";
3160
				foreach ( $statuswheres as $index => $statuswhere ) {
3161
					$statuswheres[$index] = "($statuswhere OR ({$this->db->posts}.post_status = 'inherit' AND " . str_replace( $this->db->posts, 'p2', $statuswhere ) . "))";
3162
				}
3163
			}
3164
			$where_status = implode( ' OR ', $statuswheres );
3165
			if ( ! empty( $where_status ) ) {
3166
				$where .= " AND ($where_status)";
3167
			}
3168
		} elseif ( !$this->is_singular ) {
3169
			$where .= " AND ({$this->db->posts}.post_status = 'publish'";
3170
3171
			// Add public states.
3172
			$public_states = get_post_stati( array('public' => true) );
3173
			foreach ( (array) $public_states as $state ) {
3174
				if ( 'publish' == $state ) // Publish is hard-coded above.
3175
					continue;
3176
				$where .= " OR {$this->db->posts}.post_status = '$state'";
3177
			}
3178
3179
			if ( $this->is_admin ) {
3180
				// Add protected states that should show in the admin all list.
3181
				$admin_all_states = get_post_stati( array('protected' => true, 'show_in_admin_all_list' => true) );
3182
				foreach ( (array) $admin_all_states as $state ) {
3183
					$where .= " OR {$this->db->posts}.post_status = '$state'";
3184
				}
3185
			}
3186
3187
			if ( is_user_logged_in() ) {
3188
				// Add private states that are limited to viewing by the author of a post or someone who has caps to read private states.
3189
				$private_states = get_post_stati( array('private' => true) );
3190
				foreach ( (array) $private_states as $state ) {
3191
					$where .= current_user_can( $read_private_cap ) ? " OR {$this->db->posts}.post_status = '$state'" : " OR {$this->db->posts}.post_author = $user_id AND {$this->db->posts}.post_status = '$state'";
3192
				}
3193
			}
3194
3195
			$where .= ')';
3196
		}
3197
3198
		/*
3199
		 * Apply filters on where and join prior to paging so that any
3200
		 * manipulations to them are reflected in the paging by day queries.
3201
		 */
3202
		if ( !$q['suppress_filters'] ) {
3203
			/**
3204
			 * Filters the WHERE clause of the query.
3205
			 *
3206
			 * @since 1.5.0
3207
			 *
3208
			 * @param string   $where The WHERE clause of the query.
3209
			 * @param WP_Query &$this The WP_Query instance (passed by reference).
3210
			 */
3211
			$where = apply_filters_ref_array( 'posts_where', array( $where, &$this ) );
3212
3213
			/**
3214
			 * Filters the JOIN clause of the query.
3215
			 *
3216
			 * @since 1.5.0
3217
			 *
3218
			 * @param string   $where The JOIN clause of the query.
3219
			 * @param WP_Query &$this The WP_Query instance (passed by reference).
3220
			 */
3221
			$join = apply_filters_ref_array( 'posts_join', array( $join, &$this ) );
3222
		}
3223
3224
		// Paging
3225
		if ( empty($q['nopaging']) && !$this->is_singular ) {
3226
			$page = absint($q['paged']);
3227
			if ( !$page )
3228
				$page = 1;
3229
3230
			// If 'offset' is provided, it takes precedence over 'paged'.
3231
			if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {
3232
				$q['offset'] = absint( $q['offset'] );
3233
				$pgstrt = $q['offset'] . ', ';
3234
			} else {
3235
				$pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
3236
			}
3237
			$limits = 'LIMIT ' . $pgstrt . $q['posts_per_page'];
3238
		}
3239
3240
		// Comments feeds
3241
		if ( $this->is_comment_feed && ! $this->is_singular ) {
3242
			if ( $this->is_archive || $this->is_search ) {
3243
				$cjoin = "JOIN {$this->db->posts} ON ({$this->db->comments}.comment_post_ID = {$this->db->posts}.ID) $join ";
3244
				$cwhere = "WHERE comment_approved = '1' $where";
3245
				$cgroupby = "{$this->db->comments}.comment_id";
3246
			} else { // Other non singular e.g. front
3247
				$cjoin = "JOIN {$this->db->posts} ON ( {$this->db->comments}.comment_post_ID = {$this->db->posts}.ID )";
3248
				$cwhere = "WHERE ( post_status = 'publish' OR ( post_status = 'inherit' && post_type = 'attachment' ) ) AND comment_approved = '1'";
3249
				$cgroupby = '';
3250
			}
3251
3252
			if ( !$q['suppress_filters'] ) {
3253
				/**
3254
				 * Filters the JOIN clause of the comments feed query before sending.
3255
				 *
3256
				 * @since 2.2.0
3257
				 *
3258
				 * @param string   $cjoin The JOIN clause of the query.
3259
				 * @param WP_Query &$this The WP_Query instance (passed by reference).
3260
				 */
3261
				$cjoin = apply_filters_ref_array( 'comment_feed_join', array( $cjoin, &$this ) );
3262
3263
				/**
3264
				 * Filters the WHERE clause of the comments feed query before sending.
3265
				 *
3266
				 * @since 2.2.0
3267
				 *
3268
				 * @param string   $cwhere The WHERE clause of the query.
3269
				 * @param WP_Query &$this  The WP_Query instance (passed by reference).
3270
				 */
3271
				$cwhere = apply_filters_ref_array( 'comment_feed_where', array( $cwhere, &$this ) );
3272
3273
				/**
3274
				 * Filters the GROUP BY clause of the comments feed query before sending.
3275
				 *
3276
				 * @since 2.2.0
3277
				 *
3278
				 * @param string   $cgroupby The GROUP BY clause of the query.
3279
				 * @param WP_Query &$this    The WP_Query instance (passed by reference).
3280
				 */
3281
				$cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( $cgroupby, &$this ) );
3282
3283
				/**
3284
				 * Filters the ORDER BY clause of the comments feed query before sending.
3285
				 *
3286
				 * @since 2.8.0
3287
				 *
3288
				 * @param string   $corderby The ORDER BY clause of the query.
3289
				 * @param WP_Query &$this    The WP_Query instance (passed by reference).
3290
				 */
3291
				$corderby = apply_filters_ref_array( 'comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
3292
3293
				/**
3294
				 * Filters the LIMIT clause of the comments feed query before sending.
3295
				 *
3296
				 * @since 2.8.0
3297
				 *
3298
				 * @param string   $climits The JOIN clause of the query.
3299
				 * @param WP_Query &$this   The WP_Query instance (passed by reference).
3300
				 */
3301
				$climits = apply_filters_ref_array( 'comment_feed_limits', array( 'LIMIT ' . get_option('posts_per_rss'), &$this ) );
3302
			}
3303
			$cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
3304
			$corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
3305
3306
			$comments = (array) $this->db->get_results("SELECT $distinct {$this->db->comments}.* FROM {$this->db->comments} $cjoin $cwhere $cgroupby $corderby $climits");
0 ignored issues
show
The variable $climits does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3307
			// Convert to WP_Comment
3308
			$this->comments = array_map( 'get_comment', $comments );
3309
			$this->comment_count = count($this->comments);
3310
3311
			$post_ids = array();
3312
3313
			foreach ( $this->comments as $comment )
3314
				$post_ids[] = (int) $comment->comment_post_ID;
3315
3316
			$post_ids = join(',', $post_ids);
3317
			$join = '';
3318
			if ( $post_ids ) {
3319
				$where = "AND {$this->db->posts}.ID IN ($post_ids) ";
3320
			} else {
3321
				$where = "AND 0";
3322
			}
3323
		}
3324
3325
		$pieces = array( 'where', 'groupby', 'join', 'orderby', 'distinct', 'fields', 'limits' );
3326
3327
		/*
3328
		 * Apply post-paging filters on where and join. Only plugins that
3329
		 * manipulate paging queries should use these hooks.
3330
		 */
3331 View Code Duplication
		if ( !$q['suppress_filters'] ) {
3332
			/**
3333
			 * Filters the WHERE clause of the query.
3334
			 *
3335
			 * Specifically for manipulating paging queries.
3336
			 *
3337
			 * @since 1.5.0
3338
			 *
3339
			 * @param string   $where The WHERE clause of the query.
3340
			 * @param WP_Query &$this The WP_Query instance (passed by reference).
3341
			 */
3342
			$where = apply_filters_ref_array( 'posts_where_paged', array( $where, &$this ) );
3343
3344
			/**
3345
			 * Filters the GROUP BY clause of the query.
3346
			 *
3347
			 * @since 2.0.0
3348
			 *
3349
			 * @param string   $groupby The GROUP BY clause of the query.
3350
			 * @param WP_Query &$this   The WP_Query instance (passed by reference).
3351
			 */
3352
			$groupby = apply_filters_ref_array( 'posts_groupby', array( $groupby, &$this ) );
3353
3354
			/**
3355
			 * Filters the JOIN clause of the query.
3356
			 *
3357
			 * Specifically for manipulating paging queries.
3358
			 *
3359
			 * @since 1.5.0
3360
			 *
3361
			 * @param string   $join  The JOIN clause of the query.
3362
			 * @param WP_Query &$this The WP_Query instance (passed by reference).
3363
			 */
3364
			$join = apply_filters_ref_array( 'posts_join_paged', array( $join, &$this ) );
3365
3366
			/**
3367
			 * Filters the ORDER BY clause of the query.
3368
			 *
3369
			 * @since 1.5.1
3370
			 *
3371
			 * @param string   $orderby The ORDER BY clause of the query.
3372
			 * @param WP_Query &$this   The WP_Query instance (passed by reference).
3373
			 */
3374
			$orderby = apply_filters_ref_array( 'posts_orderby', array( $orderby, &$this ) );
3375
3376
			/**
3377
			 * Filters the DISTINCT clause of the query.
3378
			 *
3379
			 * @since 2.1.0
3380
			 *
3381
			 * @param string   $distinct The DISTINCT clause of the query.
3382
			 * @param WP_Query &$this    The WP_Query instance (passed by reference).
3383
			 */
3384
			$distinct = apply_filters_ref_array( 'posts_distinct', array( $distinct, &$this ) );
3385
3386
			/**
3387
			 * Filters the LIMIT clause of the query.
3388
			 *
3389
			 * @since 2.1.0
3390
			 *
3391
			 * @param string   $limits The LIMIT clause of the query.
3392
			 * @param WP_Query &$this  The WP_Query instance (passed by reference).
3393
			 */
3394
			$limits = apply_filters_ref_array( 'post_limits', array( $limits, &$this ) );
3395
3396
			/**
3397
			 * Filters the SELECT clause of the query.
3398
			 *
3399
			 * @since 2.1.0
3400
			 *
3401
			 * @param string   $fields The SELECT clause of the query.
3402
			 * @param WP_Query &$this  The WP_Query instance (passed by reference).
3403
			 */
3404
			$fields = apply_filters_ref_array( 'posts_fields', array( $fields, &$this ) );
3405
3406
			/**
3407
			 * Filters all query clauses at once, for convenience.
3408
			 *
3409
			 * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
3410
			 * fields (SELECT), and LIMITS clauses.
3411
			 *
3412
			 * @since 3.1.0
3413
			 *
3414
			 * @param array    $clauses The list of clauses for the query.
3415
			 * @param WP_Query &$this   The WP_Query instance (passed by reference).
3416
			 */
3417
			$clauses = (array) apply_filters_ref_array( 'posts_clauses', array( compact( $pieces ), &$this ) );
3418
3419
			$where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
3420
			$groupby = isset( $clauses[ 'groupby' ] ) ? $clauses[ 'groupby' ] : '';
3421
			$join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
3422
			$orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
3423
			$distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
3424
			$fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
3425
			$limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
3426
		}
3427
3428
		/**
3429
		 * Fires to announce the query's current selection parameters.
3430
		 *
3431
		 * For use by caching plugins.
3432
		 *
3433
		 * @since 2.3.0
3434
		 *
3435
		 * @param string $selection The assembled selection query.
3436
		 */
3437
		do_action( 'posts_selection', $where . $groupby . $orderby . $limits . $join );
3438
3439
		/*
3440
		 * Filters again for the benefit of caching plugins.
3441
		 * Regular plugins should use the hooks above.
3442
		 */
3443 View Code Duplication
		if ( !$q['suppress_filters'] ) {
3444
			/**
3445
			 * Filters the WHERE clause of the query.
3446
			 *
3447
			 * For use by caching plugins.
3448
			 *
3449
			 * @since 2.5.0
3450
			 *
3451
			 * @param string   $where The WHERE clause of the query.
3452
			 * @param WP_Query &$this The WP_Query instance (passed by reference).
3453
			 */
3454
			$where = apply_filters_ref_array( 'posts_where_request', array( $where, &$this ) );
3455
3456
			/**
3457
			 * Filters the GROUP BY clause of the query.
3458
			 *
3459
			 * For use by caching plugins.
3460
			 *
3461
			 * @since 2.5.0
3462
			 *
3463
			 * @param string   $groupby The GROUP BY clause of the query.
3464
			 * @param WP_Query &$this   The WP_Query instance (passed by reference).
3465
			 */
3466
			$groupby = apply_filters_ref_array( 'posts_groupby_request', array( $groupby, &$this ) );
3467
3468
			/**
3469
			 * Filters the JOIN clause of the query.
3470
			 *
3471
			 * For use by caching plugins.
3472
			 *
3473
			 * @since 2.5.0
3474
			 *
3475
			 * @param string   $join  The JOIN clause of the query.
3476
			 * @param WP_Query &$this The WP_Query instance (passed by reference).
3477
			 */
3478
			$join = apply_filters_ref_array( 'posts_join_request', array( $join, &$this ) );
3479
3480
			/**
3481
			 * Filters the ORDER BY clause of the query.
3482
			 *
3483
			 * For use by caching plugins.
3484
			 *
3485
			 * @since 2.5.0
3486
			 *
3487
			 * @param string   $orderby The ORDER BY clause of the query.
3488
			 * @param WP_Query &$this   The WP_Query instance (passed by reference).
3489
			 */
3490
			$orderby = apply_filters_ref_array( 'posts_orderby_request', array( $orderby, &$this ) );
3491
3492
			/**
3493
			 * Filters the DISTINCT clause of the query.
3494
			 *
3495
			 * For use by caching plugins.
3496
			 *
3497
			 * @since 2.5.0
3498
			 *
3499
			 * @param string   $distinct The DISTINCT clause of the query.
3500
			 * @param WP_Query &$this    The WP_Query instance (passed by reference).
3501
			 */
3502
			$distinct = apply_filters_ref_array( 'posts_distinct_request', array( $distinct, &$this ) );
3503
3504
			/**
3505
			 * Filters the SELECT clause of the query.
3506
			 *
3507
			 * For use by caching plugins.
3508
			 *
3509
			 * @since 2.5.0
3510
			 *
3511
			 * @param string   $fields The SELECT clause of the query.
3512
			 * @param WP_Query &$this  The WP_Query instance (passed by reference).
3513
			 */
3514
			$fields = apply_filters_ref_array( 'posts_fields_request', array( $fields, &$this ) );
3515
3516
			/**
3517
			 * Filters the LIMIT clause of the query.
3518
			 *
3519
			 * For use by caching plugins.
3520
			 *
3521
			 * @since 2.5.0
3522
			 *
3523
			 * @param string   $limits The LIMIT clause of the query.
3524
			 * @param WP_Query &$this  The WP_Query instance (passed by reference).
3525
			 */
3526
			$limits = apply_filters_ref_array( 'post_limits_request', array( $limits, &$this ) );
3527
3528
			/**
3529
			 * Filters all query clauses at once, for convenience.
3530
			 *
3531
			 * For use by caching plugins.
3532
			 *
3533
			 * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
3534
			 * fields (SELECT), and LIMITS clauses.
3535
			 *
3536
			 * @since 3.1.0
3537
			 *
3538
			 * @param array    $pieces The pieces of the query.
3539
			 * @param WP_Query &$this  The WP_Query instance (passed by reference).
3540
			 */
3541
			$clauses = (array) apply_filters_ref_array( 'posts_clauses_request', array( compact( $pieces ), &$this ) );
3542
3543
			$where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
3544
			$groupby = isset( $clauses[ 'groupby' ] ) ? $clauses[ 'groupby' ] : '';
3545
			$join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
3546
			$orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
3547
			$distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
3548
			$fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
3549
			$limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
3550
		}
3551
3552
		if ( ! empty($groupby) )
3553
			$groupby = 'GROUP BY ' . $groupby;
3554
		if ( !empty( $orderby ) )
3555
			$orderby = 'ORDER BY ' . $orderby;
3556
3557
		$found_rows = '';
3558
		if ( !$q['no_found_rows'] && !empty($limits) )
3559
			$found_rows = 'SQL_CALC_FOUND_ROWS';
3560
3561
		$this->request = $old_request = "SELECT $found_rows $distinct $fields FROM {$this->db->posts} $join WHERE 1=1 $where $groupby $orderby $limits";
3562
3563
		if ( !$q['suppress_filters'] ) {
3564
			/**
3565
			 * Filters the completed SQL query before sending.
3566
			 *
3567
			 * @since 2.0.0
3568
			 *
3569
			 * @param string   $request The complete SQL query.
3570
			 * @param WP_Query &$this   The WP_Query instance (passed by reference).
3571
			 */
3572
			$this->request = apply_filters_ref_array( 'posts_request', array( $this->request, &$this ) );
3573
		}
3574
3575
		/**
3576
		 * Filters the posts array before the query takes place.
3577
		 *
3578
		 * Return a non-null value to bypass WordPress's default post queries.
3579
		 *
3580
		 * Filtering functions that require pagination information are encouraged to set
3581
		 * the `found_posts` and `max_num_pages` properties of the WP_Query object,
3582
		 * passed to the filter by reference. If WP_Query does not perform a database
3583
		 * query, it will not have enough information to generate these values itself.
3584
		 *
3585
		 * @since 4.6.0
3586
		 *
3587
		 * @param array|null $posts Return an array of post data to short-circuit WP's query,
3588
		 *                          or null to allow WP to run its normal queries.
3589
		 * @param WP_Query   $this  The WP_Query instance, passed by reference.
3590
		 */
3591
		$this->posts = apply_filters_ref_array( 'posts_pre_query', array( null, &$this ) );
0 ignored issues
show
Documentation Bug introduced by
It seems like apply_filters_ref_array(...', array(null, &$this)) of type * is incompatible with the declared type array of property $posts.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
3592
3593
		if ( 'ids' == $q['fields'] ) {
3594
			if ( null === $this->posts ) {
3595
				$this->posts = $this->db->get_col( $this->request );
3596
			}
3597
3598
			$this->posts = array_map( 'intval', $this->posts );
3599
			$this->post_count = count( $this->posts );
3600
			$this->set_found_posts( $q, $limits );
3601
3602
			return $this->posts;
3603
		}
3604
3605
		if ( 'id=>parent' == $q['fields'] ) {
3606
			if ( null === $this->posts ) {
3607
				$this->posts = $this->db->get_results( $this->request );
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->get_results($this->request) can be null. However, the property $posts is declared as array. Maybe change the type of the property to array|null or add a type check?

Our type inference engine has found an assignment of a scalar value (like a string, an integer or null) to a property which is an array.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property.

To type hint that a parameter can be either an array or null, you can set a type hint of array and a default value of null. The PHP interpreter will then accept both an array or null for that parameter.

function aContainsB(array $needle = null, array  $haystack) {
    if (!$needle) {
        return false;
    }

    return array_intersect($haystack, $needle) == $haystack;
}

The function can be called with either null or an array for the parameter $needle but will only accept an array as $haystack.

Loading history...
3608
			}
3609
3610
			$this->post_count = count( $this->posts );
3611
			$this->set_found_posts( $q, $limits );
3612
3613
			$r = array();
3614
			foreach ( $this->posts as $key => $post ) {
3615
				$this->posts[ $key ]->ID = (int) $post->ID;
3616
				$this->posts[ $key ]->post_parent = (int) $post->post_parent;
3617
3618
				$r[ (int) $post->ID ] = (int) $post->post_parent;
3619
			}
3620
3621
			return $r;
3622
		}
3623
3624
		if ( null === $this->posts ) {
3625
			$split_the_query = ( $old_request == $this->request && "{$this->db->posts}.*" == $fields && !empty( $limits ) && $q['posts_per_page'] < 500 );
3626
3627
			/**
3628
			 * Filters whether to split the query.
3629
			 *
3630
			 * Splitting the query will cause it to fetch just the IDs of the found posts
3631
			 * (and then individually fetch each post by ID), rather than fetching every
3632
			 * complete row at once. One massive result vs. many small results.
3633
			 *
3634
			 * @since 3.4.0
3635
			 *
3636
			 * @param bool     $split_the_query Whether or not to split the query.
3637
			 * @param WP_Query $this            The WP_Query instance.
3638
			 */
3639
			$split_the_query = apply_filters( 'split_the_query', $split_the_query, $this );
3640
3641
			if ( $split_the_query ) {
3642
				// First get the IDs and then fill in the objects
3643
3644
				$this->request = "SELECT $found_rows $distinct {$this->db->posts}.ID FROM {$this->db->posts} $join WHERE 1=1 $where $groupby $orderby $limits";
3645
3646
				/**
3647
				 * Filters the Post IDs SQL request before sending.
3648
				 *
3649
				 * @since 3.4.0
3650
				 *
3651
				 * @param string   $request The post ID request.
3652
				 * @param WP_Query $this    The WP_Query instance.
3653
				 */
3654
				$this->request = apply_filters( 'posts_request_ids', $this->request, $this );
3655
3656
				$ids = $this->db->get_col( $this->request );
3657
3658
				if ( $ids ) {
3659
					$this->posts = $ids;
3660
					$this->set_found_posts( $q, $limits );
3661
					_prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
3662
				} else {
3663
					$this->posts = array();
3664
				}
3665
			} else {
3666
				$this->posts = $this->db->get_results( $this->request );
3667
				$this->set_found_posts( $q, $limits );
3668
			}
3669
		}
3670
3671
		// Convert to WP_Post objects.
3672
		if ( $this->posts ) {
3673
			$this->posts = array_map( 'get_post', $this->posts );
3674
		}
3675
3676 View Code Duplication
		if ( ! $q['suppress_filters'] ) {
3677
			/**
3678
			 * Filters the raw post results array, prior to status checks.
3679
			 *
3680
			 * @since 2.3.0
3681
			 *
3682
			 * @param array    $posts The post results array.
3683
			 * @param WP_Query &$this The WP_Query instance (passed by reference).
3684
			 */
3685
			$this->posts = apply_filters_ref_array( 'posts_results', array( $this->posts, &$this ) );
0 ignored issues
show
Documentation Bug introduced by
It seems like apply_filters_ref_array(...($this->posts, &$this)) of type * is incompatible with the declared type array of property $posts.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
3686
		}
3687
3688
		if ( !empty($this->posts) && $this->is_comment_feed && $this->is_singular ) {
3689
			/** This filter is documented in wp-includes/query.php */
3690
			$cjoin = apply_filters_ref_array( 'comment_feed_join', array( '', &$this ) );
3691
3692
			/** This filter is documented in wp-includes/query.php */
3693
			$cwhere = apply_filters_ref_array( 'comment_feed_where', array( "WHERE comment_post_ID = '{$this->posts[0]->ID}' AND comment_approved = '1'", &$this ) );
3694
3695
			/** This filter is documented in wp-includes/query.php */
3696
			$cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( '', &$this ) );
3697
			$cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
3698
3699
			/** This filter is documented in wp-includes/query.php */
3700
			$corderby = apply_filters_ref_array( 'comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
3701
			$corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
3702
3703
			/** This filter is documented in wp-includes/query.php */
3704
			$climits = apply_filters_ref_array( 'comment_feed_limits', array( 'LIMIT ' . get_option('posts_per_rss'), &$this ) );
3705
3706
			$comments_request = "SELECT {$this->db->comments}.* FROM {$this->db->comments} $cjoin $cwhere $cgroupby $corderby $climits";
3707
			$comments = $this->db->get_results($comments_request);
3708
			// Convert to WP_Comment
3709
			$this->comments = array_map( 'get_comment', $comments );
3710
			$this->comment_count = count($this->comments);
3711
		}
3712
3713
		// Check post status to determine if post should be displayed.
3714
		if ( !empty($this->posts) && ($this->is_single || $this->is_page) ) {
3715
			$status = get_post_status($this->posts[0]);
3716
			if ( 'attachment' === $this->posts[0]->post_type && 0 === (int) $this->posts[0]->post_parent ) {
3717
				$this->is_page = false;
3718
				$this->is_single = true;
3719
				$this->is_attachment = true;
3720
			}
3721
			$post_status_obj = get_post_status_object($status);
0 ignored issues
show
It seems like $status defined by get_post_status($this->posts[0]) on line 3715 can also be of type false; however, get_post_status_object() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
3722
3723
			// If the post_status was specifically requested, let it pass through.
3724
			if ( !$post_status_obj->public && ! in_array( $status, $q_status ) ) {
3725
3726
				if ( ! is_user_logged_in() ) {
3727
					// User must be logged in to view unpublished posts.
3728
					$this->posts = array();
3729
				} else {
3730
					if  ( $post_status_obj->protected ) {
3731
						// User must have edit permissions on the draft to preview.
3732
						if ( ! current_user_can($edit_cap, $this->posts[0]->ID) ) {
3733
							$this->posts = array();
3734
						} else {
3735
							$this->is_preview = true;
3736
							if ( 'future' != $status )
3737
								$this->posts[0]->post_date = current_time('mysql');
3738
						}
3739
					} elseif ( $post_status_obj->private ) {
3740
						if ( ! current_user_can($read_cap, $this->posts[0]->ID) )
3741
							$this->posts = array();
3742
					} else {
3743
						$this->posts = array();
3744
					}
3745
				}
3746
			}
3747
3748
			if ( $this->is_preview && $this->posts && current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
3749
				/**
3750
				 * Filters the single post for preview mode.
3751
				 *
3752
				 * @since 2.7.0
3753
				 *
3754
				 * @param WP_Post  $post_preview  The Post object.
3755
				 * @param WP_Query &$this         The WP_Query instance (passed by reference).
3756
				 */
3757
				$this->posts[0] = get_post( apply_filters_ref_array( 'the_preview', array( $this->posts[0], &$this ) ) );
3758
			}
3759
		}
3760
3761
		// Put sticky posts at the top of the posts array
3762
		$sticky_posts = get_option('sticky_posts');
3763
		if ( $this->is_home && $page <= 1 && is_array($sticky_posts) && !empty($sticky_posts) && !$q['ignore_sticky_posts'] ) {
3764
			$num_posts = count($this->posts);
3765
			$sticky_offset = 0;
3766
			// Loop over posts and relocate stickies to the front.
3767
			for ( $i = 0; $i < $num_posts; $i++ ) {
3768
				if ( in_array($this->posts[$i]->ID, $sticky_posts) ) {
3769
					$sticky_post = $this->posts[$i];
3770
					// Remove sticky from current position
3771
					array_splice($this->posts, $i, 1);
3772
					// Move to front, after other stickies
3773
					array_splice($this->posts, $sticky_offset, 0, array($sticky_post));
3774
					// Increment the sticky offset. The next sticky will be placed at this offset.
3775
					$sticky_offset++;
3776
					// Remove post from sticky posts array
3777
					$offset = array_search($sticky_post->ID, $sticky_posts);
3778
					unset( $sticky_posts[$offset] );
3779
				}
3780
			}
3781
3782
			// If any posts have been excluded specifically, Ignore those that are sticky.
3783
			if ( !empty($sticky_posts) && !empty($q['post__not_in']) )
3784
				$sticky_posts = array_diff($sticky_posts, $q['post__not_in']);
3785
3786
			// Fetch sticky posts that weren't in the query results
3787
			if ( !empty($sticky_posts) ) {
3788
				$stickies = get_posts( array(
3789
					'post__in' => $sticky_posts,
3790
					'post_type' => $post_type,
3791
					'post_status' => 'publish',
3792
					'nopaging' => true
3793
				) );
3794
3795
				foreach ( $stickies as $sticky_post ) {
3796
					array_splice( $this->posts, $sticky_offset, 0, array( $sticky_post ) );
3797
					$sticky_offset++;
3798
				}
3799
			}
3800
		}
3801
3802
		// If comments have been fetched as part of the query, make sure comment meta lazy-loading is set up.
3803
		if ( ! empty( $this->comments ) ) {
3804
			wp_queue_comments_for_comment_meta_lazyload( $this->comments );
3805
		}
3806
3807 View Code Duplication
		if ( ! $q['suppress_filters'] ) {
3808
			/**
3809
			 * Filters the array of retrieved posts after they've been fetched and
3810
			 * internally processed.
3811
			 *
3812
			 * @since 1.5.0
3813
			 *
3814
			 * @param array    $posts The array of retrieved posts.
3815
			 * @param WP_Query &$this The WP_Query instance (passed by reference).
3816
			 */
3817
			$this->posts = apply_filters_ref_array( 'the_posts', array( $this->posts, &$this ) );
0 ignored issues
show
Documentation Bug introduced by
It seems like apply_filters_ref_array(...($this->posts, &$this)) of type * is incompatible with the declared type array of property $posts.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
3818
		}
3819
3820
		// Ensure that any posts added/modified via one of the filters above are
3821
		// of the type WP_Post and are filtered.
3822
		if ( $this->posts ) {
3823
			$this->post_count = count( $this->posts );
3824
3825
			$this->posts = array_map( 'get_post', $this->posts );
3826
3827
			if ( $q['cache_results'] )
3828
				update_post_caches($this->posts, $post_type, $q['update_post_term_cache'], $q['update_post_meta_cache']);
3829
3830
			$this->post = reset( $this->posts );
3831
		} else {
3832
			$this->post_count = 0;
3833
			$this->posts = array();
3834
		}
3835
3836
		if ( $q['lazy_load_term_meta'] ) {
3837
			wp_queue_posts_for_term_meta_lazyload( $this->posts );
3838
		}
3839
3840
		return $this->posts;
3841
	}
3842
3843
	/**
3844
	 * Set up the amount of found posts and the number of pages (if limit clause was used)
3845
	 * for the current query.
3846
	 *
3847
	 * @since 3.5.0
3848
	 * @access private
3849
	 *
3850
	 * @param array  $q      Query variables.
3851
	 * @param string $limits LIMIT clauses of the query.
3852
	 */
3853
	private function set_found_posts( $q, $limits ) {
3854
		// Bail if posts is an empty array. Continue if posts is an empty string,
3855
		// null, or false to accommodate caching plugins that fill posts later.
3856
		if ( $q['no_found_rows'] || ( is_array( $this->posts ) && ! $this->posts ) )
3857
			return;
3858
3859
		if ( ! empty( $limits ) ) {
3860
			/**
3861
			 * Filters the query to run for retrieving the found posts.
3862
			 *
3863
			 * @since 2.1.0
3864
			 *
3865
			 * @param string   $found_posts The query to run to find the found posts.
3866
			 * @param WP_Query &$this       The WP_Query instance (passed by reference).
3867
			 */
3868
			$this->found_posts = $this->db->get_var( apply_filters_ref_array( 'found_posts_query', array( 'SELECT FOUND_ROWS()', &$this ) ) );
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->get_var(apply...OUND_ROWS()', &$this))) can also be of type string. However, the property $found_posts is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
3869
		} else {
3870
			$this->found_posts = count( $this->posts );
3871
		}
3872
3873
		/**
3874
		 * Filters the number of found posts for the query.
3875
		 *
3876
		 * @since 2.1.0
3877
		 *
3878
		 * @param int      $found_posts The number of posts found.
3879
		 * @param WP_Query &$this       The WP_Query instance (passed by reference).
3880
		 */
3881
		$this->found_posts = apply_filters_ref_array( 'found_posts', array( $this->found_posts, &$this ) );
3882
3883
		if ( ! empty( $limits ) )
3884
			$this->max_num_pages = ceil( $this->found_posts / $q['posts_per_page'] );
0 ignored issues
show
Documentation Bug introduced by
The property $max_num_pages was declared of type integer, but ceil($this->found_posts / $q['posts_per_page']) is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
3885
	}
3886
3887
	/**
3888
	 * Set up the next post and iterate current post index.
3889
	 *
3890
	 * @since 1.5.0
3891
	 * @access public
3892
	 *
3893
	 * @return WP_Post Next post.
3894
	 */
3895
	public function next_post() {
3896
3897
		$this->current_post++;
3898
3899
		$this->post = $this->posts[$this->current_post];
3900
		return $this->post;
3901
	}
3902
3903
	/**
3904
	 * Sets up the current post.
3905
	 *
3906
	 * Retrieves the next post, sets up the post, sets the 'in the loop'
3907
	 * property to true.
3908
	 *
3909
	 * @since 1.5.0
3910
	 * @access public
3911
	 *
3912
	 * @global WP_Post $post
3913
	 */
3914
	public function the_post() {
3915
		global $post;
3916
		$this->in_the_loop = true;
3917
3918
		if ( $this->current_post == -1 ) // loop has just started
3919
			/**
3920
			 * Fires once the loop is started.
3921
			 *
3922
			 * @since 2.0.0
3923
			 *
3924
			 * @param WP_Query &$this The WP_Query instance (passed by reference).
3925
			 */
3926
			do_action_ref_array( 'loop_start', array( &$this ) );
3927
3928
		$post = $this->next_post();
3929
		$this->setup_postdata( $post );
3930
	}
3931
3932
	/**
3933
	 * Determines whether there are more posts available in the loop.
3934
	 *
3935
	 * Calls the {@see 'loop_end'} action when the loop is complete.
3936
	 *
3937
	 * @since 1.5.0
3938
	 * @access public
3939
	 *
3940
	 * @return bool True if posts are available, false if end of loop.
3941
	 */
3942
	public function have_posts() {
3943
		if ( $this->current_post + 1 < $this->post_count ) {
3944
			return true;
3945
		} elseif ( $this->current_post + 1 == $this->post_count && $this->post_count > 0 ) {
3946
			/**
3947
			 * Fires once the loop has ended.
3948
			 *
3949
			 * @since 2.0.0
3950
			 *
3951
			 * @param WP_Query &$this The WP_Query instance (passed by reference).
3952
			 */
3953
			do_action_ref_array( 'loop_end', array( &$this ) );
3954
			// Do some cleaning up after the loop
3955
			$this->rewind_posts();
3956
		}
3957
3958
		$this->in_the_loop = false;
3959
		return false;
3960
	}
3961
3962
	/**
3963
	 * Rewind the posts and reset post index.
3964
	 *
3965
	 * @since 1.5.0
3966
	 * @access public
3967
	 */
3968
	public function rewind_posts() {
3969
		$this->current_post = -1;
3970
		if ( $this->post_count > 0 ) {
3971
			$this->post = $this->posts[0];
3972
		}
3973
	}
3974
3975
	/**
3976
	 * Iterate current comment index and return WP_Comment object.
3977
	 *
3978
	 * @since 2.2.0
3979
	 * @access public
3980
	 *
3981
	 * @return WP_Comment Comment object.
3982
	 */
3983
	public function next_comment() {
3984
		$this->current_comment++;
3985
3986
		$this->comment = $this->comments[$this->current_comment];
3987
		return $this->comment;
3988
	}
3989
3990
	/**
3991
	 * Sets up the current comment.
3992
	 *
3993
	 * @since 2.2.0
3994
	 * @access public
3995
	 * @global WP_Comment $comment Current comment.
3996
	 */
3997
	public function the_comment() {
3998
		global $comment;
3999
4000
		$comment = $this->next_comment();
4001
4002
		if ( $this->current_comment == 0 ) {
4003
			/**
4004
			 * Fires once the comment loop is started.
4005
			 *
4006
			 * @since 2.2.0
4007
			 */
4008
			do_action( 'comment_loop_start' );
4009
		}
4010
	}
4011
4012
	/**
4013
	 * Whether there are more comments available.
4014
	 *
4015
	 * Automatically rewinds comments when finished.
4016
	 *
4017
	 * @since 2.2.0
4018
	 * @access public
4019
	 *
4020
	 * @return bool True, if more comments. False, if no more posts.
4021
	 */
4022
	public function have_comments() {
4023
		if ( $this->current_comment + 1 < $this->comment_count ) {
4024
			return true;
4025
		} elseif ( $this->current_comment + 1 == $this->comment_count ) {
4026
			$this->rewind_comments();
4027
		}
4028
4029
		return false;
4030
	}
4031
4032
	/**
4033
	 * Rewind the comments, resets the comment index and comment to first.
4034
	 *
4035
	 * @since 2.2.0
4036
	 * @access public
4037
	 */
4038
	public function rewind_comments() {
4039
		$this->current_comment = -1;
4040
		if ( $this->comment_count > 0 ) {
4041
			$this->comment = $this->comments[0];
4042
		}
4043
	}
4044
4045
	/**
4046
	 * Sets up the WordPress query by parsing query string.
4047
	 *
4048
	 * @since 1.5.0
4049
	 * @access public
4050
	 *
4051
	 * @param string $query URL query string.
4052
	 * @return array List of posts.
4053
	 */
4054
	public function query( $query ) {
4055
		$this->init();
4056
		$this->query = $this->query_vars = wp_parse_args( $query );
4057
		return $this->get_posts();
4058
	}
4059
4060
	/**
4061
	 * Retrieve queried object.
4062
	 *
4063
	 * If queried object is not set, then the queried object will be set from
4064
	 * the category, tag, taxonomy, posts page, single post, page, or author
4065
	 * query variable. After it is set up, it will be returned.
4066
	 *
4067
	 * @since 1.5.0
4068
	 * @access public
4069
	 *
4070
	 * @return object
4071
	 */
4072
	public function get_queried_object() {
4073
		if ( isset($this->queried_object) )
4074
			return $this->queried_object;
4075
4076
		$this->queried_object = null;
4077
		$this->queried_object_id = null;
4078
4079
		if ( $this->is_category || $this->is_tag || $this->is_tax ) {
4080
			if ( $this->is_category ) {
4081
				if ( $this->get( 'cat' ) ) {
4082
					$term = get_term( $this->get( 'cat' ), 'category' );
4083
				} elseif ( $this->get( 'category_name' ) ) {
4084
					$term = get_term_by( 'slug', $this->get( 'category_name' ), 'category' );
4085
				}
4086
			} elseif ( $this->is_tag ) {
4087
				if ( $this->get( 'tag_id' ) ) {
4088
					$term = get_term( $this->get( 'tag_id' ), 'post_tag' );
4089
				} elseif ( $this->get( 'tag' ) ) {
4090
					$term = get_term_by( 'slug', $this->get( 'tag' ), 'post_tag' );
4091
				}
4092
			} else {
4093
				// For other tax queries, grab the first term from the first clause.
4094
				$tax_query_in_and = wp_list_filter( $this->tax_query->queried_terms, array( 'operator' => 'NOT IN' ), 'NOT' );
4095
4096
				if ( ! empty( $tax_query_in_and ) ) {
4097
					$queried_taxonomies = array_keys( $tax_query_in_and );
4098
					$matched_taxonomy = reset( $queried_taxonomies );
4099
					$query = $tax_query_in_and[ $matched_taxonomy ];
4100
4101
					if ( $query['terms'] ) {
4102
						if ( 'term_id' == $query['field'] ) {
4103
							$term = get_term( reset( $query['terms'] ), $matched_taxonomy );
4104
						} else {
4105
							$term = get_term_by( $query['field'], reset( $query['terms'] ), $matched_taxonomy );
4106
						}
4107
					}
4108
				}
4109
			}
4110
4111
			if ( ! empty( $term ) && ! is_wp_error( $term ) )  {
4112
				$this->queried_object = $term;
4113
				$this->queried_object_id = (int) $term->term_id;
4114
4115
				if ( $this->is_category && 'category' === $this->queried_object->taxonomy )
4116
					_make_cat_compat( $this->queried_object );
4117
			}
4118
		} elseif ( $this->is_post_type_archive ) {
4119
			$post_type = $this->get( 'post_type' );
4120
			if ( is_array( $post_type ) )
4121
				$post_type = reset( $post_type );
4122
			$this->queried_object = get_post_type_object( $post_type );
4123
		} elseif ( $this->is_posts_page ) {
4124
			$page_for_posts = get_option('page_for_posts');
4125
			$this->queried_object = get_post( $page_for_posts );
4126
			$this->queried_object_id = (int) $this->queried_object->ID;
4127
		} elseif ( $this->is_singular && ! empty( $this->post ) ) {
4128
			$this->queried_object = $this->post;
4129
			$this->queried_object_id = (int) $this->post->ID;
4130
		} elseif ( $this->is_author ) {
4131
			$this->queried_object_id = (int) $this->get('author');
4132
			$this->queried_object = get_userdata( $this->queried_object_id );
0 ignored issues
show
Documentation Bug introduced by
It seems like get_userdata($this->queried_object_id) can also be of type false. However, the property $queried_object is declared as type object|array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
4133
		}
4134
4135
		return $this->queried_object;
4136
	}
4137
4138
	/**
4139
	 * Retrieve ID of the current queried object.
4140
	 *
4141
	 * @since 1.5.0
4142
	 * @access public
4143
	 *
4144
	 * @return int
4145
	 */
4146
	public function get_queried_object_id() {
4147
		$this->get_queried_object();
4148
4149
		if ( isset($this->queried_object_id) ) {
4150
			return $this->queried_object_id;
4151
		}
4152
4153
		return 0;
4154
	}
4155
4156
	/**
4157
	 * Constructor.
4158
	 *
4159
	 * Sets up the WordPress query, if parameter is not empty.
4160
	 *
4161
	 * @since 1.5.0
4162
	 * @access public
4163
	 *
4164
	 * @param string|array $query URL query string or array of vars.
4165
	 */
4166
	public function __construct( $query = '' ) {
4167
		$this->db = $GLOBALS['wpdb'];
4168
4169
		if ( ! empty( $query ) ) {
4170
			$this->query( $query );
0 ignored issues
show
It seems like $query defined by parameter $query on line 4166 can also be of type array; however, WP_Query::query() does only seem to accept string, maybe add an additional type check?

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.

Loading history...
4171
		}
4172
	}
4173
4174
	/**
4175
	 * Make private properties readable for backward compatibility.
4176
	 *
4177
	 * @since 4.0.0
4178
	 * @access public
4179
	 *
4180
	 * @param string $name Property to get.
4181
	 * @return mixed Property.
4182
	 */
4183
	public function __get( $name ) {
4184
		if ( in_array( $name, $this->compat_fields ) ) {
4185
			return $this->$name;
4186
		}
4187
	}
4188
4189
	/**
4190
	 * Make private properties checkable for backward compatibility.
4191
	 *
4192
	 * @since 4.0.0
4193
	 * @access public
4194
	 *
4195
	 * @param string $name Property to check if set.
4196
	 * @return bool Whether the property is set.
4197
	 */
4198
	public function __isset( $name ) {
4199
		if ( in_array( $name, $this->compat_fields ) ) {
4200
			return isset( $this->$name );
4201
		}
4202
	}
4203
4204
	/**
4205
	 * Make private/protected methods readable for backward compatibility.
4206
	 *
4207
	 * @since 4.0.0
4208
	 * @access public
4209
	 *
4210
	 * @param callable $name      Method to call.
4211
	 * @param array    $arguments Arguments to pass when calling.
4212
	 * @return mixed|false Return value of the callback, false otherwise.
4213
	 */
4214
	public function __call( $name, $arguments ) {
4215
		if ( in_array( $name, $this->compat_methods ) ) {
4216
			return call_user_func_array( array( $this, $name ), $arguments );
4217
		}
4218
		return false;
4219
	}
4220
4221
	/**
4222
 	 * Is the query for an existing archive page?
4223
 	 *
4224
 	 * Month, Year, Category, Author, Post Type archive...
4225
	 *
4226
 	 * @since 3.1.0
4227
 	 *
4228
 	 * @return bool
4229
 	 */
4230
	public function is_archive() {
4231
		return (bool) $this->is_archive;
4232
	}
4233
4234
	/**
4235
	 * Is the query for an existing post type archive page?
4236
	 *
4237
	 * @since 3.1.0
4238
	 *
4239
	 * @param mixed $post_types Optional. Post type or array of posts types to check against.
4240
	 * @return bool
4241
	 */
4242
	public function is_post_type_archive( $post_types = '' ) {
4243
		if ( empty( $post_types ) || ! $this->is_post_type_archive )
4244
			return (bool) $this->is_post_type_archive;
4245
4246
		$post_type = $this->get( 'post_type' );
4247
		if ( is_array( $post_type ) )
4248
			$post_type = reset( $post_type );
4249
		$post_type_object = get_post_type_object( $post_type );
4250
4251
		return in_array( $post_type_object->name, (array) $post_types );
4252
	}
4253
4254
	/**
4255
	 * Is the query for an existing attachment page?
4256
	 *
4257
	 * @since 3.1.0
4258
	 *
4259
	 * @param mixed $attachment Attachment ID, title, slug, or array of such.
4260
	 * @return bool
4261
	 */
4262 View Code Duplication
	public function is_attachment( $attachment = '' ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4263
		if ( ! $this->is_attachment ) {
4264
			return false;
4265
		}
4266
4267
		if ( empty( $attachment ) ) {
4268
			return true;
4269
		}
4270
4271
		$attachment = array_map( 'strval', (array) $attachment );
4272
4273
		$post_obj = $this->get_queried_object();
4274
4275
		if ( in_array( (string) $post_obj->ID, $attachment ) ) {
4276
			return true;
4277
		} elseif ( in_array( $post_obj->post_title, $attachment ) ) {
4278
			return true;
4279
		} elseif ( in_array( $post_obj->post_name, $attachment ) ) {
4280
			return true;
4281
		}
4282
		return false;
4283
	}
4284
4285
	/**
4286
	 * Is the query for an existing author archive page?
4287
	 *
4288
	 * If the $author parameter is specified, this function will additionally
4289
	 * check if the query is for one of the authors specified.
4290
	 *
4291
	 * @since 3.1.0
4292
	 *
4293
	 * @param mixed $author Optional. User ID, nickname, nicename, or array of User IDs, nicknames, and nicenames
4294
	 * @return bool
4295
	 */
4296 View Code Duplication
	public function is_author( $author = '' ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4297
		if ( !$this->is_author )
4298
			return false;
4299
4300
		if ( empty($author) )
4301
			return true;
4302
4303
		$author_obj = $this->get_queried_object();
4304
4305
		$author = array_map( 'strval', (array) $author );
4306
4307
		if ( in_array( (string) $author_obj->ID, $author ) )
4308
			return true;
4309
		elseif ( in_array( $author_obj->nickname, $author ) )
4310
			return true;
4311
		elseif ( in_array( $author_obj->user_nicename, $author ) )
4312
			return true;
4313
4314
		return false;
4315
	}
4316
4317
	/**
4318
	 * Is the query for an existing category archive page?
4319
	 *
4320
	 * If the $category parameter is specified, this function will additionally
4321
	 * check if the query is for one of the categories specified.
4322
	 *
4323
	 * @since 3.1.0
4324
	 *
4325
	 * @param mixed $category Optional. Category ID, name, slug, or array of Category IDs, names, and slugs.
4326
	 * @return bool
4327
	 */
4328 View Code Duplication
	public function is_category( $category = '' ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4329
		if ( !$this->is_category )
4330
			return false;
4331
4332
		if ( empty($category) )
4333
			return true;
4334
4335
		$cat_obj = $this->get_queried_object();
4336
4337
		$category = array_map( 'strval', (array) $category );
4338
4339
		if ( in_array( (string) $cat_obj->term_id, $category ) )
4340
			return true;
4341
		elseif ( in_array( $cat_obj->name, $category ) )
4342
			return true;
4343
		elseif ( in_array( $cat_obj->slug, $category ) )
4344
			return true;
4345
4346
		return false;
4347
	}
4348
4349
	/**
4350
	 * Is the query for an existing tag archive page?
4351
	 *
4352
	 * If the $tag parameter is specified, this function will additionally
4353
	 * check if the query is for one of the tags specified.
4354
	 *
4355
	 * @since 3.1.0
4356
	 *
4357
	 * @param mixed $tag Optional. Tag ID, name, slug, or array of Tag IDs, names, and slugs.
4358
	 * @return bool
4359
	 */
4360 View Code Duplication
	public function is_tag( $tag = '' ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4361
		if ( ! $this->is_tag )
4362
			return false;
4363
4364
		if ( empty( $tag ) )
4365
			return true;
4366
4367
		$tag_obj = $this->get_queried_object();
4368
4369
		$tag = array_map( 'strval', (array) $tag );
4370
4371
		if ( in_array( (string) $tag_obj->term_id, $tag ) )
4372
			return true;
4373
		elseif ( in_array( $tag_obj->name, $tag ) )
4374
			return true;
4375
		elseif ( in_array( $tag_obj->slug, $tag ) )
4376
			return true;
4377
4378
		return false;
4379
	}
4380
4381
	/**
4382
	 * Is the query for an existing custom taxonomy archive page?
4383
	 *
4384
	 * If the $taxonomy parameter is specified, this function will additionally
4385
	 * check if the query is for that specific $taxonomy.
4386
	 *
4387
	 * If the $term parameter is specified in addition to the $taxonomy parameter,
4388
	 * this function will additionally check if the query is for one of the terms
4389
	 * specified.
4390
	 *
4391
	 * @since 3.1.0
4392
	 *
4393
	 * @global array $wp_taxonomies
4394
	 *
4395
	 * @param mixed $taxonomy Optional. Taxonomy slug or slugs.
4396
	 * @param mixed $term     Optional. Term ID, name, slug or array of Term IDs, names, and slugs.
4397
	 * @return bool True for custom taxonomy archive pages, false for built-in taxonomies (category and tag archives).
4398
	 */
4399
	public function is_tax( $taxonomy = '', $term = '' ) {
4400
		global $wp_taxonomies;
4401
4402
		if ( !$this->is_tax )
4403
			return false;
4404
4405
		if ( empty( $taxonomy ) )
4406
			return true;
4407
4408
		$queried_object = $this->get_queried_object();
4409
		$tax_array = array_intersect( array_keys( $wp_taxonomies ), (array) $taxonomy );
4410
		$term_array = (array) $term;
4411
4412
		// Check that the taxonomy matches.
4413
		if ( ! ( isset( $queried_object->taxonomy ) && count( $tax_array ) && in_array( $queried_object->taxonomy, $tax_array ) ) )
4414
			return false;
4415
4416
		// Only a Taxonomy provided.
4417
		if ( empty( $term ) )
4418
			return true;
4419
4420
		return isset( $queried_object->term_id ) &&
4421
			count( array_intersect(
4422
				array( $queried_object->term_id, $queried_object->name, $queried_object->slug ),
4423
				$term_array
4424
			) );
4425
	}
4426
4427
	/**
4428
	 * Whether the current URL is within the comments popup window.
4429
	 *
4430
	 * @since 3.1.0
4431
	 * @deprecated 4.5.0
4432
	 *
4433
	 * @return bool
4434
	 */
4435
	public function is_comments_popup() {
4436
		_deprecated_function( __FUNCTION__, '4.5.0' );
4437
4438
		return false;
4439
	}
4440
4441
	/**
4442
	 * Is the query for an existing date archive?
4443
	 *
4444
	 * @since 3.1.0
4445
	 *
4446
	 * @return bool
4447
	 */
4448
	public function is_date() {
4449
		return (bool) $this->is_date;
4450
	}
4451
4452
	/**
4453
	 * Is the query for an existing day archive?
4454
	 *
4455
	 * @since 3.1.0
4456
	 *
4457
	 * @return bool
4458
	 */
4459
	public function is_day() {
4460
		return (bool) $this->is_day;
4461
	}
4462
4463
	/**
4464
	 * Is the query for a feed?
4465
	 *
4466
	 * @since 3.1.0
4467
	 *
4468
	 * @param string|array $feeds Optional feed types to check.
4469
	 * @return bool
4470
	 */
4471
	public function is_feed( $feeds = '' ) {
4472
		if ( empty( $feeds ) || ! $this->is_feed )
4473
			return (bool) $this->is_feed;
4474
		$qv = $this->get( 'feed' );
4475
		if ( 'feed' == $qv )
4476
			$qv = get_default_feed();
4477
		return in_array( $qv, (array) $feeds );
4478
	}
4479
4480
	/**
4481
	 * Is the query for a comments feed?
4482
	 *
4483
	 * @since 3.1.0
4484
	 *
4485
	 * @return bool
4486
	 */
4487
	public function is_comment_feed() {
4488
		return (bool) $this->is_comment_feed;
4489
	}
4490
4491
	/**
4492
	 * Is the query for the front page of the site?
4493
	 *
4494
	 * This is for what is displayed at your site's main URL.
4495
	 *
4496
	 * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_on_front'.
4497
	 *
4498
	 * If you set a static page for the front page of your site, this function will return
4499
	 * true when viewing that page.
4500
	 *
4501
	 * Otherwise the same as @see WP_Query::is_home()
4502
	 *
4503
	 * @since 3.1.0
4504
	 *
4505
	 * @return bool True, if front of site.
4506
	 */
4507
	public function is_front_page() {
4508
		// most likely case
4509
		if ( 'posts' == get_option( 'show_on_front') && $this->is_home() )
4510
			return true;
4511
		elseif ( 'page' == get_option( 'show_on_front') && get_option( 'page_on_front' ) && $this->is_page( get_option( 'page_on_front' ) ) )
4512
			return true;
4513
		else
4514
			return false;
4515
	}
4516
4517
	/**
4518
	 * Is the query for the blog homepage?
4519
	 *
4520
	 * This is the page which shows the time based blog content of your site.
4521
	 *
4522
	 * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_for_posts'.
4523
	 *
4524
	 * If you set a static page for the front page of your site, this function will return
4525
	 * true only on the page you set as the "Posts page".
4526
	 *
4527
	 * @see WP_Query::is_front_page()
4528
	 *
4529
	 * @since 3.1.0
4530
	 *
4531
	 * @return bool True if blog view homepage.
4532
	 */
4533
	public function is_home() {
4534
		return (bool) $this->is_home;
4535
	}
4536
4537
	/**
4538
	 * Is the query for an existing month archive?
4539
	 *
4540
	 * @since 3.1.0
4541
	 *
4542
	 * @return bool
4543
	 */
4544
	public function is_month() {
4545
		return (bool) $this->is_month;
4546
	}
4547
4548
	/**
4549
	 * Is the query for an existing single page?
4550
	 *
4551
	 * If the $page parameter is specified, this function will additionally
4552
	 * check if the query is for one of the pages specified.
4553
	 *
4554
	 * @see WP_Query::is_single()
4555
	 * @see WP_Query::is_singular()
4556
	 *
4557
	 * @since 3.1.0
4558
	 *
4559
	 * @param int|string|array $page Optional. Page ID, title, slug, path, or array of such. Default empty.
4560
	 * @return bool Whether the query is for an existing single page.
4561
	 */
4562 View Code Duplication
	public function is_page( $page = '' ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4563
		if ( !$this->is_page )
4564
			return false;
4565
4566
		if ( empty( $page ) )
4567
			return true;
4568
4569
		$page_obj = $this->get_queried_object();
4570
4571
		$page = array_map( 'strval', (array) $page );
4572
4573
		if ( in_array( (string) $page_obj->ID, $page ) ) {
4574
			return true;
4575
		} elseif ( in_array( $page_obj->post_title, $page ) ) {
4576
			return true;
4577
		} elseif ( in_array( $page_obj->post_name, $page ) ) {
4578
			return true;
4579
		} else {
4580
			foreach ( $page as $pagepath ) {
4581
				if ( ! strpos( $pagepath, '/' ) ) {
4582
					continue;
4583
				}
4584
				$pagepath_obj = get_page_by_path( $pagepath );
4585
4586
				if ( $pagepath_obj && ( $pagepath_obj->ID == $page_obj->ID ) ) {
4587
					return true;
4588
				}
4589
			}
4590
		}
4591
4592
		return false;
4593
	}
4594
4595
	/**
4596
	 * Is the query for paged result and not for the first page?
4597
	 *
4598
	 * @since 3.1.0
4599
	 *
4600
	 * @return bool
4601
	 */
4602
	public function is_paged() {
4603
		return (bool) $this->is_paged;
4604
	}
4605
4606
	/**
4607
	 * Is the query for a post or page preview?
4608
	 *
4609
	 * @since 3.1.0
4610
	 *
4611
	 * @return bool
4612
	 */
4613
	public function is_preview() {
4614
		return (bool) $this->is_preview;
4615
	}
4616
4617
	/**
4618
	 * Is the query for the robots file?
4619
	 *
4620
	 * @since 3.1.0
4621
	 *
4622
	 * @return bool
4623
	 */
4624
	public function is_robots() {
4625
		return (bool) $this->is_robots;
4626
	}
4627
4628
	/**
4629
	 * Is the query for a search?
4630
	 *
4631
	 * @since 3.1.0
4632
	 *
4633
	 * @return bool
4634
	 */
4635
	public function is_search() {
4636
		return (bool) $this->is_search;
4637
	}
4638
4639
	/**
4640
	 * Is the query for an existing single post?
4641
	 *
4642
	 * Works for any post type, except attachments and pages
4643
	 *
4644
	 * If the $post parameter is specified, this function will additionally
4645
	 * check if the query is for one of the Posts specified.
4646
	 *
4647
	 * @see WP_Query::is_page()
4648
	 * @see WP_Query::is_singular()
4649
	 *
4650
	 * @since 3.1.0
4651
	 *
4652
	 * @param int|string|array $post Optional. Post ID, title, slug, path, or array of such. Default empty.
4653
	 * @return bool Whether the query is for an existing single post.
4654
	 */
4655 View Code Duplication
	public function is_single( $post = '' ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4656
		if ( !$this->is_single )
4657
			return false;
4658
4659
		if ( empty($post) )
4660
			return true;
4661
4662
		$post_obj = $this->get_queried_object();
4663
4664
		$post = array_map( 'strval', (array) $post );
4665
4666
		if ( in_array( (string) $post_obj->ID, $post ) ) {
4667
			return true;
4668
		} elseif ( in_array( $post_obj->post_title, $post ) ) {
4669
			return true;
4670
		} elseif ( in_array( $post_obj->post_name, $post ) ) {
4671
			return true;
4672
		} else {
4673
			foreach ( $post as $postpath ) {
4674
				if ( ! strpos( $postpath, '/' ) ) {
4675
					continue;
4676
				}
4677
				$postpath_obj = get_page_by_path( $postpath, OBJECT, $post_obj->post_type );
4678
4679
				if ( $postpath_obj && ( $postpath_obj->ID == $post_obj->ID ) ) {
4680
					return true;
4681
				}
4682
			}
4683
		}
4684
		return false;
4685
	}
4686
4687
	/**
4688
	 * Is the query for an existing single post of any post type (post, attachment, page, ... )?
4689
	 *
4690
	 * If the $post_types parameter is specified, this function will additionally
4691
	 * check if the query is for one of the Posts Types specified.
4692
	 *
4693
	 * @see WP_Query::is_page()
4694
	 * @see WP_Query::is_single()
4695
	 *
4696
	 * @since 3.1.0
4697
	 *
4698
	 * @param string|array $post_types Optional. Post type or array of post types. Default empty.
4699
	 * @return bool Whether the query is for an existing single post of any of the given post types.
4700
	 */
4701
	public function is_singular( $post_types = '' ) {
4702
		if ( empty( $post_types ) || !$this->is_singular )
4703
			return (bool) $this->is_singular;
4704
4705
		$post_obj = $this->get_queried_object();
4706
4707
		return in_array( $post_obj->post_type, (array) $post_types );
4708
	}
4709
4710
	/**
4711
	 * Is the query for a specific time?
4712
	 *
4713
	 * @since 3.1.0
4714
	 *
4715
	 * @return bool
4716
	 */
4717
	public function is_time() {
4718
		return (bool) $this->is_time;
4719
	}
4720
4721
	/**
4722
	 * Is the query for a trackback endpoint call?
4723
	 *
4724
	 * @since 3.1.0
4725
	 *
4726
	 * @return bool
4727
	 */
4728
	public function is_trackback() {
4729
		return (bool) $this->is_trackback;
4730
	}
4731
4732
	/**
4733
	 * Is the query for an existing year archive?
4734
	 *
4735
	 * @since 3.1.0
4736
	 *
4737
	 * @return bool
4738
	 */
4739
	public function is_year() {
4740
		return (bool) $this->is_year;
4741
	}
4742
4743
	/**
4744
	 * Is the query a 404 (returns no results)?
4745
	 *
4746
	 * @since 3.1.0
4747
	 *
4748
	 * @return bool
4749
	 */
4750
	public function is_404() {
4751
		return (bool) $this->is_404;
4752
	}
4753
4754
	/**
4755
	 * Is the query for an embedded post?
4756
	 *
4757
	 * @since 4.4.0
4758
	 *
4759
	 * @return bool
4760
	 */
4761
	public function is_embed() {
4762
		return (bool) $this->is_embed;
4763
	}
4764
4765
	/**
4766
	 * Is the query the main query?
4767
	 *
4768
	 * @since 3.3.0
4769
	 *
4770
	 * @global WP_Query $wp_query Global WP_Query instance.
4771
	 *
4772
	 * @return bool
4773
	 */
4774
	public function is_main_query() {
4775
		global $wp_the_query;
4776
		return $wp_the_query === $this;
4777
	}
4778
4779
	/**
4780
	 * Set up global post data.
4781
	 *
4782
	 * @since 4.1.0
4783
	 * @since 4.4.0 Added the ability to pass a post ID to `$post`.
4784
	 *
4785
	 * @global int             $id
4786
	 * @global WP_User         $authordata
4787
	 * @global string|int|bool $currentday
4788
	 * @global string|int|bool $currentmonth
4789
	 * @global int             $page
4790
	 * @global array           $pages
4791
	 * @global int             $multipage
4792
	 * @global int             $more
4793
	 * @global int             $numpages
4794
	 *
4795
	 * @param WP_Post|object|int $post WP_Post instance or Post ID/object.
4796
	 * @return true True when finished.
4797
	 */
4798
	public function setup_postdata( $post ) {
4799
		global $id, $authordata, $currentday, $currentmonth, $page, $pages, $multipage, $more, $numpages;
4800
4801
		if ( ! ( $post instanceof WP_Post ) ) {
4802
			$post = get_post( $post );
0 ignored issues
show
It seems like $post defined by get_post($post) on line 4802 can also be of type object; however, get_post() does only seem to accept integer|object<WP_Post>|null, 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.

Loading history...
4803
		}
4804
4805
		if ( ! $post ) {
4806
			return;
4807
		}
4808
4809
		$id = (int) $post->ID;
4810
4811
		$authordata = get_userdata($post->post_author);
4812
4813
		$currentday = mysql2date('d.m.y', $post->post_date, false);
4814
		$currentmonth = mysql2date('m', $post->post_date, false);
4815
		$numpages = 1;
4816
		$multipage = 0;
4817
		$page = $this->get( 'page' );
4818
		if ( ! $page )
4819
			$page = 1;
4820
4821
		/*
4822
		 * Force full post content when viewing the permalink for the $post,
4823
		 * or when on an RSS feed. Otherwise respect the 'more' tag.
4824
		 */
4825
		if ( $post->ID === get_queried_object_id() && ( $this->is_page() || $this->is_single() ) ) {
4826
			$more = 1;
4827
		} elseif ( $this->is_feed() ) {
4828
			$more = 1;
4829
		} else {
4830
			$more = 0;
4831
		}
4832
4833
		$content = $post->post_content;
4834
		if ( false !== strpos( $content, '<!--nextpage-->' ) ) {
4835
			$content = str_replace( "\n<!--nextpage-->\n", '<!--nextpage-->', $content );
4836
			$content = str_replace( "\n<!--nextpage-->", '<!--nextpage-->', $content );
4837
			$content = str_replace( "<!--nextpage-->\n", '<!--nextpage-->', $content );
4838
4839
			// Ignore nextpage at the beginning of the content.
4840
			if ( 0 === strpos( $content, '<!--nextpage-->' ) )
4841
				$content = substr( $content, 15 );
4842
4843
			$pages = explode('<!--nextpage-->', $content);
4844
		} else {
4845
			$pages = array( $post->post_content );
4846
		}
4847
4848
		/**
4849
		 * Filters the "pages" derived from splitting the post content.
4850
		 *
4851
		 * "Pages" are determined by splitting the post content based on the presence
4852
		 * of `<!-- nextpage -->` tags.
4853
		 *
4854
		 * @since 4.4.0
4855
		 *
4856
		 * @param array   $pages Array of "pages" derived from the post content.
4857
		 *                       of `<!-- nextpage -->` tags..
4858
		 * @param WP_Post $post  Current post object.
4859
		 */
4860
		$pages = apply_filters( 'content_pagination', $pages, $post );
4861
4862
		$numpages = count( $pages );
4863
4864
		if ( $numpages > 1 ) {
4865
			if ( $page > 1 ) {
4866
				$more = 1;
4867
			}
4868
			$multipage = 1;
4869
		} else {
4870
	 		$multipage = 0;
4871
	 	}
4872
4873
		/**
4874
		 * Fires once the post data has been setup.
4875
		 *
4876
		 * @since 2.8.0
4877
		 * @since 4.1.0 Introduced `$this` parameter.
4878
		 *
4879
		 * @param WP_Post  &$post The Post object (passed by reference).
4880
		 * @param WP_Query &$this The current Query object (passed by reference).
4881
		 */
4882
		do_action_ref_array( 'the_post', array( &$post, &$this ) );
4883
4884
		return true;
4885
	}
4886
	/**
4887
	 * After looping through a nested query, this function
4888
	 * restores the $post global to the current post in this query.
4889
	 *
4890
	 * @since 3.7.0
4891
	 *
4892
	 * @global WP_Post $post
4893
	 */
4894
	public function reset_postdata() {
4895
		if ( ! empty( $this->post ) ) {
4896
			$GLOBALS['post'] = $this->post;
4897
			$this->setup_postdata( $this->post );
4898
		}
4899
	}
4900
4901
	/**
4902
	 * Lazyload term meta for posts in the loop.
4903
	 *
4904
	 * @since 4.4.0
4905
	 * @deprecated 4.5.0 See wp_queue_posts_for_term_meta_lazyload().
4906
	 *
4907
	 * @param mixed $check
4908
	 * @param int   $term_id
4909
	 * @return mixed
4910
	 */
4911
	public function lazyload_term_meta( $check, $term_id ) {
4912
		_deprecated_function( __METHOD__, '4.5.0' );
4913
		return $check;
4914
	}
4915
4916
	/**
4917
	 * Lazyload comment meta for comments in the loop.
4918
	 *
4919
	 * @since 4.4.0
4920
	 * @deprecated 4.5.0 See wp_queue_comments_for_comment_meta_lazyload().
4921
	 *
4922
	 * @param mixed $check
4923
	 * @param int   $comment_id
4924
	 * @return mixed
4925
	 */
4926
	public function lazyload_comment_meta( $check, $comment_id ) {
4927
		_deprecated_function( __METHOD__, '4.5.0' );
4928
		return $check;
4929
	}
4930
}
4931
4932
/**
4933
 * Redirect old slugs to the correct permalink.
4934
 *
4935
 * Attempts to find the current slug from the past slugs.
4936
 *
4937
 * @since 2.1.0
4938
 *
4939
 * @global WP_Query   $wp_query   Global WP_Query instance.
4940
 * @global wpdb       $wpdb       WordPress database abstraction object.
4941
 */
4942
function wp_old_slug_redirect() {
4943
	global $wp_query;
4944
4945
	if ( is_404() && '' !== $wp_query->query_vars['name'] ) :
4946
		global $wpdb;
4947
4948
		// Guess the current post_type based on the query vars.
4949
		if ( get_query_var( 'post_type' ) ) {
4950
			$post_type = get_query_var( 'post_type' );
4951
		} elseif ( get_query_var( 'attachment' ) ) {
4952
			$post_type = 'attachment';
4953
		} elseif ( ! empty( $wp_query->query_vars['pagename'] ) ) {
4954
			$post_type = 'page';
4955
		} else {
4956
			$post_type = 'post';
4957
		}
4958
4959
		if ( is_array( $post_type ) ) {
4960
			if ( count( $post_type ) > 1 )
4961
				return;
4962
			$post_type = reset( $post_type );
4963
		}
4964
4965
		// Do not attempt redirect for hierarchical post types
4966
		if ( is_post_type_hierarchical( $post_type ) )
4967
			return;
4968
4969
		$query = $wpdb->prepare("SELECT post_id FROM $wpdb->postmeta, $wpdb->posts WHERE ID = post_id AND post_type = %s AND meta_key = '_wp_old_slug' AND meta_value = %s", $post_type, $wp_query->query_vars['name']);
4970
4971
		// if year, monthnum, or day have been specified, make our query more precise
4972
		// just in case there are multiple identical _wp_old_slug values
4973 View Code Duplication
		if ( '' != $wp_query->query_vars['year'] )
4974
			$query .= $wpdb->prepare(" AND YEAR(post_date) = %d", $wp_query->query_vars['year']);
4975 View Code Duplication
		if ( '' != $wp_query->query_vars['monthnum'] )
4976
			$query .= $wpdb->prepare(" AND MONTH(post_date) = %d", $wp_query->query_vars['monthnum']);
4977 View Code Duplication
		if ( '' != $wp_query->query_vars['day'] )
4978
			$query .= $wpdb->prepare(" AND DAYOFMONTH(post_date) = %d", $wp_query->query_vars['day']);
4979
4980
		$id = (int) $wpdb->get_var($query);
4981
4982
		if ( ! $id )
4983
			return;
4984
4985
		$link = get_permalink( $id );
4986
4987
		if ( isset( $GLOBALS['wp_query']->query_vars['paged'] ) && $GLOBALS['wp_query']->query_vars['paged'] > 1 ) {
4988
			$link = user_trailingslashit( trailingslashit( $link ) . 'page/' . $GLOBALS['wp_query']->query_vars['paged'] );
0 ignored issues
show
It seems like $link can also be of type false; however, trailingslashit() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
4989
		} elseif( is_embed() ) {
4990
			$link = user_trailingslashit( trailingslashit( $link ) . 'embed' );
0 ignored issues
show
It seems like $link can also be of type false; however, trailingslashit() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
4991
		}
4992
4993
		/**
4994
		 * Filters the old slug redirect URL.
4995
		 *
4996
		 * @since 4.4.0
4997
		 *
4998
		 * @param string $link The redirect URL.
4999
		 */
5000
		$link = apply_filters( 'old_slug_redirect_url', $link );
5001
5002
		if ( ! $link ) {
5003
			return;
5004
		}
5005
5006
		wp_redirect( $link, 301 ); // Permanent redirect
5007
		exit;
5008
	endif;
5009
}
5010
5011
/**
5012
 * Set up global post data.
5013
 *
5014
 * @since 1.5.0
5015
 * @since 4.4.0 Added the ability to pass a post ID to `$post`.
5016
 *
5017
 * @global WP_Query $wp_query Global WP_Query instance.
5018
 *
5019
 * @param WP_Post|object|int $post WP_Post instance or Post ID/object.
5020
 * @return bool True when finished.
5021
 */
5022
function setup_postdata( $post ) {
5023
	global $wp_query;
5024
5025
	if ( ! empty( $wp_query ) && $wp_query instanceof WP_Query ) {
5026
		return $wp_query->setup_postdata( $post );
5027
	}
5028
5029
	return false;
5030
}
5031