WordPress 4.5
[autoinstalls/wordpress.git] / wp-includes / query.php
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  * Set up The Loop with query parameters.
81  *
82  * This will override the current WordPress Loop and shouldn't be used more than
83  * once. This must not be used within the WordPress Loop.
84  *
85  * @since 1.5.0
86  *
87  * @global WP_Query $wp_query Global WP_Query instance.
88  *
89  * @param string $query
90  * @return array List of posts
91  */
92 function query_posts($query) {
93         $GLOBALS['wp_query'] = new WP_Query();
94         return $GLOBALS['wp_query']->query($query);
95 }
96
97 /**
98  * Destroy the previous query and set up a new query.
99  *
100  * This should be used after {@link query_posts()} and before another {@link
101  * query_posts()}. This will remove obscure bugs that occur when the previous
102  * wp_query object is not destroyed properly before another is set up.
103  *
104  * @since 2.3.0
105  *
106  * @global WP_Query $wp_query     Global WP_Query instance.
107  * @global WP_Query $wp_the_query Copy of the global WP_Query instance created during wp_reset_query().
108  */
109 function wp_reset_query() {
110         $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
111         wp_reset_postdata();
112 }
113
114 /**
115  * After looping through a separate query, this function restores
116  * the $post global to the current post in the main query.
117  *
118  * @since 3.0.0
119  *
120  * @global WP_Query $wp_query Global WP_Query instance.
121  */
122 function wp_reset_postdata() {
123         global $wp_query;
124
125         if ( isset( $wp_query ) ) {
126                 $wp_query->reset_postdata();
127         }
128 }
129
130 /*
131  * Query type checks.
132  */
133
134 /**
135  * Is the query for an existing archive page?
136  *
137  * Month, Year, Category, Author, Post Type archive...
138  *
139  * @since 1.5.0
140  *
141  * @global WP_Query $wp_query Global WP_Query instance.
142  *
143  * @return bool
144  */
145 function is_archive() {
146         global $wp_query;
147
148         if ( ! isset( $wp_query ) ) {
149                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
150                 return false;
151         }
152
153         return $wp_query->is_archive();
154 }
155
156 /**
157  * Is the query for an existing post type archive page?
158  *
159  * @since 3.1.0
160  *
161  * @global WP_Query $wp_query Global WP_Query instance.
162  *
163  * @param string|array $post_types Optional. Post type or array of posts types to check against.
164  * @return bool
165  */
166 function is_post_type_archive( $post_types = '' ) {
167         global $wp_query;
168
169         if ( ! isset( $wp_query ) ) {
170                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
171                 return false;
172         }
173
174         return $wp_query->is_post_type_archive( $post_types );
175 }
176
177 /**
178  * Is the query for an existing attachment page?
179  *
180  * @since 2.0.0
181  *
182  * @global WP_Query $wp_query Global WP_Query instance.
183  *
184  * @param int|string|array|object $attachment Attachment ID, title, slug, or array of such.
185  * @return bool
186  */
187 function is_attachment( $attachment = '' ) {
188         global $wp_query;
189
190         if ( ! isset( $wp_query ) ) {
191                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
192                 return false;
193         }
194
195         return $wp_query->is_attachment( $attachment );
196 }
197
198 /**
199  * Is the query for an existing author archive page?
200  *
201  * If the $author parameter is specified, this function will additionally
202  * check if the query is for one of the authors specified.
203  *
204  * @since 1.5.0
205  *
206  * @global WP_Query $wp_query Global WP_Query instance.
207  *
208  * @param mixed $author Optional. User ID, nickname, nicename, or array of User IDs, nicknames, and nicenames
209  * @return bool
210  */
211 function is_author( $author = '' ) {
212         global $wp_query;
213
214         if ( ! isset( $wp_query ) ) {
215                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
216                 return false;
217         }
218
219         return $wp_query->is_author( $author );
220 }
221
222 /**
223  * Is the query for an existing category archive page?
224  *
225  * If the $category parameter is specified, this function will additionally
226  * check if the query is for one of the categories specified.
227  *
228  * @since 1.5.0
229  *
230  * @global WP_Query $wp_query Global WP_Query instance.
231  *
232  * @param mixed $category Optional. Category ID, name, slug, or array of Category IDs, names, and slugs.
233  * @return bool
234  */
235 function is_category( $category = '' ) {
236         global $wp_query;
237
238         if ( ! isset( $wp_query ) ) {
239                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
240                 return false;
241         }
242
243         return $wp_query->is_category( $category );
244 }
245
246 /**
247  * Is the query for an existing tag archive page?
248  *
249  * If the $tag parameter is specified, this function will additionally
250  * check if the query is for one of the tags specified.
251  *
252  * @since 2.3.0
253  *
254  * @global WP_Query $wp_query Global WP_Query instance.
255  *
256  * @param mixed $tag Optional. Tag ID, name, slug, or array of Tag IDs, names, and slugs.
257  * @return bool
258  */
259 function is_tag( $tag = '' ) {
260         global $wp_query;
261
262         if ( ! isset( $wp_query ) ) {
263                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
264                 return false;
265         }
266
267         return $wp_query->is_tag( $tag );
268 }
269
270 /**
271  * Is the query for an existing taxonomy archive page?
272  *
273  * If the $taxonomy parameter is specified, this function will additionally
274  * check if the query is for that specific $taxonomy.
275  *
276  * If the $term parameter is specified in addition to the $taxonomy parameter,
277  * this function will additionally check if the query is for one of the terms
278  * specified.
279  *
280  * @since 2.5.0
281  *
282  * @global WP_Query $wp_query Global WP_Query instance.
283  *
284  * @param string|array     $taxonomy Optional. Taxonomy slug or slugs.
285  * @param int|string|array $term     Optional. Term ID, name, slug or array of Term IDs, names, and slugs.
286  * @return bool
287  */
288 function is_tax( $taxonomy = '', $term = '' ) {
289         global $wp_query;
290
291         if ( ! isset( $wp_query ) ) {
292                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
293                 return false;
294         }
295
296         return $wp_query->is_tax( $taxonomy, $term );
297 }
298
299 /**
300  * Is the query for an existing date archive?
301  *
302  * @since 1.5.0
303  *
304  * @global WP_Query $wp_query Global WP_Query instance.
305  *
306  * @return bool
307  */
308 function is_date() {
309         global $wp_query;
310
311         if ( ! isset( $wp_query ) ) {
312                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
313                 return false;
314         }
315
316         return $wp_query->is_date();
317 }
318
319 /**
320  * Is the query for an existing day archive?
321  *
322  * @since 1.5.0
323  *
324  * @global WP_Query $wp_query Global WP_Query instance.
325  *
326  * @return bool
327  */
328 function is_day() {
329         global $wp_query;
330
331         if ( ! isset( $wp_query ) ) {
332                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
333                 return false;
334         }
335
336         return $wp_query->is_day();
337 }
338
339 /**
340  * Is the query for a feed?
341  *
342  * @since 1.5.0
343  *
344  * @global WP_Query $wp_query Global WP_Query instance.
345  *
346  * @param string|array $feeds Optional feed types to check.
347  * @return bool
348  */
349 function is_feed( $feeds = '' ) {
350         global $wp_query;
351
352         if ( ! isset( $wp_query ) ) {
353                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
354                 return false;
355         }
356
357         return $wp_query->is_feed( $feeds );
358 }
359
360 /**
361  * Is the query for a comments feed?
362  *
363  * @since 3.0.0
364  *
365  * @global WP_Query $wp_query Global WP_Query instance.
366  *
367  * @return bool
368  */
369 function is_comment_feed() {
370         global $wp_query;
371
372         if ( ! isset( $wp_query ) ) {
373                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
374                 return false;
375         }
376
377         return $wp_query->is_comment_feed();
378 }
379
380 /**
381  * Is the query for the front page of the site?
382  *
383  * This is for what is displayed at your site's main URL.
384  *
385  * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_on_front'.
386  *
387  * If you set a static page for the front page of your site, this function will return
388  * true when viewing that page.
389  *
390  * Otherwise the same as @see is_home()
391  *
392  * @since 2.5.0
393  *
394  * @global WP_Query $wp_query Global WP_Query instance.
395  *
396  * @return bool True, if front of site.
397  */
398 function is_front_page() {
399         global $wp_query;
400
401         if ( ! isset( $wp_query ) ) {
402                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
403                 return false;
404         }
405
406         return $wp_query->is_front_page();
407 }
408
409 /**
410  * Is the query for the blog homepage?
411  *
412  * This is the page which shows the time based blog content of your site.
413  *
414  * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_for_posts'.
415  *
416  * If you set a static page for the front page of your site, this function will return
417  * true only on the page you set as the "Posts page".
418  *
419  * @see is_front_page()
420  *
421  * @since 1.5.0
422  *
423  * @global WP_Query $wp_query Global WP_Query instance.
424  *
425  * @return bool True if blog view homepage.
426  */
427 function is_home() {
428         global $wp_query;
429
430         if ( ! isset( $wp_query ) ) {
431                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
432                 return false;
433         }
434
435         return $wp_query->is_home();
436 }
437
438 /**
439  * Is the query for an existing month archive?
440  *
441  * @since 1.5.0
442  *
443  * @global WP_Query $wp_query Global WP_Query instance.
444  *
445  * @return bool
446  */
447 function is_month() {
448         global $wp_query;
449
450         if ( ! isset( $wp_query ) ) {
451                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
452                 return false;
453         }
454
455         return $wp_query->is_month();
456 }
457
458 /**
459  * Is the query for an existing single page?
460  *
461  * If the $page parameter is specified, this function will additionally
462  * check if the query is for one of the pages specified.
463  *
464  * @see is_single()
465  * @see is_singular()
466  *
467  * @since 1.5.0
468  *
469  * @global WP_Query $wp_query Global WP_Query instance.
470  *
471  * @param int|string|array $page Optional. Page ID, title, slug, or array of such. Default empty.
472  * @return bool Whether the query is for an existing single page.
473  */
474 function is_page( $page = '' ) {
475         global $wp_query;
476
477         if ( ! isset( $wp_query ) ) {
478                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
479                 return false;
480         }
481
482         return $wp_query->is_page( $page );
483 }
484
485 /**
486  * Is the query for paged result and not for the first page?
487  *
488  * @since 1.5.0
489  *
490  * @global WP_Query $wp_query Global WP_Query instance.
491  *
492  * @return bool
493  */
494 function is_paged() {
495         global $wp_query;
496
497         if ( ! isset( $wp_query ) ) {
498                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
499                 return false;
500         }
501
502         return $wp_query->is_paged();
503 }
504
505 /**
506  * Is the query for a post or page preview?
507  *
508  * @since 2.0.0
509  *
510  * @global WP_Query $wp_query Global WP_Query instance.
511  *
512  * @return bool
513  */
514 function is_preview() {
515         global $wp_query;
516
517         if ( ! isset( $wp_query ) ) {
518                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
519                 return false;
520         }
521
522         return $wp_query->is_preview();
523 }
524
525 /**
526  * Is the query for the robots file?
527  *
528  * @since 2.1.0
529  *
530  * @global WP_Query $wp_query Global WP_Query instance.
531  *
532  * @return bool
533  */
534 function is_robots() {
535         global $wp_query;
536
537         if ( ! isset( $wp_query ) ) {
538                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
539                 return false;
540         }
541
542         return $wp_query->is_robots();
543 }
544
545 /**
546  * Is the query for a search?
547  *
548  * @since 1.5.0
549  *
550  * @global WP_Query $wp_query Global WP_Query instance.
551  *
552  * @return bool
553  */
554 function is_search() {
555         global $wp_query;
556
557         if ( ! isset( $wp_query ) ) {
558                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
559                 return false;
560         }
561
562         return $wp_query->is_search();
563 }
564
565 /**
566  * Is the query for an existing single post?
567  *
568  * Works for any post type, except attachments and pages
569  *
570  * If the $post parameter is specified, this function will additionally
571  * check if the query is for one of the Posts specified.
572  *
573  * @see is_page()
574  * @see is_singular()
575  *
576  * @since 1.5.0
577  *
578  * @global WP_Query $wp_query Global WP_Query instance.
579  *
580  * @param int|string|array $post Optional. Post ID, title, slug, or array of such. Default empty.
581  * @return bool Whether the query is for an existing single post.
582  */
583 function is_single( $post = '' ) {
584         global $wp_query;
585
586         if ( ! isset( $wp_query ) ) {
587                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
588                 return false;
589         }
590
591         return $wp_query->is_single( $post );
592 }
593
594 /**
595  * Is the query for an existing single post of any post type (post, attachment, page, ... )?
596  *
597  * If the $post_types parameter is specified, this function will additionally
598  * check if the query is for one of the Posts Types specified.
599  *
600  * @see is_page()
601  * @see is_single()
602  *
603  * @since 1.5.0
604  *
605  * @global WP_Query $wp_query Global WP_Query instance.
606  *
607  * @param string|array $post_types Optional. Post type or array of post types. Default empty.
608  * @return bool Whether the query is for an existing single post of any of the given post types.
609  */
610 function is_singular( $post_types = '' ) {
611         global $wp_query;
612
613         if ( ! isset( $wp_query ) ) {
614                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
615                 return false;
616         }
617
618         return $wp_query->is_singular( $post_types );
619 }
620
621 /**
622  * Is the query for a specific time?
623  *
624  * @since 1.5.0
625  *
626  * @global WP_Query $wp_query Global WP_Query instance.
627  *
628  * @return bool
629  */
630 function is_time() {
631         global $wp_query;
632
633         if ( ! isset( $wp_query ) ) {
634                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
635                 return false;
636         }
637
638         return $wp_query->is_time();
639 }
640
641 /**
642  * Is the query for a trackback endpoint call?
643  *
644  * @since 1.5.0
645  *
646  * @global WP_Query $wp_query Global WP_Query instance.
647  *
648  * @return bool
649  */
650 function is_trackback() {
651         global $wp_query;
652
653         if ( ! isset( $wp_query ) ) {
654                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
655                 return false;
656         }
657
658         return $wp_query->is_trackback();
659 }
660
661 /**
662  * Is the query for an existing year archive?
663  *
664  * @since 1.5.0
665  *
666  * @global WP_Query $wp_query Global WP_Query instance.
667  *
668  * @return bool
669  */
670 function is_year() {
671         global $wp_query;
672
673         if ( ! isset( $wp_query ) ) {
674                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
675                 return false;
676         }
677
678         return $wp_query->is_year();
679 }
680
681 /**
682  * Is the query a 404 (returns no results)?
683  *
684  * @since 1.5.0
685  *
686  * @global WP_Query $wp_query Global WP_Query instance.
687  *
688  * @return bool
689  */
690 function is_404() {
691         global $wp_query;
692
693         if ( ! isset( $wp_query ) ) {
694                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
695                 return false;
696         }
697
698         return $wp_query->is_404();
699 }
700
701 /**
702  * Is the query for an embedded post?
703  *
704  * @since 4.4.0
705  *
706  * @global WP_Query $wp_query Global WP_Query instance.
707  *
708  * @return bool Whether we're in an embedded post or not.
709  */
710 function is_embed() {
711         global $wp_query;
712
713         if ( ! isset( $wp_query ) ) {
714                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
715                 return false;
716         }
717
718         return $wp_query->is_embed();
719 }
720
721 /**
722  * Is the query the main query?
723  *
724  * @since 3.3.0
725  *
726  * @global WP_Query $wp_query Global WP_Query instance.
727  *
728  * @return bool
729  */
730 function is_main_query() {
731         if ( 'pre_get_posts' === current_filter() ) {
732                 $message = sprintf(
733                         /* translators: 1: pre_get_posts 2: WP_Query->is_main_query() 3: is_main_query() 4: link to codex is_main_query() page. */
734                         __( 'In %1$s, use the %2$s method, not the %3$s function. See %4$s.' ),
735                         '<code>pre_get_posts</code>',
736                         '<code>WP_Query->is_main_query()</code>',
737                         '<code>is_main_query()</code>',
738                         __( 'https://codex.wordpress.org/Function_Reference/is_main_query' )
739                 );
740                 _doing_it_wrong( __FUNCTION__, $message, '3.7' );
741         }
742
743         global $wp_query;
744         return $wp_query->is_main_query();
745 }
746
747 /*
748  * The Loop. Post loop control.
749  */
750
751 /**
752  * Whether current WordPress query has results to loop over.
753  *
754  * @since 1.5.0
755  *
756  * @global WP_Query $wp_query Global WP_Query instance.
757  *
758  * @return bool
759  */
760 function have_posts() {
761         global $wp_query;
762         return $wp_query->have_posts();
763 }
764
765 /**
766  * Whether the caller is in the Loop.
767  *
768  * @since 2.0.0
769  *
770  * @global WP_Query $wp_query Global WP_Query instance.
771  *
772  * @return bool True if caller is within loop, false if loop hasn't started or ended.
773  */
774 function in_the_loop() {
775         global $wp_query;
776         return $wp_query->in_the_loop;
777 }
778
779 /**
780  * Rewind the loop posts.
781  *
782  * @since 1.5.0
783  *
784  * @global WP_Query $wp_query Global WP_Query instance.
785  */
786 function rewind_posts() {
787         global $wp_query;
788         $wp_query->rewind_posts();
789 }
790
791 /**
792  * Iterate the post index in the loop.
793  *
794  * @since 1.5.0
795  *
796  * @global WP_Query $wp_query Global WP_Query instance.
797  */
798 function the_post() {
799         global $wp_query;
800         $wp_query->the_post();
801 }
802
803 /*
804  * Comments loop.
805  */
806
807 /**
808  * Whether there are comments to loop over.
809  *
810  * @since 2.2.0
811  *
812  * @global WP_Query $wp_query Global WP_Query instance.
813  *
814  * @return bool
815  */
816 function have_comments() {
817         global $wp_query;
818         return $wp_query->have_comments();
819 }
820
821 /**
822  * Iterate comment index in the comment loop.
823  *
824  * @since 2.2.0
825  *
826  * @global WP_Query $wp_query Global WP_Query instance.
827  *
828  * @return object
829  */
830 function the_comment() {
831         global $wp_query;
832         return $wp_query->the_comment();
833 }
834
835 /*
836  * WP_Query
837  */
838
839 /**
840  * The WordPress Query class.
841  *
842  * @link https://codex.wordpress.org/Function_Reference/WP_Query Codex page.
843  *
844  * @since 1.5.0
845  * @since 4.5.0 Removed the `$comments_popup` property.
846  */
847 class WP_Query {
848
849         /**
850          * Query vars set by the user
851          *
852          * @since 1.5.0
853          * @access public
854          * @var array
855          */
856         public $query;
857
858         /**
859          * Query vars, after parsing
860          *
861          * @since 1.5.0
862          * @access public
863          * @var array
864          */
865         public $query_vars = array();
866
867         /**
868          * Taxonomy query, as passed to get_tax_sql()
869          *
870          * @since 3.1.0
871          * @access public
872          * @var object WP_Tax_Query
873          */
874         public $tax_query;
875
876         /**
877          * Metadata query container
878          *
879          * @since 3.2.0
880          * @access public
881          * @var object WP_Meta_Query
882          */
883         public $meta_query = false;
884
885         /**
886          * Date query container
887          *
888          * @since 3.7.0
889          * @access public
890          * @var object WP_Date_Query
891          */
892         public $date_query = false;
893
894         /**
895          * Holds the data for a single object that is queried.
896          *
897          * Holds the contents of a post, page, category, attachment.
898          *
899          * @since 1.5.0
900          * @access public
901          * @var object|array
902          */
903         public $queried_object;
904
905         /**
906          * The ID of the queried object.
907          *
908          * @since 1.5.0
909          * @access public
910          * @var int
911          */
912         public $queried_object_id;
913
914         /**
915          * Get post database query.
916          *
917          * @since 2.0.1
918          * @access public
919          * @var string
920          */
921         public $request;
922
923         /**
924          * List of posts.
925          *
926          * @since 1.5.0
927          * @access public
928          * @var array
929          */
930         public $posts;
931
932         /**
933          * The amount of posts for the current query.
934          *
935          * @since 1.5.0
936          * @access public
937          * @var int
938          */
939         public $post_count = 0;
940
941         /**
942          * Index of the current item in the loop.
943          *
944          * @since 1.5.0
945          * @access public
946          * @var int
947          */
948         public $current_post = -1;
949
950         /**
951          * Whether the loop has started and the caller is in the loop.
952          *
953          * @since 2.0.0
954          * @access public
955          * @var bool
956          */
957         public $in_the_loop = false;
958
959         /**
960          * The current post.
961          *
962          * @since 1.5.0
963          * @access public
964          * @var WP_Post
965          */
966         public $post;
967
968         /**
969          * The list of comments for current post.
970          *
971          * @since 2.2.0
972          * @access public
973          * @var array
974          */
975         public $comments;
976
977         /**
978          * The amount of comments for the posts.
979          *
980          * @since 2.2.0
981          * @access public
982          * @var int
983          */
984         public $comment_count = 0;
985
986         /**
987          * The index of the comment in the comment loop.
988          *
989          * @since 2.2.0
990          * @access public
991          * @var int
992          */
993         public $current_comment = -1;
994
995         /**
996          * Current comment ID.
997          *
998          * @since 2.2.0
999          * @access public
1000          * @var int
1001          */
1002         public $comment;
1003
1004         /**
1005          * The amount of found posts for the current query.
1006          *
1007          * If limit clause was not used, equals $post_count.
1008          *
1009          * @since 2.1.0
1010          * @access public
1011          * @var int
1012          */
1013         public $found_posts = 0;
1014
1015         /**
1016          * The amount of pages.
1017          *
1018          * @since 2.1.0
1019          * @access public
1020          * @var int
1021          */
1022         public $max_num_pages = 0;
1023
1024         /**
1025          * The amount of comment pages.
1026          *
1027          * @since 2.7.0
1028          * @access public
1029          * @var int
1030          */
1031         public $max_num_comment_pages = 0;
1032
1033         /**
1034          * Set if query is single post.
1035          *
1036          * @since 1.5.0
1037          * @access public
1038          * @var bool
1039          */
1040         public $is_single = false;
1041
1042         /**
1043          * Set if query is preview of blog.
1044          *
1045          * @since 2.0.0
1046          * @access public
1047          * @var bool
1048          */
1049         public $is_preview = false;
1050
1051         /**
1052          * Set if query returns a page.
1053          *
1054          * @since 1.5.0
1055          * @access public
1056          * @var bool
1057          */
1058         public $is_page = false;
1059
1060         /**
1061          * Set if query is an archive list.
1062          *
1063          * @since 1.5.0
1064          * @access public
1065          * @var bool
1066          */
1067         public $is_archive = false;
1068
1069         /**
1070          * Set if query is part of a date.
1071          *
1072          * @since 1.5.0
1073          * @access public
1074          * @var bool
1075          */
1076         public $is_date = false;
1077
1078         /**
1079          * Set if query contains a year.
1080          *
1081          * @since 1.5.0
1082          * @access public
1083          * @var bool
1084          */
1085         public $is_year = false;
1086
1087         /**
1088          * Set if query contains a month.
1089          *
1090          * @since 1.5.0
1091          * @access public
1092          * @var bool
1093          */
1094         public $is_month = false;
1095
1096         /**
1097          * Set if query contains a day.
1098          *
1099          * @since 1.5.0
1100          * @access public
1101          * @var bool
1102          */
1103         public $is_day = false;
1104
1105         /**
1106          * Set if query contains time.
1107          *
1108          * @since 1.5.0
1109          * @access public
1110          * @var bool
1111          */
1112         public $is_time = false;
1113
1114         /**
1115          * Set if query contains an author.
1116          *
1117          * @since 1.5.0
1118          * @access public
1119          * @var bool
1120          */
1121         public $is_author = false;
1122
1123         /**
1124          * Set if query contains category.
1125          *
1126          * @since 1.5.0
1127          * @access public
1128          * @var bool
1129          */
1130         public $is_category = false;
1131
1132         /**
1133          * Set if query contains tag.
1134          *
1135          * @since 2.3.0
1136          * @access public
1137          * @var bool
1138          */
1139         public $is_tag = false;
1140
1141         /**
1142          * Set if query contains taxonomy.
1143          *
1144          * @since 2.5.0
1145          * @access public
1146          * @var bool
1147          */
1148         public $is_tax = false;
1149
1150         /**
1151          * Set if query was part of a search result.
1152          *
1153          * @since 1.5.0
1154          * @access public
1155          * @var bool
1156          */
1157         public $is_search = false;
1158
1159         /**
1160          * Set if query is feed display.
1161          *
1162          * @since 1.5.0
1163          * @access public
1164          * @var bool
1165          */
1166         public $is_feed = false;
1167
1168         /**
1169          * Set if query is comment feed display.
1170          *
1171          * @since 2.2.0
1172          * @access public
1173          * @var bool
1174          */
1175         public $is_comment_feed = false;
1176
1177         /**
1178          * Set if query is trackback.
1179          *
1180          * @since 1.5.0
1181          * @access public
1182          * @var bool
1183          */
1184         public $is_trackback = false;
1185
1186         /**
1187          * Set if query is blog homepage.
1188          *
1189          * @since 1.5.0
1190          * @access public
1191          * @var bool
1192          */
1193         public $is_home = false;
1194
1195         /**
1196          * Set if query couldn't found anything.
1197          *
1198          * @since 1.5.0
1199          * @access public
1200          * @var bool
1201          */
1202         public $is_404 = false;
1203
1204         /**
1205          * Set if query is embed.
1206          *
1207          * @since 4.4.0
1208          * @access public
1209          * @var bool
1210          */
1211         public $is_embed = false;
1212
1213         /**
1214          * Set if query is paged
1215          *
1216          * @since 1.5.0
1217          * @access public
1218          * @var bool
1219          */
1220         public $is_paged = false;
1221
1222         /**
1223          * Set if query is part of administration page.
1224          *
1225          * @since 1.5.0
1226          * @access public
1227          * @var bool
1228          */
1229         public $is_admin = false;
1230
1231         /**
1232          * Set if query is an attachment.
1233          *
1234          * @since 2.0.0
1235          * @access public
1236          * @var bool
1237          */
1238         public $is_attachment = false;
1239
1240         /**
1241          * Set if is single, is a page, or is an attachment.
1242          *
1243          * @since 2.1.0
1244          * @access public
1245          * @var bool
1246          */
1247         public $is_singular = false;
1248
1249         /**
1250          * Set if query is for robots.
1251          *
1252          * @since 2.1.0
1253          * @access public
1254          * @var bool
1255          */
1256         public $is_robots = false;
1257
1258         /**
1259          * Set if query contains posts.
1260          *
1261          * Basically, the homepage if the option isn't set for the static homepage.
1262          *
1263          * @since 2.1.0
1264          * @access public
1265          * @var bool
1266          */
1267         public $is_posts_page = false;
1268
1269         /**
1270          * Set if query is for a post type archive.
1271          *
1272          * @since 3.1.0
1273          * @access public
1274          * @var bool
1275          */
1276         public $is_post_type_archive = false;
1277
1278         /**
1279          * Stores the ->query_vars state like md5(serialize( $this->query_vars ) ) so we know
1280          * whether we have to re-parse because something has changed
1281          *
1282          * @since 3.1.0
1283          * @access private
1284          * @var bool|string
1285          */
1286         private $query_vars_hash = false;
1287
1288         /**
1289          * Whether query vars have changed since the initial parse_query() call. Used to catch modifications to query vars made
1290          * via pre_get_posts hooks.
1291          *
1292          * @since 3.1.1
1293          * @access private
1294          */
1295         private $query_vars_changed = true;
1296
1297         /**
1298          * Set if post thumbnails are cached
1299          *
1300          * @since 3.2.0
1301          * @access public
1302          * @var bool
1303          */
1304          public $thumbnails_cached = false;
1305
1306         /**
1307          * Cached list of search stopwords.
1308          *
1309          * @since 3.7.0
1310          * @var array
1311          */
1312         private $stopwords;
1313
1314         private $compat_fields = array( 'query_vars_hash', 'query_vars_changed' );
1315
1316         private $compat_methods = array( 'init_query_flags', 'parse_tax_query' );
1317
1318         /**
1319          * Resets query flags to false.
1320          *
1321          * The query flags are what page info WordPress was able to figure out.
1322          *
1323          * @since 2.0.0
1324          * @access private
1325          */
1326         private function init_query_flags() {
1327                 $this->is_single = false;
1328                 $this->is_preview = false;
1329                 $this->is_page = false;
1330                 $this->is_archive = false;
1331                 $this->is_date = false;
1332                 $this->is_year = false;
1333                 $this->is_month = false;
1334                 $this->is_day = false;
1335                 $this->is_time = false;
1336                 $this->is_author = false;
1337                 $this->is_category = false;
1338                 $this->is_tag = false;
1339                 $this->is_tax = false;
1340                 $this->is_search = false;
1341                 $this->is_feed = false;
1342                 $this->is_comment_feed = false;
1343                 $this->is_trackback = false;
1344                 $this->is_home = false;
1345                 $this->is_404 = false;
1346                 $this->is_paged = false;
1347                 $this->is_admin = false;
1348                 $this->is_attachment = false;
1349                 $this->is_singular = false;
1350                 $this->is_robots = false;
1351                 $this->is_posts_page = false;
1352                 $this->is_post_type_archive = false;
1353         }
1354
1355         /**
1356          * Initiates object properties and sets default values.
1357          *
1358          * @since 1.5.0
1359          * @access public
1360          */
1361         public function init() {
1362                 unset($this->posts);
1363                 unset($this->query);
1364                 $this->query_vars = array();
1365                 unset($this->queried_object);
1366                 unset($this->queried_object_id);
1367                 $this->post_count = 0;
1368                 $this->current_post = -1;
1369                 $this->in_the_loop = false;
1370                 unset( $this->request );
1371                 unset( $this->post );
1372                 unset( $this->comments );
1373                 unset( $this->comment );
1374                 $this->comment_count = 0;
1375                 $this->current_comment = -1;
1376                 $this->found_posts = 0;
1377                 $this->max_num_pages = 0;
1378                 $this->max_num_comment_pages = 0;
1379
1380                 $this->init_query_flags();
1381         }
1382
1383         /**
1384          * Reparse the query vars.
1385          *
1386          * @since 1.5.0
1387          * @access public
1388          */
1389         public function parse_query_vars() {
1390                 $this->parse_query();
1391         }
1392
1393         /**
1394          * Fills in the query variables, which do not exist within the parameter.
1395          *
1396          * @since 2.1.0
1397          * @since 4.4.0 Removed the `comments_popup` public query variable.
1398          * @access public
1399          *
1400          * @param array $array Defined query variables.
1401          * @return array Complete query variables with undefined ones filled in empty.
1402          */
1403         public function fill_query_vars($array) {
1404                 $keys = array(
1405                         'error'
1406                         , 'm'
1407                         , 'p'
1408                         , 'post_parent'
1409                         , 'subpost'
1410                         , 'subpost_id'
1411                         , 'attachment'
1412                         , 'attachment_id'
1413                         , 'name'
1414                         , 'static'
1415                         , 'pagename'
1416                         , 'page_id'
1417                         , 'second'
1418                         , 'minute'
1419                         , 'hour'
1420                         , 'day'
1421                         , 'monthnum'
1422                         , 'year'
1423                         , 'w'
1424                         , 'category_name'
1425                         , 'tag'
1426                         , 'cat'
1427                         , 'tag_id'
1428                         , 'author'
1429                         , 'author_name'
1430                         , 'feed'
1431                         , 'tb'
1432                         , 'paged'
1433                         , 'meta_key'
1434                         , 'meta_value'
1435                         , 'preview'
1436                         , 's'
1437                         , 'sentence'
1438                         , 'title'
1439                         , 'fields'
1440                         , 'menu_order'
1441                         , 'embed'
1442                 );
1443
1444                 foreach ( $keys as $key ) {
1445                         if ( !isset($array[$key]) )
1446                                 $array[$key] = '';
1447                 }
1448
1449                 $array_keys = array( 'category__in', 'category__not_in', 'category__and', 'post__in', 'post__not_in', 'post_name__in',
1450                         'tag__in', 'tag__not_in', 'tag__and', 'tag_slug__in', 'tag_slug__and', 'post_parent__in', 'post_parent__not_in',
1451                         'author__in', 'author__not_in' );
1452
1453                 foreach ( $array_keys as $key ) {
1454                         if ( !isset($array[$key]) )
1455                                 $array[$key] = array();
1456                 }
1457                 return $array;
1458         }
1459
1460         /**
1461          * Parse a query string and set query type booleans.
1462          *
1463          * @since 1.5.0
1464          * @since 4.2.0 Introduced the ability to order by specific clauses of a `$meta_query`, by passing the clause's
1465          *              array key to `$orderby`.
1466          * @since 4.4.0 Introduced `$post_name__in` and `$title` parameters. `$s` was updated to support excluded
1467          *              search terms, by prepending a hyphen.
1468          * @since 4.5.0 Removed the `$comments_popup` parameter.
1469          *              Introduced the `$comment_status` and `$ping_status` parameters.
1470          *              Introduced `RAND(x)` syntax for `$orderby`, which allows an integer seed value to random sorts.
1471          * @access public
1472          *
1473          * @param string|array $query {
1474          *     Optional. Array or string of Query parameters.
1475          *
1476          *     @type int          $attachment_id           Attachment post ID. Used for 'attachment' post_type.
1477          *     @type int|string   $author                  Author ID, or comma-separated list of IDs.
1478          *     @type string       $author_name             User 'user_nicename'.
1479          *     @type array        $author__in              An array of author IDs to query from.
1480          *     @type array        $author__not_in          An array of author IDs not to query from.
1481          *     @type bool         $cache_results           Whether to cache post information. Default true.
1482          *     @type int|string   $cat                     Category ID or comma-separated list of IDs (this or any children).
1483          *     @type array        $category__and           An array of category IDs (AND in).
1484          *     @type array        $category__in            An array of category IDs (OR in, no children).
1485          *     @type array        $category__not_in        An array of category IDs (NOT in).
1486          *     @type string       $category_name           Use category slug (not name, this or any children).
1487          *     @type string       $comment_status          Comment status.
1488          *     @type int          $comments_per_page       The number of comments to return per page.
1489          *                                                 Default 'comments_per_page' option.
1490          *     @type array        $date_query              An associative array of WP_Date_Query arguments.
1491          *                                                 {@see WP_Date_Query::__construct()}
1492          *     @type int          $day                     Day of the month. Default empty. Accepts numbers 1-31.
1493          *     @type bool         $exact                   Whether to search by exact keyword. Default false.
1494          *     @type string|array $fields                  Which fields to return. Single field or all fields (string),
1495          *                                                 or array of fields. 'id=>parent' uses 'id' and 'post_parent'.
1496          *                                                 Default all fields. Accepts 'ids', 'id=>parent'.
1497          *     @type int          $hour                    Hour of the day. Default empty. Accepts numbers 0-23.
1498          *     @type int|bool     $ignore_sticky_posts     Whether to ignore sticky posts or not. Setting this to false
1499          *                                                 excludes stickies from 'post__in'. Accepts 1|true, 0|false.
1500          *                                                 Default 0|false.
1501          *     @type int          $m                       Combination YearMonth. Accepts any four-digit year and month
1502          *                                                 numbers 1-12. Default empty.
1503          *     @type string       $meta_compare            Comparison operator to test the 'meta_value'.
1504          *     @type string       $meta_key                Custom field key.
1505          *     @type array        $meta_query              An associative array of WP_Meta_Query arguments.
1506          *                                                 {@see WP_Meta_Query}
1507          *     @type string       $meta_value              Custom field value.
1508          *     @type int          $meta_value_num          Custom field value number.
1509          *     @type int          $menu_order              The menu order of the posts.
1510          *     @type int          $monthnum                The two-digit month. Default empty. Accepts numbers 1-12.
1511          *     @type string       $name                    Post slug.
1512          *     @type bool         $nopaging                Show all posts (true) or paginate (false). Default false.
1513          *     @type bool         $no_found_rows           Whether to skip counting the total rows found. Enabling can improve
1514          *                                                 performance. Default false.
1515          *     @type int          $offset                  The number of posts to offset before retrieval.
1516          *     @type string       $order                   Designates ascending or descending order of posts. Default 'DESC'.
1517          *                                                 Accepts 'ASC', 'DESC'.
1518          *     @type string|array $orderby                 Sort retrieved posts by parameter. One or more options may be
1519          *                                                 passed. To use 'meta_value', or 'meta_value_num',
1520          *                                                 'meta_key=keyname' must be also be defined. To sort by a
1521          *                                                 specific `$meta_query` clause, use that clause's array key.
1522          *                                                 Default 'date'. Accepts 'none', 'name', 'author', 'date',
1523          *                                                 'title', 'modified', 'menu_order', 'parent', 'ID', 'rand',
1524          *                                                 'RAND(x)' (where 'x' is an integer seed value),
1525          *                                                 'comment_count', 'meta_value', 'meta_value_num', 'post__in',
1526          *                                                 and the array keys of `$meta_query`.
1527          *     @type int          $p                       Post ID.
1528          *     @type int          $page                    Show the number of posts that would show up on page X of a
1529          *                                                 static front page.
1530          *     @type int          $paged                   The number of the current page.
1531          *     @type int          $page_id                 Page ID.
1532          *     @type string       $pagename                Page slug.
1533          *     @type string       $perm                    Show posts if user has the appropriate capability.
1534          *     @type string       $ping_status             Ping status.
1535          *     @type array        $post__in                An array of post IDs to retrieve, sticky posts will be included
1536          *     @type string       $post_mime_type          The mime type of the post. Used for 'attachment' post_type.
1537          *     @type array        $post__not_in            An array of post IDs not to retrieve. Note: a string of comma-
1538          *                                                 separated IDs will NOT work.
1539          *     @type int          $post_parent             Page ID to retrieve child pages for. Use 0 to only retrieve
1540          *                                                 top-level pages.
1541          *     @type array        $post_parent__in         An array containing parent page IDs to query child pages from.
1542          *     @type array        $post_parent__not_in     An array containing parent page IDs not to query child pages from.
1543          *     @type string|array $post_type               A post type slug (string) or array of post type slugs.
1544          *                                                 Default 'any' if using 'tax_query'.
1545          *     @type string|array $post_status             A post status (string) or array of post statuses.
1546          *     @type int          $posts_per_page          The number of posts to query for. Use -1 to request all posts.
1547          *     @type int          $posts_per_archive_page  The number of posts to query for by archive page. Overrides
1548          *                                                 'posts_per_page' when is_archive(), or is_search() are true.
1549          *     @type array        $post_name__in           An array of post slugs that results must match.
1550          *     @type string       $s                       Search keyword(s). Prepending a term with a hyphen will
1551          *                                                 exclude posts matching that term. Eg, 'pillow -sofa' will
1552          *                                                 return posts containing 'pillow' but not 'sofa'.
1553          *     @type int          $second                  Second of the minute. Default empty. Accepts numbers 0-60.
1554          *     @type bool         $sentence                Whether to search by phrase. Default false.
1555          *     @type bool         $suppress_filters        Whether to suppress filters. Default false.
1556          *     @type string       $tag                     Tag slug. Comma-separated (either), Plus-separated (all).
1557          *     @type array        $tag__and                An array of tag ids (AND in).
1558          *     @type array        $tag__in                 An array of tag ids (OR in).
1559          *     @type array        $tag__not_in             An array of tag ids (NOT in).
1560          *     @type int          $tag_id                  Tag id or comma-separated list of IDs.
1561          *     @type array        $tag_slug__and           An array of tag slugs (AND in).
1562          *     @type array        $tag_slug__in            An array of tag slugs (OR in). unless 'ignore_sticky_posts' is
1563          *                                                 true. Note: a string of comma-separated IDs will NOT work.
1564          *     @type array        $tax_query               An associative array of WP_Tax_Query arguments.
1565          *                                                 {@see WP_Tax_Query->queries}
1566          *     @type string       $title                   Post title.
1567          *     @type bool         $update_post_meta_cache  Whether to update the post meta cache. Default true.
1568          *     @type bool         $update_post_term_cache  Whether to update the post term cache. Default true.
1569          *     @type int          $w                       The week number of the year. Default empty. Accepts numbers 0-53.
1570          *     @type int          $year                    The four-digit year. Default empty. Accepts any four-digit year.
1571          * }
1572          */
1573         public function parse_query( $query =  '' ) {
1574                 if ( ! empty( $query ) ) {
1575                         $this->init();
1576                         $this->query = $this->query_vars = wp_parse_args( $query );
1577                 } elseif ( ! isset( $this->query ) ) {
1578                         $this->query = $this->query_vars;
1579                 }
1580
1581                 $this->query_vars = $this->fill_query_vars($this->query_vars);
1582                 $qv = &$this->query_vars;
1583                 $this->query_vars_changed = true;
1584
1585                 if ( ! empty($qv['robots']) )
1586                         $this->is_robots = true;
1587
1588                 $qv['p'] =  absint($qv['p']);
1589                 $qv['page_id'] =  absint($qv['page_id']);
1590                 $qv['year'] = absint($qv['year']);
1591                 $qv['monthnum'] = absint($qv['monthnum']);
1592                 $qv['day'] = absint($qv['day']);
1593                 $qv['w'] = absint($qv['w']);
1594                 $qv['m'] = preg_replace( '|[^0-9]|', '', $qv['m'] );
1595                 $qv['paged'] = absint($qv['paged']);
1596                 $qv['cat'] = preg_replace( '|[^0-9,-]|', '', $qv['cat'] ); // comma separated list of positive or negative integers
1597                 $qv['author'] = preg_replace( '|[^0-9,-]|', '', $qv['author'] ); // comma separated list of positive or negative integers
1598                 $qv['pagename'] = trim( $qv['pagename'] );
1599                 $qv['name'] = trim( $qv['name'] );
1600                 $qv['title'] = trim( $qv['title'] );
1601                 if ( '' !== $qv['hour'] ) $qv['hour'] = absint($qv['hour']);
1602                 if ( '' !== $qv['minute'] ) $qv['minute'] = absint($qv['minute']);
1603                 if ( '' !== $qv['second'] ) $qv['second'] = absint($qv['second']);
1604                 if ( '' !== $qv['menu_order'] ) $qv['menu_order'] = absint($qv['menu_order']);
1605
1606                 // Fairly insane upper bound for search string lengths.
1607                 if ( ! is_scalar( $qv['s'] ) || ( ! empty( $qv['s'] ) && strlen( $qv['s'] ) > 1600 ) ) {
1608                         $qv['s'] = '';
1609                 }
1610
1611                 // Compat. Map subpost to attachment.
1612                 if ( '' != $qv['subpost'] )
1613                         $qv['attachment'] = $qv['subpost'];
1614                 if ( '' != $qv['subpost_id'] )
1615                         $qv['attachment_id'] = $qv['subpost_id'];
1616
1617                 $qv['attachment_id'] = absint($qv['attachment_id']);
1618
1619                 if ( ('' != $qv['attachment']) || !empty($qv['attachment_id']) ) {
1620                         $this->is_single = true;
1621                         $this->is_attachment = true;
1622                 } elseif ( '' != $qv['name'] ) {
1623                         $this->is_single = true;
1624                 } elseif ( $qv['p'] ) {
1625                         $this->is_single = true;
1626                 } elseif ( ('' !== $qv['hour']) && ('' !== $qv['minute']) &&('' !== $qv['second']) && ('' != $qv['year']) && ('' != $qv['monthnum']) && ('' != $qv['day']) ) {
1627                         // If year, month, day, hour, minute, and second are set, a single
1628                         // post is being queried.
1629                         $this->is_single = true;
1630                 } elseif ( '' != $qv['static'] || '' != $qv['pagename'] || !empty($qv['page_id']) ) {
1631                         $this->is_page = true;
1632                         $this->is_single = false;
1633                 } else {
1634                         // Look for archive queries. Dates, categories, authors, search, post type archives.
1635
1636                         if ( isset( $this->query['s'] ) ) {
1637                                 $this->is_search = true;
1638                         }
1639
1640                         if ( '' !== $qv['second'] ) {
1641                                 $this->is_time = true;
1642                                 $this->is_date = true;
1643                         }
1644
1645                         if ( '' !== $qv['minute'] ) {
1646                                 $this->is_time = true;
1647                                 $this->is_date = true;
1648                         }
1649
1650                         if ( '' !== $qv['hour'] ) {
1651                                 $this->is_time = true;
1652                                 $this->is_date = true;
1653                         }
1654
1655                         if ( $qv['day'] ) {
1656                                 if ( ! $this->is_date ) {
1657                                         $date = sprintf( '%04d-%02d-%02d', $qv['year'], $qv['monthnum'], $qv['day'] );
1658                                         if ( $qv['monthnum'] && $qv['year'] && ! wp_checkdate( $qv['monthnum'], $qv['day'], $qv['year'], $date ) ) {
1659                                                 $qv['error'] = '404';
1660                                         } else {
1661                                                 $this->is_day = true;
1662                                                 $this->is_date = true;
1663                                         }
1664                                 }
1665                         }
1666
1667                         if ( $qv['monthnum'] ) {
1668                                 if ( ! $this->is_date ) {
1669                                         if ( 12 < $qv['monthnum'] ) {
1670                                                 $qv['error'] = '404';
1671                                         } else {
1672                                                 $this->is_month = true;
1673                                                 $this->is_date = true;
1674                                         }
1675                                 }
1676                         }
1677
1678                         if ( $qv['year'] ) {
1679                                 if ( ! $this->is_date ) {
1680                                         $this->is_year = true;
1681                                         $this->is_date = true;
1682                                 }
1683                         }
1684
1685                         if ( $qv['m'] ) {
1686                                 $this->is_date = true;
1687                                 if ( strlen($qv['m']) > 9 ) {
1688                                         $this->is_time = true;
1689                                 } elseif ( strlen( $qv['m'] ) > 7 ) {
1690                                         $this->is_day = true;
1691                                 } elseif ( strlen( $qv['m'] ) > 5 ) {
1692                                         $this->is_month = true;
1693                                 } else {
1694                                         $this->is_year = true;
1695                                 }
1696                         }
1697
1698                         if ( '' != $qv['w'] ) {
1699                                 $this->is_date = true;
1700                         }
1701
1702                         $this->query_vars_hash = false;
1703                         $this->parse_tax_query( $qv );
1704
1705                         foreach ( $this->tax_query->queries as $tax_query ) {
1706                                 if ( ! is_array( $tax_query ) ) {
1707                                         continue;
1708                                 }
1709
1710                                 if ( isset( $tax_query['operator'] ) && 'NOT IN' != $tax_query['operator'] ) {
1711                                         switch ( $tax_query['taxonomy'] ) {
1712                                                 case 'category':
1713                                                         $this->is_category = true;
1714                                                         break;
1715                                                 case 'post_tag':
1716                                                         $this->is_tag = true;
1717                                                         break;
1718                                                 default:
1719                                                         $this->is_tax = true;
1720                                         }
1721                                 }
1722                         }
1723                         unset( $tax_query );
1724
1725                         if ( empty($qv['author']) || ($qv['author'] == '0') ) {
1726                                 $this->is_author = false;
1727                         } else {
1728                                 $this->is_author = true;
1729                         }
1730
1731                         if ( '' != $qv['author_name'] )
1732                                 $this->is_author = true;
1733
1734                         if ( !empty( $qv['post_type'] ) && ! is_array( $qv['post_type'] ) ) {
1735                                 $post_type_obj = get_post_type_object( $qv['post_type'] );
1736                                 if ( ! empty( $post_type_obj->has_archive ) )
1737                                         $this->is_post_type_archive = true;
1738                         }
1739
1740                         if ( $this->is_post_type_archive || $this->is_date || $this->is_author || $this->is_category || $this->is_tag || $this->is_tax )
1741                                 $this->is_archive = true;
1742                 }
1743
1744                 if ( '' != $qv['feed'] )
1745                         $this->is_feed = true;
1746
1747                 if ( '' != $qv['embed'] ) {
1748                         $this->is_embed = true;
1749                 }
1750
1751                 if ( '' != $qv['tb'] )
1752                         $this->is_trackback = true;
1753
1754                 if ( '' != $qv['paged'] && ( intval($qv['paged']) > 1 ) )
1755                         $this->is_paged = true;
1756
1757                 // if we're previewing inside the write screen
1758                 if ( '' != $qv['preview'] )
1759                         $this->is_preview = true;
1760
1761                 if ( is_admin() )
1762                         $this->is_admin = true;
1763
1764                 if ( false !== strpos($qv['feed'], 'comments-') ) {
1765                         $qv['feed'] = str_replace('comments-', '', $qv['feed']);
1766                         $qv['withcomments'] = 1;
1767                 }
1768
1769                 $this->is_singular = $this->is_single || $this->is_page || $this->is_attachment;
1770
1771                 if ( $this->is_feed && ( !empty($qv['withcomments']) || ( empty($qv['withoutcomments']) && $this->is_singular ) ) )
1772                         $this->is_comment_feed = true;
1773
1774                 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 ) )
1775                         $this->is_home = true;
1776
1777                 // Correct is_* for page_on_front and page_for_posts
1778                 if ( $this->is_home && 'page' == get_option('show_on_front') && get_option('page_on_front') ) {
1779                         $_query = wp_parse_args($this->query);
1780                         // pagename can be set and empty depending on matched rewrite rules. Ignore an empty pagename.
1781                         if ( isset($_query['pagename']) && '' == $_query['pagename'] )
1782                                 unset($_query['pagename']);
1783
1784                         unset( $_query['embed'] );
1785
1786                         if ( empty($_query) || !array_diff( array_keys($_query), array('preview', 'page', 'paged', 'cpage') ) ) {
1787                                 $this->is_page = true;
1788                                 $this->is_home = false;
1789                                 $qv['page_id'] = get_option('page_on_front');
1790                                 // Correct <!--nextpage--> for page_on_front
1791                                 if ( !empty($qv['paged']) ) {
1792                                         $qv['page'] = $qv['paged'];
1793                                         unset($qv['paged']);
1794                                 }
1795                         }
1796                 }
1797
1798                 if ( '' != $qv['pagename'] ) {
1799                         $this->queried_object = get_page_by_path( $qv['pagename'] );
1800
1801                         if ( $this->queried_object && 'attachment' == $this->queried_object->post_type ) {
1802                                 if ( preg_match( "/^[^%]*%(?:postname)%/", get_option( 'permalink_structure' ) ) ) {
1803                                         // See if we also have a post with the same slug
1804                                         $post = get_page_by_path( $qv['pagename'], OBJECT, 'post' );
1805                                         if ( $post ) {
1806                                                 $this->queried_object = $post;
1807                                                 $this->is_page = false;
1808                                                 $this->is_single = true;
1809                                         }
1810                                 }
1811                         }
1812
1813                         if ( ! empty( $this->queried_object ) ) {
1814                                 $this->queried_object_id = (int) $this->queried_object->ID;
1815                         } else {
1816                                 unset( $this->queried_object );
1817                         }
1818
1819                         if  ( 'page' == get_option('show_on_front') && isset($this->queried_object_id) && $this->queried_object_id == get_option('page_for_posts') ) {
1820                                 $this->is_page = false;
1821                                 $this->is_home = true;
1822                                 $this->is_posts_page = true;
1823                         }
1824                 }
1825
1826                 if ( $qv['page_id'] ) {
1827                         if  ( 'page' == get_option('show_on_front') && $qv['page_id'] == get_option('page_for_posts') ) {
1828                                 $this->is_page = false;
1829                                 $this->is_home = true;
1830                                 $this->is_posts_page = true;
1831                         }
1832                 }
1833
1834                 if ( !empty($qv['post_type']) ) {
1835                         if ( is_array($qv['post_type']) )
1836                                 $qv['post_type'] = array_map('sanitize_key', $qv['post_type']);
1837                         else
1838                                 $qv['post_type'] = sanitize_key($qv['post_type']);
1839                 }
1840
1841                 if ( ! empty( $qv['post_status'] ) ) {
1842                         if ( is_array( $qv['post_status'] ) )
1843                                 $qv['post_status'] = array_map('sanitize_key', $qv['post_status']);
1844                         else
1845                                 $qv['post_status'] = preg_replace('|[^a-z0-9_,-]|', '', $qv['post_status']);
1846                 }
1847
1848                 if ( $this->is_posts_page && ( ! isset($qv['withcomments']) || ! $qv['withcomments'] ) )
1849                         $this->is_comment_feed = false;
1850
1851                 $this->is_singular = $this->is_single || $this->is_page || $this->is_attachment;
1852                 // Done correcting is_* for page_on_front and page_for_posts
1853
1854                 if ( '404' == $qv['error'] )
1855                         $this->set_404();
1856
1857                 $this->is_embed = $this->is_embed && ( $this->is_singular || $this->is_404 );
1858
1859                 $this->query_vars_hash = md5( serialize( $this->query_vars ) );
1860                 $this->query_vars_changed = false;
1861
1862                 /**
1863                  * Fires after the main query vars have been parsed.
1864                  *
1865                  * @since 1.5.0
1866                  *
1867                  * @param WP_Query &$this The WP_Query instance (passed by reference).
1868                  */
1869                 do_action_ref_array( 'parse_query', array( &$this ) );
1870         }
1871
1872         /**
1873          * Parses various taxonomy related query vars.
1874          *
1875          * For BC, this method is not marked as protected. See [28987].
1876          *
1877          * @access protected
1878          * @since 3.1.0
1879          *
1880          * @param array $q The query variables. Passed by reference.
1881          */
1882         public function parse_tax_query( &$q ) {
1883                 if ( ! empty( $q['tax_query'] ) && is_array( $q['tax_query'] ) ) {
1884                         $tax_query = $q['tax_query'];
1885                 } else {
1886                         $tax_query = array();
1887                 }
1888
1889                 if ( !empty($q['taxonomy']) && !empty($q['term']) ) {
1890                         $tax_query[] = array(
1891                                 'taxonomy' => $q['taxonomy'],
1892                                 'terms' => array( $q['term'] ),
1893                                 'field' => 'slug',
1894                         );
1895                 }
1896
1897                 foreach ( get_taxonomies( array() , 'objects' ) as $taxonomy => $t ) {
1898                         if ( 'post_tag' == $taxonomy )
1899                                 continue;       // Handled further down in the $q['tag'] block
1900
1901                         if ( $t->query_var && !empty( $q[$t->query_var] ) ) {
1902                                 $tax_query_defaults = array(
1903                                         'taxonomy' => $taxonomy,
1904                                         'field' => 'slug',
1905                                 );
1906
1907                                 if ( isset( $t->rewrite['hierarchical'] ) && $t->rewrite['hierarchical'] ) {
1908                                         $q[$t->query_var] = wp_basename( $q[$t->query_var] );
1909                                 }
1910
1911                                 $term = $q[$t->query_var];
1912
1913                                 if ( is_array( $term ) ) {
1914                                         $term = implode( ',', $term );
1915                                 }
1916
1917                                 if ( strpos($term, '+') !== false ) {
1918                                         $terms = preg_split( '/[+]+/', $term );
1919                                         foreach ( $terms as $term ) {
1920                                                 $tax_query[] = array_merge( $tax_query_defaults, array(
1921                                                         'terms' => array( $term )
1922                                                 ) );
1923                                         }
1924                                 } else {
1925                                         $tax_query[] = array_merge( $tax_query_defaults, array(
1926                                                 'terms' => preg_split( '/[,]+/', $term )
1927                                         ) );
1928                                 }
1929                         }
1930                 }
1931
1932                 // If querystring 'cat' is an array, implode it.
1933                 if ( is_array( $q['cat'] ) ) {
1934                         $q['cat'] = implode( ',', $q['cat'] );
1935                 }
1936
1937                 // Category stuff
1938                 if ( ! empty( $q['cat'] ) && ! $this->is_singular ) {
1939                         $cat_in = $cat_not_in = array();
1940
1941                         $cat_array = preg_split( '/[,\s]+/', urldecode( $q['cat'] ) );
1942                         $cat_array = array_map( 'intval', $cat_array );
1943                         $q['cat'] = implode( ',', $cat_array );
1944
1945                         foreach ( $cat_array as $cat ) {
1946                                 if ( $cat > 0 )
1947                                         $cat_in[] = $cat;
1948                                 elseif ( $cat < 0 )
1949                                         $cat_not_in[] = abs( $cat );
1950                         }
1951
1952                         if ( ! empty( $cat_in ) ) {
1953                                 $tax_query[] = array(
1954                                         'taxonomy' => 'category',
1955                                         'terms' => $cat_in,
1956                                         'field' => 'term_id',
1957                                         'include_children' => true
1958                                 );
1959                         }
1960
1961                         if ( ! empty( $cat_not_in ) ) {
1962                                 $tax_query[] = array(
1963                                         'taxonomy' => 'category',
1964                                         'terms' => $cat_not_in,
1965                                         'field' => 'term_id',
1966                                         'operator' => 'NOT IN',
1967                                         'include_children' => true
1968                                 );
1969                         }
1970                         unset( $cat_array, $cat_in, $cat_not_in );
1971                 }
1972
1973                 if ( ! empty( $q['category__and'] ) && 1 === count( (array) $q['category__and'] ) ) {
1974                         $q['category__and'] = (array) $q['category__and'];
1975                         if ( ! isset( $q['category__in'] ) )
1976                                 $q['category__in'] = array();
1977                         $q['category__in'][] = absint( reset( $q['category__and'] ) );
1978                         unset( $q['category__and'] );
1979                 }
1980
1981                 if ( ! empty( $q['category__in'] ) ) {
1982                         $q['category__in'] = array_map( 'absint', array_unique( (array) $q['category__in'] ) );
1983                         $tax_query[] = array(
1984                                 'taxonomy' => 'category',
1985                                 'terms' => $q['category__in'],
1986                                 'field' => 'term_id',
1987                                 'include_children' => false
1988                         );
1989                 }
1990
1991                 if ( ! empty($q['category__not_in']) ) {
1992                         $q['category__not_in'] = array_map( 'absint', array_unique( (array) $q['category__not_in'] ) );
1993                         $tax_query[] = array(
1994                                 'taxonomy' => 'category',
1995                                 'terms' => $q['category__not_in'],
1996                                 'operator' => 'NOT IN',
1997                                 'include_children' => false
1998                         );
1999                 }
2000
2001                 if ( ! empty($q['category__and']) ) {
2002                         $q['category__and'] = array_map( 'absint', array_unique( (array) $q['category__and'] ) );
2003                         $tax_query[] = array(
2004                                 'taxonomy' => 'category',
2005                                 'terms' => $q['category__and'],
2006                                 'field' => 'term_id',
2007                                 'operator' => 'AND',
2008                                 'include_children' => false
2009                         );
2010                 }
2011
2012                 // If querystring 'tag' is array, implode it.
2013                 if ( is_array( $q['tag'] ) ) {
2014                         $q['tag'] = implode( ',', $q['tag'] );
2015                 }
2016
2017                 // Tag stuff
2018                 if ( '' != $q['tag'] && !$this->is_singular && $this->query_vars_changed ) {
2019                         if ( strpos($q['tag'], ',') !== false ) {
2020                                 $tags = preg_split('/[,\r\n\t ]+/', $q['tag']);
2021                                 foreach ( (array) $tags as $tag ) {
2022                                         $tag = sanitize_term_field('slug', $tag, 0, 'post_tag', 'db');
2023                                         $q['tag_slug__in'][] = $tag;
2024                                 }
2025                         } elseif ( preg_match('/[+\r\n\t ]+/', $q['tag'] ) || ! empty( $q['cat'] ) ) {
2026                                 $tags = preg_split('/[+\r\n\t ]+/', $q['tag']);
2027                                 foreach ( (array) $tags as $tag ) {
2028                                         $tag = sanitize_term_field('slug', $tag, 0, 'post_tag', 'db');
2029                                         $q['tag_slug__and'][] = $tag;
2030                                 }
2031                         } else {
2032                                 $q['tag'] = sanitize_term_field('slug', $q['tag'], 0, 'post_tag', 'db');
2033                                 $q['tag_slug__in'][] = $q['tag'];
2034                         }
2035                 }
2036
2037                 if ( !empty($q['tag_id']) ) {
2038                         $q['tag_id'] = absint( $q['tag_id'] );
2039                         $tax_query[] = array(
2040                                 'taxonomy' => 'post_tag',
2041                                 'terms' => $q['tag_id']
2042                         );
2043                 }
2044
2045                 if ( !empty($q['tag__in']) ) {
2046                         $q['tag__in'] = array_map('absint', array_unique( (array) $q['tag__in'] ) );
2047                         $tax_query[] = array(
2048                                 'taxonomy' => 'post_tag',
2049                                 'terms' => $q['tag__in']
2050                         );
2051                 }
2052
2053                 if ( !empty($q['tag__not_in']) ) {
2054                         $q['tag__not_in'] = array_map('absint', array_unique( (array) $q['tag__not_in'] ) );
2055                         $tax_query[] = array(
2056                                 'taxonomy' => 'post_tag',
2057                                 'terms' => $q['tag__not_in'],
2058                                 'operator' => 'NOT IN'
2059                         );
2060                 }
2061
2062                 if ( !empty($q['tag__and']) ) {
2063                         $q['tag__and'] = array_map('absint', array_unique( (array) $q['tag__and'] ) );
2064                         $tax_query[] = array(
2065                                 'taxonomy' => 'post_tag',
2066                                 'terms' => $q['tag__and'],
2067                                 'operator' => 'AND'
2068                         );
2069                 }
2070
2071                 if ( !empty($q['tag_slug__in']) ) {
2072                         $q['tag_slug__in'] = array_map('sanitize_title_for_query', array_unique( (array) $q['tag_slug__in'] ) );
2073                         $tax_query[] = array(
2074                                 'taxonomy' => 'post_tag',
2075                                 'terms' => $q['tag_slug__in'],
2076                                 'field' => 'slug'
2077                         );
2078                 }
2079
2080                 if ( !empty($q['tag_slug__and']) ) {
2081                         $q['tag_slug__and'] = array_map('sanitize_title_for_query', array_unique( (array) $q['tag_slug__and'] ) );
2082                         $tax_query[] = array(
2083                                 'taxonomy' => 'post_tag',
2084                                 'terms' => $q['tag_slug__and'],
2085                                 'field' => 'slug',
2086                                 'operator' => 'AND'
2087                         );
2088                 }
2089
2090                 $this->tax_query = new WP_Tax_Query( $tax_query );
2091
2092                 /**
2093                  * Fires after taxonomy-related query vars have been parsed.
2094                  *
2095                  * @since 3.7.0
2096                  *
2097                  * @param WP_Query $this The WP_Query instance.
2098                  */
2099                 do_action( 'parse_tax_query', $this );
2100         }
2101
2102         /**
2103          * Generate SQL for the WHERE clause based on passed search terms.
2104          *
2105          * @since 3.7.0
2106          *
2107          * @global wpdb $wpdb WordPress database abstraction object.
2108          *
2109          * @param array $q Query variables.
2110          * @return string WHERE clause.
2111          */
2112         protected function parse_search( &$q ) {
2113                 global $wpdb;
2114
2115                 $search = '';
2116
2117                 // added slashes screw with quote grouping when done early, so done later
2118                 $q['s'] = stripslashes( $q['s'] );
2119                 if ( empty( $_GET['s'] ) && $this->is_main_query() )
2120                         $q['s'] = urldecode( $q['s'] );
2121                 // there are no line breaks in <input /> fields
2122                 $q['s'] = str_replace( array( "\r", "\n" ), '', $q['s'] );
2123                 $q['search_terms_count'] = 1;
2124                 if ( ! empty( $q['sentence'] ) ) {
2125                         $q['search_terms'] = array( $q['s'] );
2126                 } else {
2127                         if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $q['s'], $matches ) ) {
2128                                 $q['search_terms_count'] = count( $matches[0] );
2129                                 $q['search_terms'] = $this->parse_search_terms( $matches[0] );
2130                                 // if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence
2131                                 if ( empty( $q['search_terms'] ) || count( $q['search_terms'] ) > 9 )
2132                                         $q['search_terms'] = array( $q['s'] );
2133                         } else {
2134                                 $q['search_terms'] = array( $q['s'] );
2135                         }
2136                 }
2137
2138                 $n = ! empty( $q['exact'] ) ? '' : '%';
2139                 $searchand = '';
2140                 $q['search_orderby_title'] = array();
2141                 foreach ( $q['search_terms'] as $term ) {
2142                         // Terms prefixed with '-' should be excluded.
2143                         $include = '-' !== substr( $term, 0, 1 );
2144                         if ( $include ) {
2145                                 $like_op  = 'LIKE';
2146                                 $andor_op = 'OR';
2147                         } else {
2148                                 $like_op  = 'NOT LIKE';
2149                                 $andor_op = 'AND';
2150                                 $term     = substr( $term, 1 );
2151                         }
2152
2153                         if ( $n && $include ) {
2154                                 $like = '%' . $wpdb->esc_like( $term ) . '%';
2155                                 $q['search_orderby_title'][] = $wpdb->prepare( "$wpdb->posts.post_title LIKE %s", $like );
2156                         }
2157
2158                         $like = $n . $wpdb->esc_like( $term ) . $n;
2159                         $search .= $wpdb->prepare( "{$searchand}(($wpdb->posts.post_title $like_op %s) $andor_op ($wpdb->posts.post_excerpt $like_op %s) $andor_op ($wpdb->posts.post_content $like_op %s))", $like, $like, $like );
2160                         $searchand = ' AND ';
2161                 }
2162
2163                 if ( ! empty( $search ) ) {
2164                         $search = " AND ({$search}) ";
2165                         if ( ! is_user_logged_in() )
2166                                 $search .= " AND ($wpdb->posts.post_password = '') ";
2167                 }
2168
2169                 return $search;
2170         }
2171
2172         /**
2173          * Check if the terms are suitable for searching.
2174          *
2175          * Uses an array of stopwords (terms) that are excluded from the separate
2176          * term matching when searching for posts. The list of English stopwords is
2177          * the approximate search engines list, and is translatable.
2178          *
2179          * @since 3.7.0
2180          *
2181          * @param array $terms Terms to check.
2182          * @return array Terms that are not stopwords.
2183          */
2184         protected function parse_search_terms( $terms ) {
2185                 $strtolower = function_exists( 'mb_strtolower' ) ? 'mb_strtolower' : 'strtolower';
2186                 $checked = array();
2187
2188                 $stopwords = $this->get_search_stopwords();
2189
2190                 foreach ( $terms as $term ) {
2191                         // keep before/after spaces when term is for exact match
2192                         if ( preg_match( '/^".+"$/', $term ) )
2193                                 $term = trim( $term, "\"'" );
2194                         else
2195                                 $term = trim( $term, "\"' " );
2196
2197                         // Avoid single A-Z and single dashes.
2198                         if ( ! $term || ( 1 === strlen( $term ) && preg_match( '/^[a-z\-]$/i', $term ) ) )
2199                                 continue;
2200
2201                         if ( in_array( call_user_func( $strtolower, $term ), $stopwords, true ) )
2202                                 continue;
2203
2204                         $checked[] = $term;
2205                 }
2206
2207                 return $checked;
2208         }
2209
2210         /**
2211          * Retrieve stopwords used when parsing search terms.
2212          *
2213          * @since 3.7.0
2214          *
2215          * @return array Stopwords.
2216          */
2217         protected function get_search_stopwords() {
2218                 if ( isset( $this->stopwords ) )
2219                         return $this->stopwords;
2220
2221                 /* translators: This is a comma-separated list of very common words that should be excluded from a search,
2222                  * like a, an, and the. These are usually called "stopwords". You should not simply translate these individual
2223                  * words into your language. Instead, look for and provide commonly accepted stopwords in your language.
2224                  */
2225                 $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',
2226                         'Comma-separated list of search stopwords in your language' ) );
2227
2228                 $stopwords = array();
2229                 foreach ( $words as $word ) {
2230                         $word = trim( $word, "\r\n\t " );
2231                         if ( $word )
2232                                 $stopwords[] = $word;
2233                 }
2234
2235                 /**
2236                  * Filter stopwords used when parsing search terms.
2237                  *
2238                  * @since 3.7.0
2239                  *
2240                  * @param array $stopwords Stopwords.
2241                  */
2242                 $this->stopwords = apply_filters( 'wp_search_stopwords', $stopwords );
2243                 return $this->stopwords;
2244         }
2245
2246         /**
2247          * Generate SQL for the ORDER BY condition based on passed search terms.
2248          *
2249          * @global wpdb $wpdb WordPress database abstraction object.
2250          *
2251          * @param array $q Query variables.
2252          * @return string ORDER BY clause.
2253          */
2254         protected function parse_search_order( &$q ) {
2255                 global $wpdb;
2256
2257                 if ( $q['search_terms_count'] > 1 ) {
2258                         $num_terms = count( $q['search_orderby_title'] );
2259
2260                         // If the search terms contain negative queries, don't bother ordering by sentence matches.
2261                         $like = '';
2262                         if ( ! preg_match( '/(?:\s|^)\-/', $q['s'] ) ) {
2263                                 $like = '%' . $wpdb->esc_like( $q['s'] ) . '%';
2264                         }
2265
2266                         $search_orderby = '';
2267
2268                         // sentence match in 'post_title'
2269                         if ( $like ) {
2270                                 $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_title LIKE %s THEN 1 ", $like );
2271                         }
2272
2273                         // sanity limit, sort as sentence when more than 6 terms
2274                         // (few searches are longer than 6 terms and most titles are not)
2275                         if ( $num_terms < 7 ) {
2276                                 // all words in title
2277                                 $search_orderby .= 'WHEN ' . implode( ' AND ', $q['search_orderby_title'] ) . ' THEN 2 ';
2278                                 // any word in title, not needed when $num_terms == 1
2279                                 if ( $num_terms > 1 )
2280                                         $search_orderby .= 'WHEN ' . implode( ' OR ', $q['search_orderby_title'] ) . ' THEN 3 ';
2281                         }
2282
2283                         // Sentence match in 'post_content' and 'post_excerpt'.
2284                         if ( $like ) {
2285                                 $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_excerpt LIKE %s THEN 4 ", $like );
2286                                 $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_content LIKE %s THEN 5 ", $like );
2287                         }
2288
2289                         if ( $search_orderby ) {
2290                                 $search_orderby = '(CASE ' . $search_orderby . 'ELSE 6 END)';
2291                         }
2292                 } else {
2293                         // single word or sentence search
2294                         $search_orderby = reset( $q['search_orderby_title'] ) . ' DESC';
2295                 }
2296
2297                 return $search_orderby;
2298         }
2299
2300         /**
2301          * If the passed orderby value is allowed, convert the alias to a
2302          * properly-prefixed orderby value.
2303          *
2304          * @since 4.0.0
2305          * @access protected
2306          *
2307          * @global wpdb $wpdb WordPress database abstraction object.
2308          *
2309          * @param string $orderby Alias for the field to order by.
2310          * @return string|false Table-prefixed value to used in the ORDER clause. False otherwise.
2311          */
2312         protected function parse_orderby( $orderby ) {
2313                 global $wpdb;
2314
2315                 // Used to filter values.
2316                 $allowed_keys = array(
2317                         'post_name', 'post_author', 'post_date', 'post_title', 'post_modified',
2318                         'post_parent', 'post_type', 'name', 'author', 'date', 'title', 'modified',
2319                         'parent', 'type', 'ID', 'menu_order', 'comment_count', 'rand',
2320                 );
2321
2322                 $primary_meta_key = '';
2323                 $primary_meta_query = false;
2324                 $meta_clauses = $this->meta_query->get_clauses();
2325                 if ( ! empty( $meta_clauses ) ) {
2326                         $primary_meta_query = reset( $meta_clauses );
2327
2328                         if ( ! empty( $primary_meta_query['key'] ) ) {
2329                                 $primary_meta_key = $primary_meta_query['key'];
2330                                 $allowed_keys[] = $primary_meta_key;
2331                         }
2332
2333                         $allowed_keys[] = 'meta_value';
2334                         $allowed_keys[] = 'meta_value_num';
2335                         $allowed_keys   = array_merge( $allowed_keys, array_keys( $meta_clauses ) );
2336                 }
2337
2338                 // If RAND() contains a seed value, sanitize and add to allowed keys.
2339                 $rand_with_seed = false;
2340                 if ( preg_match( '/RAND\(([0-9]+)\)/i', $orderby, $matches ) ) {
2341                         $orderby = sprintf( 'RAND(%s)', intval( $matches[1] ) );
2342                         $allowed_keys[] = $orderby;
2343                         $rand_with_seed = true;
2344                 }
2345
2346                 if ( ! in_array( $orderby, $allowed_keys, true ) ) {
2347                         return false;
2348                 }
2349
2350                 switch ( $orderby ) {
2351                         case 'post_name':
2352                         case 'post_author':
2353                         case 'post_date':
2354                         case 'post_title':
2355                         case 'post_modified':
2356                         case 'post_parent':
2357                         case 'post_type':
2358                         case 'ID':
2359                         case 'menu_order':
2360                         case 'comment_count':
2361                                 $orderby_clause = "$wpdb->posts.{$orderby}";
2362                                 break;
2363                         case 'rand':
2364                                 $orderby_clause = 'RAND()';
2365                                 break;
2366                         case $primary_meta_key:
2367                         case 'meta_value':
2368                                 if ( ! empty( $primary_meta_query['type'] ) ) {
2369                                         $orderby_clause = "CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})";
2370                                 } else {
2371                                         $orderby_clause = "{$primary_meta_query['alias']}.meta_value";
2372                                 }
2373                                 break;
2374                         case 'meta_value_num':
2375                                 $orderby_clause = "{$primary_meta_query['alias']}.meta_value+0";
2376                                 break;
2377                         default:
2378                                 if ( array_key_exists( $orderby, $meta_clauses ) ) {
2379                                         // $orderby corresponds to a meta_query clause.
2380                                         $meta_clause = $meta_clauses[ $orderby ];
2381                                         $orderby_clause = "CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})";
2382                                 } elseif ( $rand_with_seed ) {
2383                                         $orderby_clause = $orderby;
2384                                 } else {
2385                                         // Default: order by post field.
2386                                         $orderby_clause = "$wpdb->posts.post_" . sanitize_key( $orderby );
2387                                 }
2388
2389                                 break;
2390                 }
2391
2392                 return $orderby_clause;
2393         }
2394
2395         /**
2396          * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
2397          *
2398          * @since 4.0.0
2399          * @access protected
2400          *
2401          * @param string $order The 'order' query variable.
2402          * @return string The sanitized 'order' query variable.
2403          */
2404         protected function parse_order( $order ) {
2405                 if ( ! is_string( $order ) || empty( $order ) ) {
2406                         return 'DESC';
2407                 }
2408
2409                 if ( 'ASC' === strtoupper( $order ) ) {
2410                         return 'ASC';
2411                 } else {
2412                         return 'DESC';
2413                 }
2414         }
2415
2416         /**
2417          * Sets the 404 property and saves whether query is feed.
2418          *
2419          * @since 2.0.0
2420          * @access public
2421          */
2422         public function set_404() {
2423                 $is_feed = $this->is_feed;
2424
2425                 $this->init_query_flags();
2426                 $this->is_404 = true;
2427
2428                 $this->is_feed = $is_feed;
2429         }
2430
2431         /**
2432          * Retrieve query variable.
2433          *
2434          * @since 1.5.0
2435          * @since 3.9.0 The `$default` argument was introduced.
2436          *
2437          * @access public
2438          *
2439          * @param string $query_var Query variable key.
2440          * @param mixed  $default   Optional. Value to return if the query variable is not set. Default empty.
2441          * @return mixed Contents of the query variable.
2442          */
2443         public function get( $query_var, $default = '' ) {
2444                 if ( isset( $this->query_vars[ $query_var ] ) ) {
2445                         return $this->query_vars[ $query_var ];
2446                 }
2447
2448                 return $default;
2449         }
2450
2451         /**
2452          * Set query variable.
2453          *
2454          * @since 1.5.0
2455          * @access public
2456          *
2457          * @param string $query_var Query variable key.
2458          * @param mixed  $value     Query variable value.
2459          */
2460         public function set($query_var, $value) {
2461                 $this->query_vars[$query_var] = $value;
2462         }
2463
2464         /**
2465          * Retrieve the posts based on query variables.
2466          *
2467          * There are a few filters and actions that can be used to modify the post
2468          * database query.
2469          *
2470          * @since 1.5.0
2471          * @access public
2472          *
2473          * @global wpdb $wpdb WordPress database abstraction object.
2474          *
2475          * @return array List of posts.
2476          */
2477         public function get_posts() {
2478                 global $wpdb;
2479
2480                 $this->parse_query();
2481
2482                 /**
2483                  * Fires after the query variable object is created, but before the actual query is run.
2484                  *
2485                  * Note: If using conditional tags, use the method versions within the passed instance
2486                  * (e.g. $this->is_main_query() instead of is_main_query()). This is because the functions
2487                  * like is_main_query() test against the global $wp_query instance, not the passed one.
2488                  *
2489                  * @since 2.0.0
2490                  *
2491                  * @param WP_Query &$this The WP_Query instance (passed by reference).
2492                  */
2493                 do_action_ref_array( 'pre_get_posts', array( &$this ) );
2494
2495                 // Shorthand.
2496                 $q = &$this->query_vars;
2497
2498                 // Fill again in case pre_get_posts unset some vars.
2499                 $q = $this->fill_query_vars($q);
2500
2501                 // Parse meta query
2502                 $this->meta_query = new WP_Meta_Query();
2503                 $this->meta_query->parse_query_vars( $q );
2504
2505                 // Set a flag if a pre_get_posts hook changed the query vars.
2506                 $hash = md5( serialize( $this->query_vars ) );
2507                 if ( $hash != $this->query_vars_hash ) {
2508                         $this->query_vars_changed = true;
2509                         $this->query_vars_hash = $hash;
2510                 }
2511                 unset($hash);
2512
2513                 // First let's clear some variables
2514                 $distinct = '';
2515                 $whichauthor = '';
2516                 $whichmimetype = '';
2517                 $where = '';
2518                 $limits = '';
2519                 $join = '';
2520                 $search = '';
2521                 $groupby = '';
2522                 $post_status_join = false;
2523                 $page = 1;
2524
2525                 if ( isset( $q['caller_get_posts'] ) ) {
2526                         _deprecated_argument( 'WP_Query', '3.1', __( '"caller_get_posts" is deprecated. Use "ignore_sticky_posts" instead.' ) );
2527                         if ( !isset( $q['ignore_sticky_posts'] ) )
2528                                 $q['ignore_sticky_posts'] = $q['caller_get_posts'];
2529                 }
2530
2531                 if ( !isset( $q['ignore_sticky_posts'] ) )
2532                         $q['ignore_sticky_posts'] = false;
2533
2534                 if ( !isset($q['suppress_filters']) )
2535                         $q['suppress_filters'] = false;
2536
2537                 if ( !isset($q['cache_results']) ) {
2538                         if ( wp_using_ext_object_cache() )
2539                                 $q['cache_results'] = false;
2540                         else
2541                                 $q['cache_results'] = true;
2542                 }
2543
2544                 if ( !isset($q['update_post_term_cache']) )
2545                         $q['update_post_term_cache'] = true;
2546
2547                 if ( !isset($q['update_post_meta_cache']) )
2548                         $q['update_post_meta_cache'] = true;
2549
2550                 if ( !isset($q['post_type']) ) {
2551                         if ( $this->is_search )
2552                                 $q['post_type'] = 'any';
2553                         else
2554                                 $q['post_type'] = '';
2555                 }
2556                 $post_type = $q['post_type'];
2557                 if ( empty( $q['posts_per_page'] ) ) {
2558                         $q['posts_per_page'] = get_option( 'posts_per_page' );
2559                 }
2560                 if ( isset($q['showposts']) && $q['showposts'] ) {
2561                         $q['showposts'] = (int) $q['showposts'];
2562                         $q['posts_per_page'] = $q['showposts'];
2563                 }
2564                 if ( (isset($q['posts_per_archive_page']) && $q['posts_per_archive_page'] != 0) && ($this->is_archive || $this->is_search) )
2565                         $q['posts_per_page'] = $q['posts_per_archive_page'];
2566                 if ( !isset($q['nopaging']) ) {
2567                         if ( $q['posts_per_page'] == -1 ) {
2568                                 $q['nopaging'] = true;
2569                         } else {
2570                                 $q['nopaging'] = false;
2571                         }
2572                 }
2573
2574                 if ( $this->is_feed ) {
2575                         // This overrides posts_per_page.
2576                         if ( ! empty( $q['posts_per_rss'] ) ) {
2577                                 $q['posts_per_page'] = $q['posts_per_rss'];
2578                         } else {
2579                                 $q['posts_per_page'] = get_option( 'posts_per_rss' );
2580                         }
2581                         $q['nopaging'] = false;
2582                 }
2583                 $q['posts_per_page'] = (int) $q['posts_per_page'];
2584                 if ( $q['posts_per_page'] < -1 )
2585                         $q['posts_per_page'] = abs($q['posts_per_page']);
2586                 elseif ( $q['posts_per_page'] == 0 )
2587                         $q['posts_per_page'] = 1;
2588
2589                 if ( !isset($q['comments_per_page']) || $q['comments_per_page'] == 0 )
2590                         $q['comments_per_page'] = get_option('comments_per_page');
2591
2592                 if ( $this->is_home && (empty($this->query) || $q['preview'] == 'true') && ( 'page' == get_option('show_on_front') ) && get_option('page_on_front') ) {
2593                         $this->is_page = true;
2594                         $this->is_home = false;
2595                         $q['page_id'] = get_option('page_on_front');
2596                 }
2597
2598                 if ( isset($q['page']) ) {
2599                         $q['page'] = trim($q['page'], '/');
2600                         $q['page'] = absint($q['page']);
2601                 }
2602
2603                 // If true, forcibly turns off SQL_CALC_FOUND_ROWS even when limits are present.
2604                 if ( isset($q['no_found_rows']) )
2605                         $q['no_found_rows'] = (bool) $q['no_found_rows'];
2606                 else
2607                         $q['no_found_rows'] = false;
2608
2609                 switch ( $q['fields'] ) {
2610                         case 'ids':
2611                                 $fields = "$wpdb->posts.ID";
2612                                 break;
2613                         case 'id=>parent':
2614                                 $fields = "$wpdb->posts.ID, $wpdb->posts.post_parent";
2615                                 break;
2616                         default:
2617                                 $fields = "$wpdb->posts.*";
2618                 }
2619
2620                 if ( '' !== $q['menu_order'] )
2621                         $where .= " AND $wpdb->posts.menu_order = " . $q['menu_order'];
2622
2623                 // The "m" parameter is meant for months but accepts datetimes of varying specificity
2624                 if ( $q['m'] ) {
2625                         $where .= " AND YEAR($wpdb->posts.post_date)=" . substr($q['m'], 0, 4);
2626                         if ( strlen($q['m']) > 5 )
2627                                 $where .= " AND MONTH($wpdb->posts.post_date)=" . substr($q['m'], 4, 2);
2628                         if ( strlen($q['m']) > 7 )
2629                                 $where .= " AND DAYOFMONTH($wpdb->posts.post_date)=" . substr($q['m'], 6, 2);
2630                         if ( strlen($q['m']) > 9 )
2631                                 $where .= " AND HOUR($wpdb->posts.post_date)=" . substr($q['m'], 8, 2);
2632                         if ( strlen($q['m']) > 11 )
2633                                 $where .= " AND MINUTE($wpdb->posts.post_date)=" . substr($q['m'], 10, 2);
2634                         if ( strlen($q['m']) > 13 )
2635                                 $where .= " AND SECOND($wpdb->posts.post_date)=" . substr($q['m'], 12, 2);
2636                 }
2637
2638                 // Handle the other individual date parameters
2639                 $date_parameters = array();
2640
2641                 if ( '' !== $q['hour'] )
2642                         $date_parameters['hour'] = $q['hour'];
2643
2644                 if ( '' !== $q['minute'] )
2645                         $date_parameters['minute'] = $q['minute'];
2646
2647                 if ( '' !== $q['second'] )
2648                         $date_parameters['second'] = $q['second'];
2649
2650                 if ( $q['year'] )
2651                         $date_parameters['year'] = $q['year'];
2652
2653                 if ( $q['monthnum'] )
2654                         $date_parameters['monthnum'] = $q['monthnum'];
2655
2656                 if ( $q['w'] )
2657                         $date_parameters['week'] = $q['w'];
2658
2659                 if ( $q['day'] )
2660                         $date_parameters['day'] = $q['day'];
2661
2662                 if ( $date_parameters ) {
2663                         $date_query = new WP_Date_Query( array( $date_parameters ) );
2664                         $where .= $date_query->get_sql();
2665                 }
2666                 unset( $date_parameters, $date_query );
2667
2668                 // Handle complex date queries
2669                 if ( ! empty( $q['date_query'] ) ) {
2670                         $this->date_query = new WP_Date_Query( $q['date_query'] );
2671                         $where .= $this->date_query->get_sql();
2672                 }
2673
2674
2675                 // If we've got a post_type AND it's not "any" post_type.
2676                 if ( !empty($q['post_type']) && 'any' != $q['post_type'] ) {
2677                         foreach ( (array)$q['post_type'] as $_post_type ) {
2678                                 $ptype_obj = get_post_type_object($_post_type);
2679                                 if ( !$ptype_obj || !$ptype_obj->query_var || empty($q[ $ptype_obj->query_var ]) )
2680                                         continue;
2681
2682                                 if ( ! $ptype_obj->hierarchical ) {
2683                                         // Non-hierarchical post types can directly use 'name'.
2684                                         $q['name'] = $q[ $ptype_obj->query_var ];
2685                                 } else {
2686                                         // Hierarchical post types will operate through 'pagename'.
2687                                         $q['pagename'] = $q[ $ptype_obj->query_var ];
2688                                         $q['name'] = '';
2689                                 }
2690
2691                                 // Only one request for a slug is possible, this is why name & pagename are overwritten above.
2692                                 break;
2693                         } //end foreach
2694                         unset($ptype_obj);
2695                 }
2696
2697                 if ( '' !== $q['title'] ) {
2698                         $where .= $wpdb->prepare( " AND $wpdb->posts.post_title = %s", stripslashes( $q['title'] ) );
2699                 }
2700
2701                 // Parameters related to 'post_name'.
2702                 if ( '' != $q['name'] ) {
2703                         $q['name'] = sanitize_title_for_query( $q['name'] );
2704                         $where .= " AND $wpdb->posts.post_name = '" . $q['name'] . "'";
2705                 } elseif ( '' != $q['pagename'] ) {
2706                         if ( isset($this->queried_object_id) ) {
2707                                 $reqpage = $this->queried_object_id;
2708                         } else {
2709                                 if ( 'page' != $q['post_type'] ) {
2710                                         foreach ( (array)$q['post_type'] as $_post_type ) {
2711                                                 $ptype_obj = get_post_type_object($_post_type);
2712                                                 if ( !$ptype_obj || !$ptype_obj->hierarchical )
2713                                                         continue;
2714
2715                                                 $reqpage = get_page_by_path($q['pagename'], OBJECT, $_post_type);
2716                                                 if ( $reqpage )
2717                                                         break;
2718                                         }
2719                                         unset($ptype_obj);
2720                                 } else {
2721                                         $reqpage = get_page_by_path($q['pagename']);
2722                                 }
2723                                 if ( !empty($reqpage) )
2724                                         $reqpage = $reqpage->ID;
2725                                 else
2726                                         $reqpage = 0;
2727                         }
2728
2729                         $page_for_posts = get_option('page_for_posts');
2730                         if  ( ('page' != get_option('show_on_front') ) || empty($page_for_posts) || ( $reqpage != $page_for_posts ) ) {
2731                                 $q['pagename'] = sanitize_title_for_query( wp_basename( $q['pagename'] ) );
2732                                 $q['name'] = $q['pagename'];
2733                                 $where .= " AND ($wpdb->posts.ID = '$reqpage')";
2734                                 $reqpage_obj = get_post( $reqpage );
2735                                 if ( is_object($reqpage_obj) && 'attachment' == $reqpage_obj->post_type ) {
2736                                         $this->is_attachment = true;
2737                                         $post_type = $q['post_type'] = 'attachment';
2738                                         $this->is_page = true;
2739                                         $q['attachment_id'] = $reqpage;
2740                                 }
2741                         }
2742                 } elseif ( '' != $q['attachment'] ) {
2743                         $q['attachment'] = sanitize_title_for_query( wp_basename( $q['attachment'] ) );
2744                         $q['name'] = $q['attachment'];
2745                         $where .= " AND $wpdb->posts.post_name = '" . $q['attachment'] . "'";
2746                 } elseif ( is_array( $q['post_name__in'] ) && ! empty( $q['post_name__in'] ) ) {
2747                         $q['post_name__in'] = array_map( 'sanitize_title_for_query', $q['post_name__in'] );
2748                         $where .= " AND $wpdb->posts.post_name IN ('" . implode( "' ,'", $q['post_name__in'] ) . "')";
2749                 }
2750
2751                 // If an attachment is requested by number, let it supersede any post number.
2752                 if ( $q['attachment_id'] )
2753                         $q['p'] = absint($q['attachment_id']);
2754
2755                 // If a post number is specified, load that post
2756                 if ( $q['p'] ) {
2757                         $where .= " AND {$wpdb->posts}.ID = " . $q['p'];
2758                 } elseif ( $q['post__in'] ) {
2759                         $post__in = implode(',', array_map( 'absint', $q['post__in'] ));
2760                         $where .= " AND {$wpdb->posts}.ID IN ($post__in)";
2761                 } elseif ( $q['post__not_in'] ) {
2762                         $post__not_in = implode(',',  array_map( 'absint', $q['post__not_in'] ));
2763                         $where .= " AND {$wpdb->posts}.ID NOT IN ($post__not_in)";
2764                 }
2765
2766                 if ( is_numeric( $q['post_parent'] ) ) {
2767                         $where .= $wpdb->prepare( " AND $wpdb->posts.post_parent = %d ", $q['post_parent'] );
2768                 } elseif ( $q['post_parent__in'] ) {
2769                         $post_parent__in = implode( ',', array_map( 'absint', $q['post_parent__in'] ) );
2770                         $where .= " AND {$wpdb->posts}.post_parent IN ($post_parent__in)";
2771                 } elseif ( $q['post_parent__not_in'] ) {
2772                         $post_parent__not_in = implode( ',',  array_map( 'absint', $q['post_parent__not_in'] ) );
2773                         $where .= " AND {$wpdb->posts}.post_parent NOT IN ($post_parent__not_in)";
2774                 }
2775
2776                 if ( $q['page_id'] ) {
2777                         if  ( ('page' != get_option('show_on_front') ) || ( $q['page_id'] != get_option('page_for_posts') ) ) {
2778                                 $q['p'] = $q['page_id'];
2779                                 $where = " AND {$wpdb->posts}.ID = " . $q['page_id'];
2780                         }
2781                 }
2782
2783                 // If a search pattern is specified, load the posts that match.
2784                 if ( strlen( $q['s'] ) ) {
2785                         $search = $this->parse_search( $q );
2786                 }
2787
2788                 if ( ! $q['suppress_filters'] ) {
2789                         /**
2790                          * Filter the search SQL that is used in the WHERE clause of WP_Query.
2791                          *
2792                          * @since 3.0.0
2793                          *
2794                          * @param string   $search Search SQL for WHERE clause.
2795                          * @param WP_Query $this   The current WP_Query object.
2796                          */
2797                         $search = apply_filters_ref_array( 'posts_search', array( $search, &$this ) );
2798                 }
2799
2800                 // Taxonomies
2801                 if ( !$this->is_singular ) {
2802                         $this->parse_tax_query( $q );
2803
2804                         $clauses = $this->tax_query->get_sql( $wpdb->posts, 'ID' );
2805
2806                         $join .= $clauses['join'];
2807                         $where .= $clauses['where'];
2808                 }
2809
2810                 if ( $this->is_tax ) {
2811                         if ( empty($post_type) ) {
2812                                 // Do a fully inclusive search for currently registered post types of queried taxonomies
2813                                 $post_type = array();
2814                                 $taxonomies = array_keys( $this->tax_query->queried_terms );
2815                                 foreach ( get_post_types( array( 'exclude_from_search' => false ) ) as $pt ) {
2816                                         $object_taxonomies = $pt === 'attachment' ? get_taxonomies_for_attachments() : get_object_taxonomies( $pt );
2817                                         if ( array_intersect( $taxonomies, $object_taxonomies ) )
2818                                                 $post_type[] = $pt;
2819                                 }
2820                                 if ( ! $post_type )
2821                                         $post_type = 'any';
2822                                 elseif ( count( $post_type ) == 1 )
2823                                         $post_type = $post_type[0];
2824
2825                                 $post_status_join = true;
2826                         } elseif ( in_array('attachment', (array) $post_type) ) {
2827                                 $post_status_join = true;
2828                         }
2829                 }
2830
2831                 /*
2832                  * Ensure that 'taxonomy', 'term', 'term_id', 'cat', and
2833                  * 'category_name' vars are set for backward compatibility.
2834                  */
2835                 if ( ! empty( $this->tax_query->queried_terms ) ) {
2836
2837                         /*
2838                          * Set 'taxonomy', 'term', and 'term_id' to the
2839                          * first taxonomy other than 'post_tag' or 'category'.
2840                          */
2841                         if ( ! isset( $q['taxonomy'] ) ) {
2842                                 foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
2843                                         if ( empty( $queried_items['terms'][0] ) ) {
2844                                                 continue;
2845                                         }
2846
2847                                         if ( ! in_array( $queried_taxonomy, array( 'category', 'post_tag' ) ) ) {
2848                                                 $q['taxonomy'] = $queried_taxonomy;
2849
2850                                                 if ( 'slug' === $queried_items['field'] ) {
2851                                                         $q['term'] = $queried_items['terms'][0];
2852                                                 } else {
2853                                                         $q['term_id'] = $queried_items['terms'][0];
2854                                                 }
2855
2856                                                 // Take the first one we find.
2857                                                 break;
2858                                         }
2859                                 }
2860                         }
2861
2862                         // 'cat', 'category_name', 'tag_id'
2863                         foreach ( $this->tax_query->queried_terms as $queried_taxonomy => $queried_items ) {
2864                                 if ( empty( $queried_items['terms'][0] ) ) {
2865                                         continue;
2866                                 }
2867
2868                                 if ( 'category' === $queried_taxonomy ) {
2869                                         $the_cat = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'category' );
2870                                         if ( $the_cat ) {
2871                                                 $this->set( 'cat', $the_cat->term_id );
2872                                                 $this->set( 'category_name', $the_cat->slug );
2873                                         }
2874                                         unset( $the_cat );
2875                                 }
2876
2877                                 if ( 'post_tag' === $queried_taxonomy ) {
2878                                         $the_tag = get_term_by( $queried_items['field'], $queried_items['terms'][0], 'post_tag' );
2879                                         if ( $the_tag ) {
2880                                                 $this->set( 'tag_id', $the_tag->term_id );
2881                                         }
2882                                         unset( $the_tag );
2883                                 }
2884                         }
2885                 }
2886
2887                 if ( !empty( $this->tax_query->queries ) || !empty( $this->meta_query->queries ) ) {
2888                         $groupby = "{$wpdb->posts}.ID";
2889                 }
2890
2891                 // Author/user stuff
2892
2893                 if ( ! empty( $q['author'] ) && $q['author'] != '0' ) {
2894                         $q['author'] = addslashes_gpc( '' . urldecode( $q['author'] ) );
2895                         $authors = array_unique( array_map( 'intval', preg_split( '/[,\s]+/', $q['author'] ) ) );
2896                         foreach ( $authors as $author ) {
2897                                 $key = $author > 0 ? 'author__in' : 'author__not_in';
2898                                 $q[$key][] = abs( $author );
2899                         }
2900                         $q['author'] = implode( ',', $authors );
2901                 }
2902
2903                 if ( ! empty( $q['author__not_in'] ) ) {
2904                         $author__not_in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__not_in'] ) ) );
2905                         $where .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";
2906                 } elseif ( ! empty( $q['author__in'] ) ) {
2907                         $author__in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__in'] ) ) );
2908                         $where .= " AND {$wpdb->posts}.post_author IN ($author__in) ";
2909                 }
2910
2911                 // Author stuff for nice URLs
2912
2913                 if ( '' != $q['author_name'] ) {
2914                         if ( strpos($q['author_name'], '/') !== false ) {
2915                                 $q['author_name'] = explode('/', $q['author_name']);
2916                                 if ( $q['author_name'][ count($q['author_name'])-1 ] ) {
2917                                         $q['author_name'] = $q['author_name'][count($q['author_name'])-1]; // no trailing slash
2918                                 } else {
2919                                         $q['author_name'] = $q['author_name'][count($q['author_name'])-2]; // there was a trailing slash
2920                                 }
2921                         }
2922                         $q['author_name'] = sanitize_title_for_query( $q['author_name'] );
2923                         $q['author'] = get_user_by('slug', $q['author_name']);
2924                         if ( $q['author'] )
2925                                 $q['author'] = $q['author']->ID;
2926                         $whichauthor .= " AND ($wpdb->posts.post_author = " . absint($q['author']) . ')';
2927                 }
2928
2929                 // MIME-Type stuff for attachment browsing
2930
2931                 if ( isset( $q['post_mime_type'] ) && '' != $q['post_mime_type'] )
2932                         $whichmimetype = wp_post_mime_type_where( $q['post_mime_type'], $wpdb->posts );
2933
2934                 $where .= $search . $whichauthor . $whichmimetype;
2935
2936                 if ( ! empty( $this->meta_query->queries ) ) {
2937                         $clauses = $this->meta_query->get_sql( 'post', $wpdb->posts, 'ID', $this );
2938                         $join   .= $clauses['join'];
2939                         $where  .= $clauses['where'];
2940                 }
2941
2942                 $rand = ( isset( $q['orderby'] ) && 'rand' === $q['orderby'] );
2943                 if ( ! isset( $q['order'] ) ) {
2944                         $q['order'] = $rand ? '' : 'DESC';
2945                 } else {
2946                         $q['order'] = $rand ? '' : $this->parse_order( $q['order'] );
2947                 }
2948
2949                 // Order by.
2950                 if ( empty( $q['orderby'] ) ) {
2951                         /*
2952                          * Boolean false or empty array blanks out ORDER BY,
2953                          * while leaving the value unset or otherwise empty sets the default.
2954                          */
2955                         if ( isset( $q['orderby'] ) && ( is_array( $q['orderby'] ) || false === $q['orderby'] ) ) {
2956                                 $orderby = '';
2957                         } else {
2958                                 $orderby = "$wpdb->posts.post_date " . $q['order'];
2959                         }
2960                 } elseif ( 'none' == $q['orderby'] ) {
2961                         $orderby = '';
2962                 } elseif ( $q['orderby'] == 'post__in' && ! empty( $post__in ) ) {
2963                         $orderby = "FIELD( {$wpdb->posts}.ID, $post__in )";
2964                 } elseif ( $q['orderby'] == 'post_parent__in' && ! empty( $post_parent__in ) ) {
2965                         $orderby = "FIELD( {$wpdb->posts}.post_parent, $post_parent__in )";
2966                 } else {
2967                         $orderby_array = array();
2968                         if ( is_array( $q['orderby'] ) ) {
2969                                 foreach ( $q['orderby'] as $_orderby => $order ) {
2970                                         $orderby = addslashes_gpc( urldecode( $_orderby ) );
2971                                         $parsed  = $this->parse_orderby( $orderby );
2972
2973                                         if ( ! $parsed ) {
2974                                                 continue;
2975                                         }
2976
2977                                         $orderby_array[] = $parsed . ' ' . $this->parse_order( $order );
2978                                 }
2979                                 $orderby = implode( ', ', $orderby_array );
2980
2981                         } else {
2982                                 $q['orderby'] = urldecode( $q['orderby'] );
2983                                 $q['orderby'] = addslashes_gpc( $q['orderby'] );
2984
2985                                 foreach ( explode( ' ', $q['orderby'] ) as $i => $orderby ) {
2986                                         $parsed = $this->parse_orderby( $orderby );
2987                                         // Only allow certain values for safety.
2988                                         if ( ! $parsed ) {
2989                                                 continue;
2990                                         }
2991
2992                                         $orderby_array[] = $parsed;
2993                                 }
2994                                 $orderby = implode( ' ' . $q['order'] . ', ', $orderby_array );
2995
2996                                 if ( empty( $orderby ) ) {
2997                                         $orderby = "$wpdb->posts.post_date " . $q['order'];
2998                                 } elseif ( ! empty( $q['order'] ) ) {
2999                                         $orderby .= " {$q['order']}";
3000                                 }
3001                         }
3002                 }
3003
3004                 // Order search results by relevance only when another "orderby" is not specified in the query.
3005                 if ( ! empty( $q['s'] ) ) {
3006                         $search_orderby = '';
3007                         if ( ! empty( $q['search_orderby_title'] ) && ( empty( $q['orderby'] ) && ! $this->is_feed ) || ( isset( $q['orderby'] ) && 'relevance' === $q['orderby'] ) )
3008                                 $search_orderby = $this->parse_search_order( $q );
3009
3010                         if ( ! $q['suppress_filters'] ) {
3011                                 /**
3012                                  * Filter the ORDER BY used when ordering search results.
3013                                  *
3014                                  * @since 3.7.0
3015                                  *
3016                                  * @param string   $search_orderby The ORDER BY clause.
3017                                  * @param WP_Query $this           The current WP_Query instance.
3018                                  */
3019                                 $search_orderby = apply_filters( 'posts_search_orderby', $search_orderby, $this );
3020                         }
3021
3022                         if ( $search_orderby )
3023                                 $orderby = $orderby ? $search_orderby . ', ' . $orderby : $search_orderby;
3024                 }
3025
3026                 if ( is_array( $post_type ) && count( $post_type ) > 1 ) {
3027                         $post_type_cap = 'multiple_post_type';
3028                 } else {
3029                         if ( is_array( $post_type ) )
3030                                 $post_type = reset( $post_type );
3031                         $post_type_object = get_post_type_object( $post_type );
3032                         if ( empty( $post_type_object ) )
3033                                 $post_type_cap = $post_type;
3034                 }
3035
3036                 if ( isset( $q['post_password'] ) ) {
3037                         $where .= $wpdb->prepare( " AND $wpdb->posts.post_password = %s", $q['post_password'] );
3038                         if ( empty( $q['perm'] ) ) {
3039                                 $q['perm'] = 'readable';
3040                         }
3041                 } elseif ( isset( $q['has_password'] ) ) {
3042                         $where .= sprintf( " AND $wpdb->posts.post_password %s ''", $q['has_password'] ? '!=' : '=' );
3043                 }
3044
3045                 if ( ! empty( $q['comment_status'] ) ) {
3046                         $where .= $wpdb->prepare( " AND $wpdb->posts.comment_status = %s ", $q['comment_status'] );
3047                 }
3048
3049                 if ( ! empty( $q['ping_status'] ) )  {
3050                         $where .= $wpdb->prepare( " AND $wpdb->posts.ping_status = %s ", $q['ping_status'] );
3051                 }
3052
3053                 if ( 'any' == $post_type ) {
3054                         $in_search_post_types = get_post_types( array('exclude_from_search' => false) );
3055                         if ( empty( $in_search_post_types ) )
3056                                 $where .= ' AND 1=0 ';
3057                         else
3058                                 $where .= " AND $wpdb->posts.post_type IN ('" . join("', '", $in_search_post_types ) . "')";
3059                 } elseif ( !empty( $post_type ) && is_array( $post_type ) ) {
3060                         $where .= " AND $wpdb->posts.post_type IN ('" . join("', '", $post_type) . "')";
3061                 } elseif ( ! empty( $post_type ) ) {
3062                         $where .= " AND $wpdb->posts.post_type = '$post_type'";
3063                         $post_type_object = get_post_type_object ( $post_type );
3064                 } elseif ( $this->is_attachment ) {
3065                         $where .= " AND $wpdb->posts.post_type = 'attachment'";
3066                         $post_type_object = get_post_type_object ( 'attachment' );
3067                 } elseif ( $this->is_page ) {
3068                         $where .= " AND $wpdb->posts.post_type = 'page'";
3069                         $post_type_object = get_post_type_object ( 'page' );
3070                 } else {
3071                         $where .= " AND $wpdb->posts.post_type = 'post'";
3072                         $post_type_object = get_post_type_object ( 'post' );
3073                 }
3074
3075                 $edit_cap = 'edit_post';
3076                 $read_cap = 'read_post';
3077
3078                 if ( ! empty( $post_type_object ) ) {
3079                         $edit_others_cap = $post_type_object->cap->edit_others_posts;
3080                         $read_private_cap = $post_type_object->cap->read_private_posts;
3081                 } else {
3082                         $edit_others_cap = 'edit_others_' . $post_type_cap . 's';
3083                         $read_private_cap = 'read_private_' . $post_type_cap . 's';
3084                 }
3085
3086                 $user_id = get_current_user_id();
3087
3088                 $q_status = array();
3089                 if ( ! empty( $q['post_status'] ) ) {
3090                         $statuswheres = array();
3091                         $q_status = $q['post_status'];
3092                         if ( ! is_array( $q_status ) )
3093                                 $q_status = explode(',', $q_status);
3094                         $r_status = array();
3095                         $p_status = array();
3096                         $e_status = array();
3097                         if ( in_array( 'any', $q_status ) ) {
3098                                 foreach ( get_post_stati( array( 'exclude_from_search' => true ) ) as $status ) {
3099                                         if ( ! in_array( $status, $q_status ) ) {
3100                                                 $e_status[] = "$wpdb->posts.post_status <> '$status'";
3101                                         }
3102                                 }
3103                         } else {
3104                                 foreach ( get_post_stati() as $status ) {
3105                                         if ( in_array( $status, $q_status ) ) {
3106                                                 if ( 'private' == $status )
3107                                                         $p_status[] = "$wpdb->posts.post_status = '$status'";
3108                                                 else
3109                                                         $r_status[] = "$wpdb->posts.post_status = '$status'";
3110                                         }
3111                                 }
3112                         }
3113
3114                         if ( empty($q['perm'] ) || 'readable' != $q['perm'] ) {
3115                                 $r_status = array_merge($r_status, $p_status);
3116                                 unset($p_status);
3117                         }
3118
3119                         if ( !empty($e_status) ) {
3120                                 $statuswheres[] = "(" . join( ' AND ', $e_status ) . ")";
3121                         }
3122                         if ( !empty($r_status) ) {
3123                                 if ( !empty($q['perm'] ) && 'editable' == $q['perm'] && !current_user_can($edit_others_cap) )
3124                                         $statuswheres[] = "($wpdb->posts.post_author = $user_id " . "AND (" . join( ' OR ', $r_status ) . "))";
3125                                 else
3126                                         $statuswheres[] = "(" . join( ' OR ', $r_status ) . ")";
3127                         }
3128                         if ( !empty($p_status) ) {
3129                                 if ( !empty($q['perm'] ) && 'readable' == $q['perm'] && !current_user_can($read_private_cap) )
3130                                         $statuswheres[] = "($wpdb->posts.post_author = $user_id " . "AND (" . join( ' OR ', $p_status ) . "))";
3131                                 else
3132                                         $statuswheres[] = "(" . join( ' OR ', $p_status ) . ")";
3133                         }
3134                         if ( $post_status_join ) {
3135                                 $join .= " LEFT JOIN $wpdb->posts AS p2 ON ($wpdb->posts.post_parent = p2.ID) ";
3136                                 foreach ( $statuswheres as $index => $statuswhere )
3137                                         $statuswheres[$index] = "($statuswhere OR ($wpdb->posts.post_status = 'inherit' AND " . str_replace($wpdb->posts, 'p2', $statuswhere) . "))";
3138                         }
3139                         $where_status = implode( ' OR ', $statuswheres );
3140                         if ( ! empty( $where_status ) ) {
3141                                 $where .= " AND ($where_status)";
3142                         }
3143                 } elseif ( !$this->is_singular ) {
3144                         $where .= " AND ($wpdb->posts.post_status = 'publish'";
3145
3146                         // Add public states.
3147                         $public_states = get_post_stati( array('public' => true) );
3148                         foreach ( (array) $public_states as $state ) {
3149                                 if ( 'publish' == $state ) // Publish is hard-coded above.
3150                                         continue;
3151                                 $where .= " OR $wpdb->posts.post_status = '$state'";
3152                         }
3153
3154                         if ( $this->is_admin ) {
3155                                 // Add protected states that should show in the admin all list.
3156                                 $admin_all_states = get_post_stati( array('protected' => true, 'show_in_admin_all_list' => true) );
3157                                 foreach ( (array) $admin_all_states as $state )
3158                                         $where .= " OR $wpdb->posts.post_status = '$state'";
3159                         }
3160
3161                         if ( is_user_logged_in() ) {
3162                                 // Add private states that are limited to viewing by the author of a post or someone who has caps to read private states.
3163                                 $private_states = get_post_stati( array('private' => true) );
3164                                 foreach ( (array) $private_states as $state )
3165                                         $where .= current_user_can( $read_private_cap ) ? " OR $wpdb->posts.post_status = '$state'" : " OR $wpdb->posts.post_author = $user_id AND $wpdb->posts.post_status = '$state'";
3166                         }
3167
3168                         $where .= ')';
3169                 }
3170
3171                 /*
3172                  * Apply filters on where and join prior to paging so that any
3173                  * manipulations to them are reflected in the paging by day queries.
3174                  */
3175                 if ( !$q['suppress_filters'] ) {
3176                         /**
3177                          * Filter the WHERE clause of the query.
3178                          *
3179                          * @since 1.5.0
3180                          *
3181                          * @param string   $where The WHERE clause of the query.
3182                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3183                          */
3184                         $where = apply_filters_ref_array( 'posts_where', array( $where, &$this ) );
3185
3186                         /**
3187                          * Filter the JOIN clause of the query.
3188                          *
3189                          * @since 1.5.0
3190                          *
3191                          * @param string   $where The JOIN clause of the query.
3192                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3193                          */
3194                         $join = apply_filters_ref_array( 'posts_join', array( $join, &$this ) );
3195                 }
3196
3197                 // Paging
3198                 if ( empty($q['nopaging']) && !$this->is_singular ) {
3199                         $page = absint($q['paged']);
3200                         if ( !$page )
3201                                 $page = 1;
3202
3203                         // If 'offset' is provided, it takes precedence over 'paged'.
3204                         if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {
3205                                 $q['offset'] = absint( $q['offset'] );
3206                                 $pgstrt = $q['offset'] . ', ';
3207                         } else {
3208                                 $pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
3209                         }
3210                         $limits = 'LIMIT ' . $pgstrt . $q['posts_per_page'];
3211                 }
3212
3213                 // Comments feeds
3214                 if ( $this->is_comment_feed && ! $this->is_singular ) {
3215                         if ( $this->is_archive || $this->is_search ) {
3216                                 $cjoin = "JOIN $wpdb->posts ON ($wpdb->comments.comment_post_ID = $wpdb->posts.ID) $join ";
3217                                 $cwhere = "WHERE comment_approved = '1' $where";
3218                                 $cgroupby = "$wpdb->comments.comment_id";
3219                         } else { // Other non singular e.g. front
3220                                 $cjoin = "JOIN $wpdb->posts ON ( $wpdb->comments.comment_post_ID = $wpdb->posts.ID )";
3221                                 $cwhere = "WHERE ( post_status = 'publish' OR ( post_status = 'inherit' && post_type = 'attachment' ) ) AND comment_approved = '1'";
3222                                 $cgroupby = '';
3223                         }
3224
3225                         if ( !$q['suppress_filters'] ) {
3226                                 /**
3227                                  * Filter the JOIN clause of the comments feed query before sending.
3228                                  *
3229                                  * @since 2.2.0
3230                                  *
3231                                  * @param string   $cjoin The JOIN clause of the query.
3232                                  * @param WP_Query &$this The WP_Query instance (passed by reference).
3233                                  */
3234                                 $cjoin = apply_filters_ref_array( 'comment_feed_join', array( $cjoin, &$this ) );
3235
3236                                 /**
3237                                  * Filter the WHERE clause of the comments feed query before sending.
3238                                  *
3239                                  * @since 2.2.0
3240                                  *
3241                                  * @param string   $cwhere The WHERE clause of the query.
3242                                  * @param WP_Query &$this  The WP_Query instance (passed by reference).
3243                                  */
3244                                 $cwhere = apply_filters_ref_array( 'comment_feed_where', array( $cwhere, &$this ) );
3245
3246                                 /**
3247                                  * Filter the GROUP BY clause of the comments feed query before sending.
3248                                  *
3249                                  * @since 2.2.0
3250                                  *
3251                                  * @param string   $cgroupby The GROUP BY clause of the query.
3252                                  * @param WP_Query &$this    The WP_Query instance (passed by reference).
3253                                  */
3254                                 $cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( $cgroupby, &$this ) );
3255
3256                                 /**
3257                                  * Filter the ORDER BY clause of the comments feed query before sending.
3258                                  *
3259                                  * @since 2.8.0
3260                                  *
3261                                  * @param string   $corderby The ORDER BY clause of the query.
3262                                  * @param WP_Query &$this    The WP_Query instance (passed by reference).
3263                                  */
3264                                 $corderby = apply_filters_ref_array( 'comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
3265
3266                                 /**
3267                                  * Filter the LIMIT clause of the comments feed query before sending.
3268                                  *
3269                                  * @since 2.8.0
3270                                  *
3271                                  * @param string   $climits The JOIN clause of the query.
3272                                  * @param WP_Query &$this   The WP_Query instance (passed by reference).
3273                                  */
3274                                 $climits = apply_filters_ref_array( 'comment_feed_limits', array( 'LIMIT ' . get_option('posts_per_rss'), &$this ) );
3275                         }
3276                         $cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
3277                         $corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
3278
3279                         $comments = (array) $wpdb->get_results("SELECT $distinct $wpdb->comments.* FROM $wpdb->comments $cjoin $cwhere $cgroupby $corderby $climits");
3280                         // Convert to WP_Comment
3281                         $this->comments = array_map( 'get_comment', $comments );
3282                         $this->comment_count = count($this->comments);
3283
3284                         $post_ids = array();
3285
3286                         foreach ( $this->comments as $comment )
3287                                 $post_ids[] = (int) $comment->comment_post_ID;
3288
3289                         $post_ids = join(',', $post_ids);
3290                         $join = '';
3291                         if ( $post_ids )
3292                                 $where = "AND $wpdb->posts.ID IN ($post_ids) ";
3293                         else
3294                                 $where = "AND 0";
3295                 }
3296
3297                 $pieces = array( 'where', 'groupby', 'join', 'orderby', 'distinct', 'fields', 'limits' );
3298
3299                 /*
3300                  * Apply post-paging filters on where and join. Only plugins that
3301                  * manipulate paging queries should use these hooks.
3302                  */
3303                 if ( !$q['suppress_filters'] ) {
3304                         /**
3305                          * Filter the WHERE clause of the query.
3306                          *
3307                          * Specifically for manipulating paging queries.
3308                          *
3309                          * @since 1.5.0
3310                          *
3311                          * @param string   $where The WHERE clause of the query.
3312                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3313                          */
3314                         $where = apply_filters_ref_array( 'posts_where_paged', array( $where, &$this ) );
3315
3316                         /**
3317                          * Filter the GROUP BY clause of the query.
3318                          *
3319                          * @since 2.0.0
3320                          *
3321                          * @param string   $groupby The GROUP BY clause of the query.
3322                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3323                          */
3324                         $groupby = apply_filters_ref_array( 'posts_groupby', array( $groupby, &$this ) );
3325
3326                         /**
3327                          * Filter the JOIN clause of the query.
3328                          *
3329                          * Specifically for manipulating paging queries.
3330                          *
3331                          * @since 1.5.0
3332                          *
3333                          * @param string   $join  The JOIN clause of the query.
3334                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3335                          */
3336                         $join = apply_filters_ref_array( 'posts_join_paged', array( $join, &$this ) );
3337
3338                         /**
3339                          * Filter the ORDER BY clause of the query.
3340                          *
3341                          * @since 1.5.1
3342                          *
3343                          * @param string   $orderby The ORDER BY clause of the query.
3344                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3345                          */
3346                         $orderby = apply_filters_ref_array( 'posts_orderby', array( $orderby, &$this ) );
3347
3348                         /**
3349                          * Filter the DISTINCT clause of the query.
3350                          *
3351                          * @since 2.1.0
3352                          *
3353                          * @param string   $distinct The DISTINCT clause of the query.
3354                          * @param WP_Query &$this    The WP_Query instance (passed by reference).
3355                          */
3356                         $distinct = apply_filters_ref_array( 'posts_distinct', array( $distinct, &$this ) );
3357
3358                         /**
3359                          * Filter the LIMIT clause of the query.
3360                          *
3361                          * @since 2.1.0
3362                          *
3363                          * @param string   $limits The LIMIT clause of the query.
3364                          * @param WP_Query &$this  The WP_Query instance (passed by reference).
3365                          */