]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/query.php
Wordpress 4.5.3-scripts
[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                          */
3366                         $limits = apply_filters_ref_array( 'post_limits', array( $limits, &$this ) );
3367
3368                         /**
3369                          * Filter the SELECT clause of the query.
3370                          *
3371                          * @since 2.1.0
3372                          *
3373                          * @param string   $fields The SELECT clause of the query.
3374                          * @param WP_Query &$this  The WP_Query instance (passed by reference).
3375                          */
3376                         $fields = apply_filters_ref_array( 'posts_fields', array( $fields, &$this ) );
3377
3378                         /**
3379                          * Filter all query clauses at once, for convenience.
3380                          *
3381                          * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
3382                          * fields (SELECT), and LIMITS clauses.
3383                          *
3384                          * @since 3.1.0
3385                          *
3386                          * @param array    $clauses The list of clauses for the query.
3387                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3388                          */
3389                         $clauses = (array) apply_filters_ref_array( 'posts_clauses', array( compact( $pieces ), &$this ) );
3390
3391                         $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
3392                         $groupby = isset( $clauses[ 'groupby' ] ) ? $clauses[ 'groupby' ] : '';
3393                         $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
3394                         $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
3395                         $distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
3396                         $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
3397                         $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
3398                 }
3399
3400                 /**
3401                  * Fires to announce the query's current selection parameters.
3402                  *
3403                  * For use by caching plugins.
3404                  *
3405                  * @since 2.3.0
3406                  *
3407                  * @param string $selection The assembled selection query.
3408                  */
3409                 do_action( 'posts_selection', $where . $groupby . $orderby . $limits . $join );
3410
3411                 /*
3412                  * Filter again for the benefit of caching plugins.
3413                  * Regular plugins should use the hooks above.
3414                  */
3415                 if ( !$q['suppress_filters'] ) {
3416                         /**
3417                          * Filter the WHERE clause of the query.
3418                          *
3419                          * For use by caching plugins.
3420                          *
3421                          * @since 2.5.0
3422                          *
3423                          * @param string   $where The WHERE clause of the query.
3424                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3425                          */
3426                         $where = apply_filters_ref_array( 'posts_where_request', array( $where, &$this ) );
3427
3428                         /**
3429                          * Filter the GROUP BY clause of the query.
3430                          *
3431                          * For use by caching plugins.
3432                          *
3433                          * @since 2.5.0
3434                          *
3435                          * @param string   $groupby The GROUP BY clause of the query.
3436                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3437                          */
3438                         $groupby = apply_filters_ref_array( 'posts_groupby_request', array( $groupby, &$this ) );
3439
3440                         /**
3441                          * Filter the JOIN clause of the query.
3442                          *
3443                          * For use by caching plugins.
3444                          *
3445                          * @since 2.5.0
3446                          *
3447                          * @param string   $join  The JOIN clause of the query.
3448                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3449                          */
3450                         $join = apply_filters_ref_array( 'posts_join_request', array( $join, &$this ) );
3451
3452                         /**
3453                          * Filter the ORDER BY clause of the query.
3454                          *
3455                          * For use by caching plugins.
3456                          *
3457                          * @since 2.5.0
3458                          *
3459                          * @param string   $orderby The ORDER BY clause of the query.
3460                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3461                          */
3462                         $orderby = apply_filters_ref_array( 'posts_orderby_request', array( $orderby, &$this ) );
3463
3464                         /**
3465                          * Filter the DISTINCT clause of the query.
3466                          *
3467                          * For use by caching plugins.
3468                          *
3469                          * @since 2.5.0
3470                          *
3471                          * @param string   $distinct The DISTINCT clause of the query.
3472                          * @param WP_Query &$this    The WP_Query instance (passed by reference).
3473                          */
3474                         $distinct = apply_filters_ref_array( 'posts_distinct_request', array( $distinct, &$this ) );
3475
3476                         /**
3477                          * Filter the SELECT clause of the query.
3478                          *
3479                          * For use by caching plugins.
3480                          *
3481                          * @since 2.5.0
3482                          *
3483                          * @param string   $fields The SELECT clause of the query.
3484                          * @param WP_Query &$this  The WP_Query instance (passed by reference).
3485                          */
3486                         $fields = apply_filters_ref_array( 'posts_fields_request', array( $fields, &$this ) );
3487
3488                         /**
3489                          * Filter the LIMIT clause of the query.
3490                          *
3491                          * For use by caching plugins.
3492                          *
3493                          * @since 2.5.0
3494                          *
3495                          * @param string   $limits The LIMIT clause of the query.
3496                          * @param WP_Query &$this  The WP_Query instance (passed by reference).
3497                          */
3498                         $limits = apply_filters_ref_array( 'post_limits_request', array( $limits, &$this ) );
3499
3500                         /**
3501                          * Filter all query clauses at once, for convenience.
3502                          *
3503                          * For use by caching plugins.
3504                          *
3505                          * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
3506                          * fields (SELECT), and LIMITS clauses.
3507                          *
3508                          * @since 3.1.0
3509                          *
3510                          * @param array    $pieces The pieces of the query.
3511                          * @param WP_Query &$this  The WP_Query instance (passed by reference).
3512                          */
3513                         $clauses = (array) apply_filters_ref_array( 'posts_clauses_request', array( compact( $pieces ), &$this ) );
3514
3515                         $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
3516                         $groupby = isset( $clauses[ 'groupby' ] ) ? $clauses[ 'groupby' ] : '';
3517                         $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
3518                         $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
3519                         $distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
3520                         $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
3521                         $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
3522                 }
3523
3524                 if ( ! empty($groupby) )
3525                         $groupby = 'GROUP BY ' . $groupby;
3526                 if ( !empty( $orderby ) )
3527                         $orderby = 'ORDER BY ' . $orderby;
3528
3529                 $found_rows = '';
3530                 if ( !$q['no_found_rows'] && !empty($limits) )
3531                         $found_rows = 'SQL_CALC_FOUND_ROWS';
3532
3533                 $this->request = $old_request = "SELECT $found_rows $distinct $fields FROM $wpdb->posts $join WHERE 1=1 $where $groupby $orderby $limits";
3534
3535                 if ( !$q['suppress_filters'] ) {
3536                         /**
3537                          * Filter the completed SQL query before sending.
3538                          *
3539                          * @since 2.0.0
3540                          *
3541                          * @param array    $request The complete SQL query.
3542                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3543                          */
3544                         $this->request = apply_filters_ref_array( 'posts_request', array( $this->request, &$this ) );
3545                 }
3546
3547                 if ( 'ids' == $q['fields'] ) {
3548                         $this->posts = $wpdb->get_col( $this->request );
3549                         $this->posts = array_map( 'intval', $this->posts );
3550                         $this->post_count = count( $this->posts );
3551                         $this->set_found_posts( $q, $limits );
3552
3553                         return $this->posts;
3554                 }
3555
3556                 if ( 'id=>parent' == $q['fields'] ) {
3557                         $this->posts = $wpdb->get_results( $this->request );
3558                         $this->post_count = count( $this->posts );
3559                         $this->set_found_posts( $q, $limits );
3560
3561                         $r = array();
3562                         foreach ( $this->posts as $key => $post ) {
3563                                 $this->posts[ $key ]->ID = (int) $post->ID;
3564                                 $this->posts[ $key ]->post_parent = (int) $post->post_parent;
3565
3566                                 $r[ (int) $post->ID ] = (int) $post->post_parent;
3567                         }
3568
3569                         return $r;
3570                 }
3571
3572                 $split_the_query = ( $old_request == $this->request && "$wpdb->posts.*" == $fields && !empty( $limits ) && $q['posts_per_page'] < 500 );
3573
3574                 /**
3575                  * Filter whether to split the query.
3576                  *
3577                  * Splitting the query will cause it to fetch just the IDs of the found posts
3578                  * (and then individually fetch each post by ID), rather than fetching every
3579                  * complete row at once. One massive result vs. many small results.
3580                  *
3581                  * @since 3.4.0
3582                  *
3583                  * @param bool     $split_the_query Whether or not to split the query.
3584                  * @param WP_Query $this            The WP_Query instance.
3585                  */
3586                 $split_the_query = apply_filters( 'split_the_query', $split_the_query, $this );
3587
3588                 if ( $split_the_query ) {
3589                         // First get the IDs and then fill in the objects
3590
3591                         $this->request = "SELECT $found_rows $distinct $wpdb->posts.ID FROM $wpdb->posts $join WHERE 1=1 $where $groupby $orderby $limits";
3592
3593                         /**
3594                          * Filter the Post IDs SQL request before sending.
3595                          *
3596                          * @since 3.4.0
3597                          *
3598                          * @param string   $request The post ID request.
3599                          * @param WP_Query $this    The WP_Query instance.
3600                          */
3601                         $this->request = apply_filters( 'posts_request_ids', $this->request, $this );
3602
3603                         $ids = $wpdb->get_col( $this->request );
3604
3605                         if ( $ids ) {
3606                                 $this->posts = $ids;
3607                                 $this->set_found_posts( $q, $limits );
3608                                 _prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
3609                         } else {
3610                                 $this->posts = array();
3611                         }
3612                 } else {
3613                         $this->posts = $wpdb->get_results( $this->request );
3614                         $this->set_found_posts( $q, $limits );
3615                 }
3616
3617                 // Convert to WP_Post objects
3618                 if ( $this->posts )
3619                         $this->posts = array_map( 'get_post', $this->posts );
3620
3621                 if ( ! $q['suppress_filters'] ) {
3622                         /**
3623                          * Filter the raw post results array, prior to status checks.
3624                          *
3625                          * @since 2.3.0
3626                          *
3627                          * @param array    $posts The post results array.
3628                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3629                          */
3630                         $this->posts = apply_filters_ref_array( 'posts_results', array( $this->posts, &$this ) );
3631                 }
3632
3633                 if ( !empty($this->posts) && $this->is_comment_feed && $this->is_singular ) {
3634                         /** This filter is documented in wp-includes/query.php */
3635                         $cjoin = apply_filters_ref_array( 'comment_feed_join', array( '', &$this ) );
3636
3637                         /** This filter is documented in wp-includes/query.php */
3638                         $cwhere = apply_filters_ref_array( 'comment_feed_where', array( "WHERE comment_post_ID = '{$this->posts[0]->ID}' AND comment_approved = '1'", &$this ) );
3639
3640                         /** This filter is documented in wp-includes/query.php */
3641                         $cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( '', &$this ) );
3642                         $cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
3643
3644                         /** This filter is documented in wp-includes/query.php */
3645                         $corderby = apply_filters_ref_array( 'comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
3646                         $corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
3647
3648                         /** This filter is documented in wp-includes/query.php */
3649                         $climits = apply_filters_ref_array( 'comment_feed_limits', array( 'LIMIT ' . get_option('posts_per_rss'), &$this ) );
3650
3651                         $comments_request = "SELECT $wpdb->comments.* FROM $wpdb->comments $cjoin $cwhere $cgroupby $corderby $climits";
3652                         $comments = $wpdb->get_results($comments_request);
3653                         // Convert to WP_Comment
3654                         $this->comments = array_map( 'get_comment', $comments );
3655                         $this->comment_count = count($this->comments);
3656                 }
3657
3658                 // Check post status to determine if post should be displayed.
3659                 if ( !empty($this->posts) && ($this->is_single || $this->is_page) ) {
3660                         $status = get_post_status($this->posts[0]);
3661                         if ( 'attachment' === $this->posts[0]->post_type && 0 === (int) $this->posts[0]->post_parent ) {
3662                                 $this->is_page = false;
3663                                 $this->is_single = true;
3664                                 $this->is_attachment = true;
3665                         }
3666                         $post_status_obj = get_post_status_object($status);
3667
3668                         // If the post_status was specifically requested, let it pass through.
3669                         if ( !$post_status_obj->public && ! in_array( $status, $q_status ) ) {
3670
3671                                 if ( ! is_user_logged_in() ) {
3672                                         // User must be logged in to view unpublished posts.
3673                                         $this->posts = array();
3674                                 } else {
3675                                         if  ( $post_status_obj->protected ) {
3676                                                 // User must have edit permissions on the draft to preview.
3677                                                 if ( ! current_user_can($edit_cap, $this->posts[0]->ID) ) {
3678                                                         $this->posts = array();
3679                                                 } else {
3680                                                         $this->is_preview = true;
3681                                                         if ( 'future' != $status )
3682                                                                 $this->posts[0]->post_date = current_time('mysql');
3683                                                 }
3684                                         } elseif ( $post_status_obj->private ) {
3685                                                 if ( ! current_user_can($read_cap, $this->posts[0]->ID) )
3686                                                         $this->posts = array();
3687                                         } else {
3688                                                 $this->posts = array();
3689                                         }
3690                                 }
3691                         }
3692
3693                         if ( $this->is_preview && $this->posts && current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
3694                                 /**
3695                                  * Filter the single post for preview mode.
3696                                  *
3697                                  * @since 2.7.0
3698                                  *
3699                                  * @param WP_Post  $post_preview  The Post object.
3700                                  * @param WP_Query &$this         The WP_Query instance (passed by reference).
3701                                  */
3702                                 $this->posts[0] = get_post( apply_filters_ref_array( 'the_preview', array( $this->posts[0], &$this ) ) );
3703                         }
3704                 }
3705
3706                 // Put sticky posts at the top of the posts array
3707                 $sticky_posts = get_option('sticky_posts');
3708                 if ( $this->is_home && $page <= 1 && is_array($sticky_posts) && !empty($sticky_posts) && !$q['ignore_sticky_posts'] ) {
3709                         $num_posts = count($this->posts);
3710                         $sticky_offset = 0;
3711                         // Loop over posts and relocate stickies to the front.
3712                         for ( $i = 0; $i < $num_posts; $i++ ) {
3713                                 if ( in_array($this->posts[$i]->ID, $sticky_posts) ) {
3714                                         $sticky_post = $this->posts[$i];
3715                                         // Remove sticky from current position
3716                                         array_splice($this->posts, $i, 1);
3717                                         // Move to front, after other stickies
3718                                         array_splice($this->posts, $sticky_offset, 0, array($sticky_post));
3719                                         // Increment the sticky offset. The next sticky will be placed at this offset.
3720                                         $sticky_offset++;
3721                                         // Remove post from sticky posts array
3722                                         $offset = array_search($sticky_post->ID, $sticky_posts);
3723                                         unset( $sticky_posts[$offset] );
3724                                 }
3725                         }
3726
3727                         // If any posts have been excluded specifically, Ignore those that are sticky.
3728                         if ( !empty($sticky_posts) && !empty($q['post__not_in']) )
3729                                 $sticky_posts = array_diff($sticky_posts, $q['post__not_in']);
3730
3731                         // Fetch sticky posts that weren't in the query results
3732                         if ( !empty($sticky_posts) ) {
3733                                 $stickies = get_posts( array(
3734                                         'post__in' => $sticky_posts,
3735                                         'post_type' => $post_type,
3736                                         'post_status' => 'publish',
3737                                         'nopaging' => true
3738                                 ) );
3739
3740                                 foreach ( $stickies as $sticky_post ) {
3741                                         array_splice( $this->posts, $sticky_offset, 0, array( $sticky_post ) );
3742                                         $sticky_offset++;
3743                                 }
3744                         }
3745                 }
3746
3747                 // If comments have been fetched as part of the query, make sure comment meta lazy-loading is set up.
3748                 if ( ! empty( $this->comments ) ) {
3749                         wp_queue_comments_for_comment_meta_lazyload( $this->comments );
3750                 }
3751
3752                 if ( ! $q['suppress_filters'] ) {
3753                         /**
3754                          * Filter the array of retrieved posts after they've been fetched and
3755                          * internally processed.
3756                          *
3757                          * @since 1.5.0
3758                          *
3759                          * @param array    $posts The array of retrieved posts.
3760                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3761                          */
3762                         $this->posts = apply_filters_ref_array( 'the_posts', array( $this->posts, &$this ) );
3763                 }
3764
3765                 // Ensure that any posts added/modified via one of the filters above are
3766                 // of the type WP_Post and are filtered.
3767                 if ( $this->posts ) {
3768                         $this->post_count = count( $this->posts );
3769
3770                         $this->posts = array_map( 'get_post', $this->posts );
3771
3772                         if ( $q['cache_results'] )
3773                                 update_post_caches($this->posts, $post_type, $q['update_post_term_cache'], $q['update_post_meta_cache']);
3774
3775                         $this->post = reset( $this->posts );
3776                 } else {
3777                         $this->post_count = 0;
3778                         $this->posts = array();
3779                 }
3780
3781                 if ( $q['update_post_term_cache'] ) {
3782                         wp_queue_posts_for_term_meta_lazyload( $this->posts );
3783                 }
3784
3785                 return $this->posts;
3786         }
3787
3788         /**
3789          * Set up the amount of found posts and the number of pages (if limit clause was used)
3790          * for the current query.
3791          *
3792          * @since 3.5.0
3793          * @access private
3794          *
3795          * @global wpdb $wpdb WordPress database abstraction object.
3796          *
3797          * @param array  $q      Query variables.
3798          * @param string $limits LIMIT clauses of the query.
3799          */
3800         private function set_found_posts( $q, $limits ) {
3801                 global $wpdb;
3802
3803                 // Bail if posts is an empty array. Continue if posts is an empty string,
3804                 // null, or false to accommodate caching plugins that fill posts later.
3805                 if ( $q['no_found_rows'] || ( is_array( $this->posts ) && ! $this->posts ) )
3806                         return;
3807
3808                 if ( ! empty( $limits ) ) {
3809                         /**
3810                          * Filter the query to run for retrieving the found posts.
3811                          *
3812                          * @since 2.1.0
3813                          *
3814                          * @param string   $found_posts The query to run to find the found posts.
3815                          * @param WP_Query &$this       The WP_Query instance (passed by reference).
3816                          */
3817                         $this->found_posts = $wpdb->get_var( apply_filters_ref_array( 'found_posts_query', array( 'SELECT FOUND_ROWS()', &$this ) ) );
3818                 } else {
3819                         $this->found_posts = count( $this->posts );
3820                 }
3821
3822                 /**
3823                  * Filter the number of found posts for the query.
3824                  *
3825                  * @since 2.1.0
3826                  *
3827                  * @param int      $found_posts The number of posts found.
3828                  * @param WP_Query &$this       The WP_Query instance (passed by reference).
3829                  */
3830                 $this->found_posts = apply_filters_ref_array( 'found_posts', array( $this->found_posts, &$this ) );
3831
3832                 if ( ! empty( $limits ) )
3833                         $this->max_num_pages = ceil( $this->found_posts / $q['posts_per_page'] );
3834         }
3835
3836         /**
3837          * Set up the next post and iterate current post index.
3838          *
3839          * @since 1.5.0
3840          * @access public
3841          *
3842          * @return WP_Post Next post.
3843          */
3844         public function next_post() {
3845
3846                 $this->current_post++;
3847
3848                 $this->post = $this->posts[$this->current_post];
3849                 return $this->post;
3850         }
3851
3852         /**
3853          * Sets up the current post.
3854          *
3855          * Retrieves the next post, sets up the post, sets the 'in the loop'
3856          * property to true.
3857          *
3858          * @since 1.5.0
3859          * @access public
3860          *
3861          * @global WP_Post $post
3862          */
3863         public function the_post() {
3864                 global $post;
3865                 $this->in_the_loop = true;
3866
3867                 if ( $this->current_post == -1 ) // loop has just started
3868                         /**
3869                          * Fires once the loop is started.
3870                          *
3871                          * @since 2.0.0
3872                          *
3873                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3874                          */
3875                         do_action_ref_array( 'loop_start', array( &$this ) );
3876
3877                 $post = $this->next_post();
3878                 $this->setup_postdata( $post );
3879         }
3880
3881         /**
3882          * Whether there are more posts available in the loop.
3883          *
3884          * Calls action 'loop_end', when the loop is complete.
3885          *
3886          * @since 1.5.0
3887          * @access public
3888          *
3889          * @return bool True if posts are available, false if end of loop.
3890          */
3891         public function have_posts() {
3892                 if ( $this->current_post + 1 < $this->post_count ) {
3893                         return true;
3894                 } elseif ( $this->current_post + 1 == $this->post_count && $this->post_count > 0 ) {
3895                         /**
3896                          * Fires once the loop has ended.
3897                          *
3898                          * @since 2.0.0
3899                          *
3900                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3901                          */
3902                         do_action_ref_array( 'loop_end', array( &$this ) );
3903                         // Do some cleaning up after the loop
3904                         $this->rewind_posts();
3905                 }
3906
3907                 $this->in_the_loop = false;
3908                 return false;
3909         }
3910
3911         /**
3912          * Rewind the posts and reset post index.
3913          *
3914          * @since 1.5.0
3915          * @access public
3916          */
3917         public function rewind_posts() {
3918                 $this->current_post = -1;
3919                 if ( $this->post_count > 0 ) {
3920                         $this->post = $this->posts[0];
3921                 }
3922         }
3923
3924         /**
3925          * Iterate current comment index and return WP_Comment object.
3926          *
3927          * @since 2.2.0
3928          * @access public
3929          *
3930          * @return WP_Comment Comment object.
3931          */
3932         public function next_comment() {
3933                 $this->current_comment++;
3934
3935                 $this->comment = $this->comments[$this->current_comment];
3936                 return $this->comment;
3937         }
3938
3939         /**
3940          * Sets up the current comment.
3941          *
3942          * @since 2.2.0
3943          * @access public
3944          * @global WP_Comment $comment Current comment.
3945          */
3946         public function the_comment() {
3947                 global $comment;
3948
3949                 $comment = $this->next_comment();
3950
3951                 if ( $this->current_comment == 0 ) {
3952                         /**
3953                          * Fires once the comment loop is started.
3954                          *
3955                          * @since 2.2.0
3956                          */
3957                         do_action( 'comment_loop_start' );
3958                 }
3959         }
3960
3961         /**
3962          * Whether there are more comments available.
3963          *
3964          * Automatically rewinds comments when finished.
3965          *
3966          * @since 2.2.0
3967          * @access public
3968          *
3969          * @return bool True, if more comments. False, if no more posts.
3970          */
3971         public function have_comments() {
3972                 if ( $this->current_comment + 1 < $this->comment_count ) {
3973                         return true;
3974                 } elseif ( $this->current_comment + 1 == $this->comment_count ) {
3975                         $this->rewind_comments();
3976                 }
3977
3978                 return false;
3979         }
3980
3981         /**
3982          * Rewind the comments, resets the comment index and comment to first.
3983          *
3984          * @since 2.2.0
3985          * @access public
3986          */
3987         public function rewind_comments() {
3988                 $this->current_comment = -1;
3989                 if ( $this->comment_count > 0 ) {
3990                         $this->comment = $this->comments[0];
3991                 }
3992         }
3993
3994         /**
3995          * Sets up the WordPress query by parsing query string.
3996          *
3997          * @since 1.5.0
3998          * @access public
3999          *
4000          * @param string $query URL query string.
4001          * @return array List of posts.
4002          */
4003         public function query( $query ) {
4004                 $this->init();
4005                 $this->query = $this->query_vars = wp_parse_args( $query );
4006                 return $this->get_posts();
4007         }
4008
4009         /**
4010          * Retrieve queried object.
4011          *
4012          * If queried object is not set, then the queried object will be set from
4013          * the category, tag, taxonomy, posts page, single post, page, or author
4014          * query variable. After it is set up, it will be returned.
4015          *
4016          * @since 1.5.0
4017          * @access public
4018          *
4019          * @return object
4020          */
4021         public function get_queried_object() {
4022                 if ( isset($this->queried_object) )
4023                         return $this->queried_object;
4024
4025                 $this->queried_object = null;
4026                 $this->queried_object_id = null;
4027
4028                 if ( $this->is_category || $this->is_tag || $this->is_tax ) {
4029                         if ( $this->is_category ) {
4030                                 if ( $this->get( 'cat' ) ) {
4031                                         $term = get_term( $this->get( 'cat' ), 'category' );
4032                                 } elseif ( $this->get( 'category_name' ) ) {
4033                                         $term = get_term_by( 'slug', $this->get( 'category_name' ), 'category' );
4034                                 }
4035                         } elseif ( $this->is_tag ) {
4036                                 if ( $this->get( 'tag_id' ) ) {
4037                                         $term = get_term( $this->get( 'tag_id' ), 'post_tag' );
4038                                 } elseif ( $this->get( 'tag' ) ) {
4039                                         $term = get_term_by( 'slug', $this->get( 'tag' ), 'post_tag' );
4040                                 }
4041                         } else {
4042                                 // For other tax queries, grab the first term from the first clause.
4043                                 $tax_query_in_and = wp_list_filter( $this->tax_query->queried_terms, array( 'operator' => 'NOT IN' ), 'NOT' );
4044
4045                                 if ( ! empty( $tax_query_in_and ) ) {
4046                                         $queried_taxonomies = array_keys( $tax_query_in_and );
4047                                         $matched_taxonomy = reset( $queried_taxonomies );
4048                                         $query = $tax_query_in_and[ $matched_taxonomy ];
4049
4050                                         if ( $query['terms'] ) {
4051                                                 if ( 'term_id' == $query['field'] ) {
4052                                                         $term = get_term( reset( $query['terms'] ), $matched_taxonomy );
4053                                                 } else {
4054                                                         $term = get_term_by( $query['field'], reset( $query['terms'] ), $matched_taxonomy );
4055                                                 }
4056                                         }
4057                                 }
4058                         }
4059
4060                         if ( ! empty( $term ) && ! is_wp_error( $term ) )  {
4061                                 $this->queried_object = $term;
4062                                 $this->queried_object_id = (int) $term->term_id;
4063
4064                                 if ( $this->is_category && 'category' === $this->queried_object->taxonomy )
4065                                         _make_cat_compat( $this->queried_object );
4066                         }
4067                 } elseif ( $this->is_post_type_archive ) {
4068                         $post_type = $this->get( 'post_type' );
4069                         if ( is_array( $post_type ) )
4070                                 $post_type = reset( $post_type );
4071                         $this->queried_object = get_post_type_object( $post_type );
4072                 } elseif ( $this->is_posts_page ) {
4073                         $page_for_posts = get_option('page_for_posts');
4074                         $this->queried_object = get_post( $page_for_posts );
4075                         $this->queried_object_id = (int) $this->queried_object->ID;
4076                 } elseif ( $this->is_singular && ! empty( $this->post ) ) {
4077                         $this->queried_object = $this->post;
4078                         $this->queried_object_id = (int) $this->post->ID;
4079                 } elseif ( $this->is_author ) {
4080                         $this->queried_object_id = (int) $this->get('author');
4081                         $this->queried_object = get_userdata( $this->queried_object_id );
4082                 }
4083
4084                 return $this->queried_object;
4085         }
4086
4087         /**
4088          * Retrieve ID of the current queried object.
4089          *
4090          * @since 1.5.0
4091          * @access public
4092          *
4093          * @return int
4094          */
4095         public function get_queried_object_id() {
4096                 $this->get_queried_object();
4097
4098                 if ( isset($this->queried_object_id) ) {
4099                         return $this->queried_object_id;
4100                 }
4101
4102                 return 0;
4103         }
4104
4105         /**
4106          * Constructor.
4107          *
4108          * Sets up the WordPress query, if parameter is not empty.
4109          *
4110          * @since 1.5.0
4111          * @access public
4112          *
4113          * @param string|array $query URL query string or array of vars.
4114          */
4115         public function __construct($query = '') {
4116                 if ( ! empty($query) ) {
4117                         $this->query($query);
4118                 }
4119         }
4120
4121         /**
4122          * Make private properties readable for backwards compatibility.
4123          *
4124          * @since 4.0.0
4125          * @access public
4126          *
4127          * @param string $name Property to get.
4128          * @return mixed Property.
4129          */
4130         public function __get( $name ) {
4131                 if ( in_array( $name, $this->compat_fields ) ) {
4132                         return $this->$name;
4133                 }
4134         }
4135
4136         /**
4137          * Make private properties checkable for backwards compatibility.
4138          *
4139          * @since 4.0.0
4140          * @access public
4141          *
4142          * @param string $name Property to check if set.
4143          * @return bool Whether the property is set.
4144          */
4145         public function __isset( $name ) {
4146                 if ( in_array( $name, $this->compat_fields ) ) {
4147                         return isset( $this->$name );
4148                 }
4149         }
4150
4151         /**
4152          * Make private/protected methods readable for backwards compatibility.
4153          *
4154          * @since 4.0.0
4155          * @access public
4156          *
4157          * @param callable $name      Method to call.
4158          * @param array    $arguments Arguments to pass when calling.
4159          * @return mixed|false Return value of the callback, false otherwise.
4160          */
4161         public function __call( $name, $arguments ) {
4162                 if ( in_array( $name, $this->compat_methods ) ) {
4163                         return call_user_func_array( array( $this, $name ), $arguments );
4164                 }
4165                 return false;
4166         }
4167
4168         /**
4169          * Is the query for an existing archive page?
4170          *
4171          * Month, Year, Category, Author, Post Type archive...
4172          *
4173          * @since 3.1.0
4174          *
4175          * @return bool
4176          */
4177         public function is_archive() {
4178                 return (bool) $this->is_archive;
4179         }
4180
4181         /**
4182          * Is the query for an existing post type archive page?
4183          *
4184          * @since 3.1.0
4185          *
4186          * @param mixed $post_types Optional. Post type or array of posts types to check against.
4187          * @return bool
4188          */
4189         public function is_post_type_archive( $post_types = '' ) {
4190                 if ( empty( $post_types ) || ! $this->is_post_type_archive )
4191                         return (bool) $this->is_post_type_archive;
4192
4193                 $post_type = $this->get( 'post_type' );
4194                 if ( is_array( $post_type ) )
4195                         $post_type = reset( $post_type );
4196                 $post_type_object = get_post_type_object( $post_type );
4197
4198                 return in_array( $post_type_object->name, (array) $post_types );
4199         }
4200
4201         /**
4202          * Is the query for an existing attachment page?
4203          *
4204          * @since 3.1.0
4205          *
4206          * @param mixed $attachment Attachment ID, title, slug, or array of such.
4207          * @return bool
4208          */
4209         public function is_attachment( $attachment = '' ) {
4210                 if ( ! $this->is_attachment ) {
4211                         return false;
4212                 }
4213
4214                 if ( empty( $attachment ) ) {
4215                         return true;
4216                 }
4217
4218                 $attachment = array_map( 'strval', (array) $attachment );
4219
4220                 $post_obj = $this->get_queried_object();
4221
4222                 if ( in_array( (string) $post_obj->ID, $attachment ) ) {
4223                         return true;
4224                 } elseif ( in_array( $post_obj->post_title, $attachment ) ) {
4225                         return true;
4226                 } elseif ( in_array( $post_obj->post_name, $attachment ) ) {
4227                         return true;
4228                 }
4229                 return false;
4230         }
4231
4232         /**
4233          * Is the query for an existing author archive page?
4234          *
4235          * If the $author parameter is specified, this function will additionally
4236          * check if the query is for one of the authors specified.
4237          *
4238          * @since 3.1.0
4239          *
4240          * @param mixed $author Optional. User ID, nickname, nicename, or array of User IDs, nicknames, and nicenames
4241          * @return bool
4242          */
4243         public function is_author( $author = '' ) {
4244                 if ( !$this->is_author )
4245                         return false;
4246
4247                 if ( empty($author) )
4248                         return true;
4249
4250                 $author_obj = $this->get_queried_object();
4251
4252                 $author = array_map( 'strval', (array) $author );
4253
4254                 if ( in_array( (string) $author_obj->ID, $author ) )
4255                         return true;
4256                 elseif ( in_array( $author_obj->nickname, $author ) )
4257                         return true;
4258                 elseif ( in_array( $author_obj->user_nicename, $author ) )
4259                         return true;
4260
4261                 return false;
4262         }
4263
4264         /**
4265          * Is the query for an existing category archive page?
4266          *
4267          * If the $category parameter is specified, this function will additionally
4268          * check if the query is for one of the categories specified.
4269          *
4270          * @since 3.1.0
4271          *
4272          * @param mixed $category Optional. Category ID, name, slug, or array of Category IDs, names, and slugs.
4273          * @return bool
4274          */
4275         public function is_category( $category = '' ) {
4276                 if ( !$this->is_category )
4277                         return false;
4278
4279                 if ( empty($category) )
4280                         return true;
4281
4282                 $cat_obj = $this->get_queried_object();
4283
4284                 $category = array_map( 'strval', (array) $category );
4285
4286                 if ( in_array( (string) $cat_obj->term_id, $category ) )
4287                         return true;
4288                 elseif ( in_array( $cat_obj->name, $category ) )
4289                         return true;
4290                 elseif ( in_array( $cat_obj->slug, $category ) )
4291                         return true;
4292
4293                 return false;
4294         }
4295
4296         /**
4297          * Is the query for an existing tag archive page?
4298          *
4299          * If the $tag parameter is specified, this function will additionally
4300          * check if the query is for one of the tags specified.
4301          *
4302          * @since 3.1.0
4303          *
4304          * @param mixed $tag Optional. Tag ID, name, slug, or array of Tag IDs, names, and slugs.
4305          * @return bool
4306          */
4307         public function is_tag( $tag = '' ) {
4308                 if ( ! $this->is_tag )
4309                         return false;
4310
4311                 if ( empty( $tag ) )
4312                         return true;
4313
4314                 $tag_obj = $this->get_queried_object();
4315
4316                 $tag = array_map( 'strval', (array) $tag );
4317
4318                 if ( in_array( (string) $tag_obj->term_id, $tag ) )
4319                         return true;
4320                 elseif ( in_array( $tag_obj->name, $tag ) )
4321                         return true;
4322                 elseif ( in_array( $tag_obj->slug, $tag ) )
4323                         return true;
4324
4325                 return false;
4326         }
4327
4328         /**
4329          * Is the query for an existing taxonomy archive page?
4330          *
4331          * If the $taxonomy parameter is specified, this function will additionally
4332          * check if the query is for that specific $taxonomy.
4333          *
4334          * If the $term parameter is specified in addition to the $taxonomy parameter,
4335          * this function will additionally check if the query is for one of the terms
4336          * specified.
4337          *
4338          * @since 3.1.0
4339          *
4340          * @global array $wp_taxonomies
4341          *
4342          * @param mixed $taxonomy Optional. Taxonomy slug or slugs.
4343          * @param mixed $term     Optional. Term ID, name, slug or array of Term IDs, names, and slugs.
4344          * @return bool
4345          */
4346         public function is_tax( $taxonomy = '', $term = '' ) {
4347                 global $wp_taxonomies;
4348
4349                 if ( !$this->is_tax )
4350                         return false;
4351
4352                 if ( empty( $taxonomy ) )
4353                         return true;
4354
4355                 $queried_object = $this->get_queried_object();
4356                 $tax_array = array_intersect( array_keys( $wp_taxonomies ), (array) $taxonomy );
4357                 $term_array = (array) $term;
4358
4359                 // Check that the taxonomy matches.
4360                 if ( ! ( isset( $queried_object->taxonomy ) && count( $tax_array ) && in_array( $queried_object->taxonomy, $tax_array ) ) )
4361                         return false;
4362
4363                 // Only a Taxonomy provided.
4364                 if ( empty( $term ) )
4365                         return true;
4366
4367                 return isset( $queried_object->term_id ) &&
4368                         count( array_intersect(
4369                                 array( $queried_object->term_id, $queried_object->name, $queried_object->slug ),
4370                                 $term_array
4371                         ) );
4372         }
4373
4374         /**
4375          * Whether the current URL is within the comments popup window.
4376          *
4377          * @since 3.1.0
4378          * @deprecated 4.5.0
4379          *
4380          * @return bool
4381          */
4382         public function is_comments_popup() {
4383                 _deprecated_function( __FUNCTION__, '4.5' );
4384
4385                 return false;
4386         }
4387
4388         /**
4389          * Is the query for an existing date archive?
4390          *
4391          * @since 3.1.0
4392          *
4393          * @return bool
4394          */
4395         public function is_date() {
4396                 return (bool) $this->is_date;
4397         }
4398
4399         /**
4400          * Is the query for an existing day archive?
4401          *
4402          * @since 3.1.0
4403          *
4404          * @return bool
4405          */
4406         public function is_day() {
4407                 return (bool) $this->is_day;
4408         }
4409
4410         /**
4411          * Is the query for a feed?
4412          *
4413          * @since 3.1.0
4414          *
4415          * @param string|array $feeds Optional feed types to check.
4416          * @return bool
4417          */
4418         public function is_feed( $feeds = '' ) {
4419                 if ( empty( $feeds ) || ! $this->is_feed )
4420                         return (bool) $this->is_feed;
4421                 $qv = $this->get( 'feed' );
4422                 if ( 'feed' == $qv )
4423                         $qv = get_default_feed();
4424                 return in_array( $qv, (array) $feeds );
4425         }
4426
4427         /**
4428          * Is the query for a comments feed?
4429          *
4430          * @since 3.1.0
4431          *
4432          * @return bool
4433          */
4434         public function is_comment_feed() {
4435                 return (bool) $this->is_comment_feed;
4436         }
4437
4438         /**
4439          * Is the query for the front page of the site?
4440          *
4441          * This is for what is displayed at your site's main URL.
4442          *
4443          * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_on_front'.
4444          *
4445          * If you set a static page for the front page of your site, this function will return
4446          * true when viewing that page.
4447          *
4448          * Otherwise the same as @see WP_Query::is_home()
4449          *
4450          * @since 3.1.0
4451          *
4452          * @return bool True, if front of site.
4453          */
4454         public function is_front_page() {
4455                 // most likely case
4456                 if ( 'posts' == get_option( 'show_on_front') && $this->is_home() )
4457                         return true;
4458                 elseif ( 'page' == get_option( 'show_on_front') && get_option( 'page_on_front' ) && $this->is_page( get_option( 'page_on_front' ) ) )
4459                         return true;
4460                 else
4461                         return false;
4462         }
4463
4464         /**
4465          * Is the query for the blog homepage?
4466          *
4467          * This is the page which shows the time based blog content of your site.
4468          *
4469          * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_for_posts'.
4470          *
4471          * If you set a static page for the front page of your site, this function will return
4472          * true only on the page you set as the "Posts page".
4473          *
4474          * @see WP_Query::is_front_page()
4475          *
4476          * @since 3.1.0
4477          *
4478          * @return bool True if blog view homepage.
4479          */
4480         public function is_home() {
4481                 return (bool) $this->is_home;
4482         }
4483
4484         /**
4485          * Is the query for an existing month archive?
4486          *
4487          * @since 3.1.0
4488          *
4489          * @return bool
4490          */
4491         public function is_month() {
4492                 return (bool) $this->is_month;
4493         }
4494
4495         /**
4496          * Is the query for an existing single page?
4497          *
4498          * If the $page parameter is specified, this function will additionally
4499          * check if the query is for one of the pages specified.
4500          *
4501          * @see WP_Query::is_single()
4502          * @see WP_Query::is_singular()
4503          *
4504          * @since 3.1.0
4505          *
4506          * @param int|string|array $page Optional. Page ID, title, slug, path, or array of such. Default empty.
4507          * @return bool Whether the query is for an existing single page.
4508          */
4509         public function is_page( $page = '' ) {
4510                 if ( !$this->is_page )
4511                         return false;
4512
4513                 if ( empty( $page ) )
4514                         return true;
4515
4516                 $page_obj = $this->get_queried_object();
4517
4518                 $page = array_map( 'strval', (array) $page );
4519
4520                 if ( in_array( (string) $page_obj->ID, $page ) ) {
4521                         return true;
4522                 } elseif ( in_array( $page_obj->post_title, $page ) ) {
4523                         return true;
4524                 } elseif ( in_array( $page_obj->post_name, $page ) ) {
4525                         return true;
4526                 } else {
4527                         foreach ( $page as $pagepath ) {
4528                                 if ( ! strpos( $pagepath, '/' ) ) {
4529                                         continue;
4530                                 }
4531                                 $pagepath_obj = get_page_by_path( $pagepath );
4532
4533                                 if ( $pagepath_obj && ( $pagepath_obj->ID == $page_obj->ID ) ) {
4534                                         return true;
4535                                 }
4536                         }
4537                 }
4538
4539                 return false;
4540         }
4541
4542         /**
4543          * Is the query for paged result and not for the first page?
4544          *
4545          * @since 3.1.0
4546          *
4547          * @return bool
4548          */
4549         public function is_paged() {
4550                 return (bool) $this->is_paged;
4551         }
4552
4553         /**
4554          * Is the query for a post or page preview?
4555          *
4556          * @since 3.1.0
4557          *
4558          * @return bool
4559          */
4560         public function is_preview() {
4561                 return (bool) $this->is_preview;
4562         }
4563
4564         /**
4565          * Is the query for the robots file?
4566          *
4567          * @since 3.1.0
4568          *
4569          * @return bool
4570          */
4571         public function is_robots() {
4572                 return (bool) $this->is_robots;
4573         }
4574
4575         /**
4576          * Is the query for a search?
4577          *
4578          * @since 3.1.0
4579          *
4580          * @return bool
4581          */
4582         public function is_search() {
4583                 return (bool) $this->is_search;
4584         }
4585
4586         /**
4587          * Is the query for an existing single post?
4588          *
4589          * Works for any post type, except attachments and pages
4590          *
4591          * If the $post parameter is specified, this function will additionally
4592          * check if the query is for one of the Posts specified.
4593          *
4594          * @see WP_Query::is_page()
4595          * @see WP_Query::is_singular()
4596          *
4597          * @since 3.1.0
4598          *
4599          * @param int|string|array $post Optional. Post ID, title, slug, path, or array of such. Default empty.
4600          * @return bool Whether the query is for an existing single post.
4601          */
4602         public function is_single( $post = '' ) {
4603                 if ( !$this->is_single )
4604                         return false;
4605
4606                 if ( empty($post) )
4607                         return true;
4608
4609                 $post_obj = $this->get_queried_object();
4610
4611                 $post = array_map( 'strval', (array) $post );
4612
4613                 if ( in_array( (string) $post_obj->ID, $post ) ) {
4614                         return true;
4615                 } elseif ( in_array( $post_obj->post_title, $post ) ) {
4616                         return true;
4617                 } elseif ( in_array( $post_obj->post_name, $post ) ) {
4618                         return true;
4619                 } else {
4620                         foreach ( $post as $postpath ) {
4621                                 if ( ! strpos( $postpath, '/' ) ) {
4622                                         continue;
4623                                 }
4624                                 $postpath_obj = get_page_by_path( $postpath, OBJECT, $post_obj->post_type );
4625
4626                                 if ( $postpath_obj && ( $postpath_obj->ID == $post_obj->ID ) ) {
4627                                         return true;
4628                                 }
4629                         }
4630                 }
4631                 return false;
4632         }
4633
4634         /**
4635          * Is the query for an existing single post of any post type (post, attachment, page, ... )?
4636          *
4637          * If the $post_types parameter is specified, this function will additionally
4638          * check if the query is for one of the Posts Types specified.
4639          *
4640          * @see WP_Query::is_page()
4641          * @see WP_Query::is_single()
4642          *
4643          * @since 3.1.0
4644          *
4645          * @param string|array $post_types Optional. Post type or array of post types. Default empty.
4646          * @return bool Whether the query is for an existing single post of any of the given post types.
4647          */
4648         public function is_singular( $post_types = '' ) {
4649                 if ( empty( $post_types ) || !$this->is_singular )
4650                         return (bool) $this->is_singular;
4651
4652                 $post_obj = $this->get_queried_object();
4653
4654                 return in_array( $post_obj->post_type, (array) $post_types );
4655         }
4656
4657         /**
4658          * Is the query for a specific time?
4659          *
4660          * @since 3.1.0
4661          *
4662          * @return bool
4663          */
4664         public function is_time() {
4665                 return (bool) $this->is_time;
4666         }
4667
4668         /**
4669          * Is the query for a trackback endpoint call?
4670          *
4671          * @since 3.1.0
4672          *
4673          * @return bool
4674          */
4675         public function is_trackback() {
4676                 return (bool) $this->is_trackback;
4677         }
4678
4679         /**
4680          * Is the query for an existing year archive?
4681          *
4682          * @since 3.1.0
4683          *
4684          * @return bool
4685          */
4686         public function is_year() {
4687                 return (bool) $this->is_year;
4688         }
4689
4690         /**
4691          * Is the query a 404 (returns no results)?
4692          *
4693          * @since 3.1.0
4694          *
4695          * @return bool
4696          */
4697         public function is_404() {
4698                 return (bool) $this->is_404;
4699         }
4700
4701         /**
4702          * Is the query for an embedded post?
4703          *
4704          * @since 4.4.0
4705          *
4706          * @return bool
4707          */
4708         public function is_embed() {
4709                 return (bool) $this->is_embed;
4710         }
4711
4712         /**
4713          * Is the query the main query?
4714          *
4715          * @since 3.3.0
4716          *
4717          * @global WP_Query $wp_query Global WP_Query instance.
4718          *
4719          * @return bool
4720          */
4721         public function is_main_query() {
4722                 global $wp_the_query;
4723                 return $wp_the_query === $this;
4724         }
4725
4726         /**
4727          * Set up global post data.
4728          *
4729          * @since 4.1.0
4730          * @since 4.4.0 Added the ability to pass a post ID to `$post`.
4731          *
4732          * @global int             $id
4733          * @global WP_User         $authordata
4734          * @global string|int|bool $currentday
4735          * @global string|int|bool $currentmonth
4736          * @global int             $page
4737          * @global array           $pages
4738          * @global int             $multipage
4739          * @global int             $more
4740          * @global int             $numpages
4741          *
4742          * @param WP_Post|object|int $post WP_Post instance or Post ID/object.
4743          * @return true True when finished.
4744          */
4745         public function setup_postdata( $post ) {
4746                 global $id, $authordata, $currentday, $currentmonth, $page, $pages, $multipage, $more, $numpages;
4747
4748                 if ( ! ( $post instanceof WP_Post ) ) {
4749                         $post = get_post( $post );
4750                 }
4751
4752                 if ( ! $post ) {
4753                         return;
4754                 }
4755
4756                 $id = (int) $post->ID;
4757
4758                 $authordata = get_userdata($post->post_author);
4759
4760                 $currentday = mysql2date('d.m.y', $post->post_date, false);
4761                 $currentmonth = mysql2date('m', $post->post_date, false);
4762                 $numpages = 1;
4763                 $multipage = 0;
4764                 $page = $this->get( 'page' );
4765                 if ( ! $page )
4766                         $page = 1;
4767
4768                 /*
4769                  * Force full post content when viewing the permalink for the $post,
4770                  * or when on an RSS feed. Otherwise respect the 'more' tag.
4771                  */
4772                 if ( $post->ID === get_queried_object_id() && ( $this->is_page() || $this->is_single() ) ) {
4773                         $more = 1;
4774                 } elseif ( $this->is_feed() ) {
4775                         $more = 1;
4776                 } else {
4777                         $more = 0;
4778                 }
4779
4780                 $content = $post->post_content;
4781                 if ( false !== strpos( $content, '<!--nextpage-->' ) ) {
4782                         $content = str_replace( "\n<!--nextpage-->\n", '<!--nextpage-->', $content );
4783                         $content = str_replace( "\n<!--nextpage-->", '<!--nextpage-->', $content );
4784                         $content = str_replace( "<!--nextpage-->\n", '<!--nextpage-->', $content );
4785
4786                         // Ignore nextpage at the beginning of the content.
4787                         if ( 0 === strpos( $content, '<!--nextpage-->' ) )
4788                                 $content = substr( $content, 15 );
4789
4790                         $pages = explode('<!--nextpage-->', $content);
4791                 } else {
4792                         $pages = array( $post->post_content );
4793                 }
4794
4795                 /**
4796                  * Filter the "pages" derived from splitting the post content.
4797                  *
4798                  * "Pages" are determined by splitting the post content based on the presence
4799                  * of `<!-- nextpage -->` tags.
4800                  *
4801                  * @since 4.4.0
4802                  *
4803                  * @param array   $pages Array of "pages" derived from the post content.
4804                  *                       of `<!-- nextpage -->` tags..
4805                  * @param WP_Post $post  Current post object.
4806                  */
4807                 $pages = apply_filters( 'content_pagination', $pages, $post );
4808
4809                 $numpages = count( $pages );
4810
4811                 if ( $numpages > 1 ) {
4812                         if ( $page > 1 ) {
4813                                 $more = 1;
4814                         }
4815                         $multipage = 1;
4816                 } else {
4817                         $multipage = 0;
4818                 }
4819
4820                 /**
4821                  * Fires once the post data has been setup.
4822                  *
4823                  * @since 2.8.0
4824                  * @since 4.1.0 Introduced `$this` parameter.
4825                  *
4826                  * @param WP_Post  &$post The Post object (passed by reference).
4827                  * @param WP_Query &$this The current Query object (passed by reference).
4828                  */
4829                 do_action_ref_array( 'the_post', array( &$post, &$this ) );
4830
4831                 return true;
4832         }
4833         /**
4834          * After looping through a nested query, this function
4835          * restores the $post global to the current post in this query.
4836          *
4837          * @since 3.7.0
4838          *
4839          * @global WP_Post $post
4840          */
4841         public function reset_postdata() {
4842                 if ( ! empty( $this->post ) ) {
4843                         $GLOBALS['post'] = $this->post;
4844                         $this->setup_postdata( $this->post );
4845                 }
4846         }
4847
4848         /**
4849          * Lazyload term meta for posts in the loop.
4850          *
4851          * @since 4.4.0
4852          * @deprecated 4.5.0 See wp_queue_posts_for_term_meta_lazyload().
4853          *
4854          * @param mixed $check
4855          * @param int   $term_id
4856          * @return mixed
4857          */
4858         public function lazyload_term_meta( $check, $term_id ) {
4859                 _deprecated_function( __METHOD__, '4.5.0' );
4860                 return $check;
4861         }
4862
4863         /**
4864          * Lazyload comment meta for comments in the loop.
4865          *
4866          * @since 4.4.0
4867          * @deprecated 4.5.0 See wp_queue_comments_for_comment_meta_lazyload().
4868          *
4869          * @param mixed $check
4870          * @param int   $comment_id
4871          * @return mixed
4872          */
4873         public function lazyload_comment_meta( $check, $comment_id ) {
4874                 _deprecated_function( __METHOD__, '4.5.0' );
4875                 return $check;
4876         }
4877 }
4878
4879 /**
4880  * Redirect old slugs to the correct permalink.
4881  *
4882  * Attempts to find the current slug from the past slugs.
4883  *
4884  * @since 2.1.0
4885  *
4886  * @global WP_Query   $wp_query   Global WP_Query instance.
4887  * @global wpdb       $wpdb       WordPress database abstraction object.
4888  */
4889 function wp_old_slug_redirect() {
4890         global $wp_query;
4891
4892         if ( is_404() && '' !== $wp_query->query_vars['name'] ) :
4893                 global $wpdb;
4894
4895                 // Guess the current post_type based on the query vars.
4896                 if ( get_query_var( 'post_type' ) ) {
4897                         $post_type = get_query_var( 'post_type' );
4898                 } elseif ( get_query_var( 'attachment' ) ) {
4899                         $post_type = 'attachment';
4900                 } elseif ( ! empty( $wp_query->query_vars['pagename'] ) ) {
4901                         $post_type = 'page';
4902                 } else {
4903                         $post_type = 'post';
4904                 }
4905
4906                 if ( is_array( $post_type ) ) {
4907                         if ( count( $post_type ) > 1 )
4908                                 return;
4909                         $post_type = reset( $post_type );
4910                 }
4911
4912                 // Do not attempt redirect for hierarchical post types
4913                 if ( is_post_type_hierarchical( $post_type ) )
4914                         return;
4915
4916                 $query = $wpdb->prepare("SELECT post_id FROM $wpdb->postmeta, $wpdb->posts WHERE ID = post_id AND post_type = %s AND meta_key = '_wp_old_slug' AND meta_value = %s", $post_type, $wp_query->query_vars['name']);
4917
4918                 // if year, monthnum, or day have been specified, make our query more precise
4919                 // just in case there are multiple identical _wp_old_slug values
4920                 if ( '' != $wp_query->query_vars['year'] )
4921                         $query .= $wpdb->prepare(" AND YEAR(post_date) = %d", $wp_query->query_vars['year']);
4922                 if ( '' != $wp_query->query_vars['monthnum'] )
4923                         $query .= $wpdb->prepare(" AND MONTH(post_date) = %d", $wp_query->query_vars['monthnum']);
4924                 if ( '' != $wp_query->query_vars['day'] )
4925                         $query .= $wpdb->prepare(" AND DAYOFMONTH(post_date) = %d", $wp_query->query_vars['day']);
4926
4927                 $id = (int) $wpdb->get_var($query);
4928
4929                 if ( ! $id )
4930                         return;
4931
4932                 $link = get_permalink( $id );
4933
4934                 if ( isset( $GLOBALS['wp_query']->query_vars['paged'] ) && $GLOBALS['wp_query']->query_vars['paged'] > 1 ) {
4935                         $link = user_trailingslashit( trailingslashit( $link ) . 'page/' . $GLOBALS['wp_query']->query_vars['paged'] );
4936                 } elseif( is_embed() ) {
4937                         $link = user_trailingslashit( trailingslashit( $link ) . 'embed' );
4938                 }
4939
4940                 /**
4941                  * Filter the old slug redirect URL.
4942                  *
4943                  * @since 4.4.0
4944                  *
4945                  * @param string $link The redirect URL.
4946                  */
4947                 $link = apply_filters( 'old_slug_redirect_url', $link );
4948
4949                 if ( ! $link ) {
4950                         return;
4951                 }
4952
4953                 wp_redirect( $link, 301 ); // Permanent redirect
4954                 exit;
4955         endif;
4956 }
4957
4958 /**
4959  * Set up global post data.
4960  *
4961  * @since 1.5.0
4962  * @since 4.4.0 Added the ability to pass a post ID to `$post`.
4963  *
4964  * @global WP_Query $wp_query Global WP_Query instance.
4965  *
4966  * @param WP_Post|object|int $post WP_Post instance or Post ID/object.
4967  * @return bool True when finished.
4968  */
4969 function setup_postdata( $post ) {
4970         global $wp_query;
4971
4972         if ( ! empty( $wp_query ) && $wp_query instanceof WP_Query ) {
4973                 return $wp_query->setup_postdata( $post );
4974         }
4975
4976         return false;
4977 }