]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/query.php
WordPress 4.0.1
[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 http://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  * @see WP_Query::get()
18  * @since 1.5.0
19  * @uses $wp_query
20  *
21  * @param string $var       The variable key to retrieve.
22  * @param mixed  $default   Value to return if the query variable is not set. Default ''.
23  * @return mixed
24  */
25 function get_query_var( $var, $default = '' ) {
26         global $wp_query;
27
28         return $wp_query->get( $var, $default );
29 }
30
31 /**
32  * Retrieve the currently-queried object. Wrapper for $wp_query->get_queried_object()
33  *
34  * @uses WP_Query::get_queried_object
35  *
36  * @since 3.1.0
37  * @access public
38  *
39  * @return object
40  */
41 function get_queried_object() {
42         global $wp_query;
43         return $wp_query->get_queried_object();
44 }
45
46 /**
47  * Retrieve ID of the current queried object. Wrapper for $wp_query->get_queried_object_id()
48  *
49  * @uses WP_Query::get_queried_object_id()
50  *
51  * @since 3.1.0
52  * @access public
53  *
54  * @return int
55  */
56 function get_queried_object_id() {
57         global $wp_query;
58         return $wp_query->get_queried_object_id();
59 }
60
61 /**
62  * Set query variable.
63  *
64  * @see WP_Query::set()
65  * @since 2.2.0
66  * @uses $wp_query
67  *
68  * @param string $var Query variable key.
69  * @param mixed $value
70  * @return null
71  */
72 function set_query_var($var, $value) {
73         global $wp_query;
74
75         return $wp_query->set($var, $value);
76 }
77
78 /**
79  * Set up The Loop with query parameters.
80  *
81  * This will override the current WordPress Loop and shouldn't be used more than
82  * once. This must not be used within the WordPress Loop.
83  *
84  * @since 1.5.0
85  * @uses $wp_query
86  *
87  * @param string $query
88  * @return array List of posts
89  */
90 function query_posts($query) {
91         $GLOBALS['wp_query'] = new WP_Query();
92         return $GLOBALS['wp_query']->query($query);
93 }
94
95 /**
96  * Destroy the previous query and set up a new query.
97  *
98  * This should be used after {@link query_posts()} and before another {@link
99  * query_posts()}. This will remove obscure bugs that occur when the previous
100  * wp_query object is not destroyed properly before another is set up.
101  *
102  * @since 2.3.0
103  * @uses $wp_query
104  */
105 function wp_reset_query() {
106         $GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
107         wp_reset_postdata();
108 }
109
110 /**
111  * After looping through a separate query, this function restores
112  * the $post global to the current post in the main query.
113  *
114  * @since 3.0.0
115  * @uses $wp_query
116  */
117 function wp_reset_postdata() {
118         global $wp_query;
119
120         if ( isset( $wp_query ) ) {
121                 $wp_query->reset_postdata();
122         }
123 }
124
125 /*
126  * Query type checks.
127  */
128
129 /**
130  * Is the query for an existing archive page?
131  *
132  * Month, Year, Category, Author, Post Type archive...
133  *
134  * @see WP_Query::is_archive()
135  * @since 1.5.0
136  * @uses $wp_query
137  *
138  * @return bool
139  */
140 function is_archive() {
141         global $wp_query;
142
143         if ( ! isset( $wp_query ) ) {
144                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
145                 return false;
146         }
147
148         return $wp_query->is_archive();
149 }
150
151 /**
152  * Is the query for an existing post type archive page?
153  *
154  * @see WP_Query::is_post_type_archive()
155  * @since 3.1.0
156  * @uses $wp_query
157  *
158  * @param mixed $post_types Optional. Post type or array of posts types to check against.
159  * @return bool
160  */
161 function is_post_type_archive( $post_types = '' ) {
162         global $wp_query;
163
164         if ( ! isset( $wp_query ) ) {
165                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
166                 return false;
167         }
168
169         return $wp_query->is_post_type_archive( $post_types );
170 }
171
172 /**
173  * Is the query for an existing attachment page?
174  *
175  * @see WP_Query::is_attachment()
176  * @since 2.0.0
177  * @uses $wp_query
178  *
179  * @param mixed $attachment Attachment ID, title, slug, or array of such.
180  * @return bool
181  */
182 function is_attachment( $attachment = '' ) {
183         global $wp_query;
184
185         if ( ! isset( $wp_query ) ) {
186                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
187                 return false;
188         }
189
190         return $wp_query->is_attachment( $attachment );
191 }
192
193 /**
194  * Is the query for an existing author archive page?
195  *
196  * If the $author parameter is specified, this function will additionally
197  * check if the query is for one of the authors specified.
198  *
199  * @see WP_Query::is_author()
200  * @since 1.5.0
201  * @uses $wp_query
202  *
203  * @param mixed $author Optional. User ID, nickname, nicename, or array of User IDs, nicknames, and nicenames
204  * @return bool
205  */
206 function is_author( $author = '' ) {
207         global $wp_query;
208
209         if ( ! isset( $wp_query ) ) {
210                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
211                 return false;
212         }
213
214         return $wp_query->is_author( $author );
215 }
216
217 /**
218  * Is the query for an existing category archive page?
219  *
220  * If the $category parameter is specified, this function will additionally
221  * check if the query is for one of the categories specified.
222  *
223  * @see WP_Query::is_category()
224  * @since 1.5.0
225  * @uses $wp_query
226  *
227  * @param mixed $category Optional. Category ID, name, slug, or array of Category IDs, names, and slugs.
228  * @return bool
229  */
230 function is_category( $category = '' ) {
231         global $wp_query;
232
233         if ( ! isset( $wp_query ) ) {
234                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
235                 return false;
236         }
237
238         return $wp_query->is_category( $category );
239 }
240
241 /**
242  * Is the query for an existing tag archive page?
243  *
244  * If the $tag parameter is specified, this function will additionally
245  * check if the query is for one of the tags specified.
246  *
247  * @see WP_Query::is_tag()
248  * @since 2.3.0
249  * @uses $wp_query
250  *
251  * @param mixed $tag Optional. Tag ID, name, slug, or array of Tag IDs, names, and slugs.
252  * @return bool
253  */
254 function is_tag( $tag = '' ) {
255         global $wp_query;
256
257         if ( ! isset( $wp_query ) ) {
258                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
259                 return false;
260         }
261
262         return $wp_query->is_tag( $tag );
263 }
264
265 /**
266  * Is the query for an existing taxonomy archive page?
267  *
268  * If the $taxonomy parameter is specified, this function will additionally
269  * check if the query is for that specific $taxonomy.
270  *
271  * If the $term parameter is specified in addition to the $taxonomy parameter,
272  * this function will additionally check if the query is for one of the terms
273  * specified.
274  *
275  * @see WP_Query::is_tax()
276  * @since 2.5.0
277  * @uses $wp_query
278  *
279  * @param mixed $taxonomy Optional. Taxonomy slug or slugs.
280  * @param mixed $term Optional. Term ID, name, slug or array of Term IDs, names, and slugs.
281  * @return bool
282  */
283 function is_tax( $taxonomy = '', $term = '' ) {
284         global $wp_query;
285
286         if ( ! isset( $wp_query ) ) {
287                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
288                 return false;
289         }
290
291         return $wp_query->is_tax( $taxonomy, $term );
292 }
293
294 /**
295  * Whether the current URL is within the comments popup window.
296  *
297  * @see WP_Query::is_comments_popup()
298  * @since 1.5.0
299  * @uses $wp_query
300  *
301  * @return bool
302  */
303 function is_comments_popup() {
304         global $wp_query;
305
306         if ( ! isset( $wp_query ) ) {
307                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
308                 return false;
309         }
310
311         return $wp_query->is_comments_popup();
312 }
313
314 /**
315  * Is the query for an existing date archive?
316  *
317  * @see WP_Query::is_date()
318  * @since 1.5.0
319  * @uses $wp_query
320  *
321  * @return bool
322  */
323 function is_date() {
324         global $wp_query;
325
326         if ( ! isset( $wp_query ) ) {
327                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
328                 return false;
329         }
330
331         return $wp_query->is_date();
332 }
333
334 /**
335  * Is the query for an existing day archive?
336  *
337  * @see WP_Query::is_day()
338  * @since 1.5.0
339  * @uses $wp_query
340  *
341  * @return bool
342  */
343 function is_day() {
344         global $wp_query;
345
346         if ( ! isset( $wp_query ) ) {
347                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
348                 return false;
349         }
350
351         return $wp_query->is_day();
352 }
353
354 /**
355  * Is the query for a feed?
356  *
357  * @see WP_Query::is_feed()
358  * @since 1.5.0
359  * @uses $wp_query
360  *
361  * @param string|array $feeds Optional feed types to check.
362  * @return bool
363  */
364 function is_feed( $feeds = '' ) {
365         global $wp_query;
366
367         if ( ! isset( $wp_query ) ) {
368                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
369                 return false;
370         }
371
372         return $wp_query->is_feed( $feeds );
373 }
374
375 /**
376  * Is the query for a comments feed?
377  *
378  * @see WP_Query::is_comments_feed()
379  * @since 3.0.0
380  * @uses $wp_query
381  *
382  * @return bool
383  */
384 function is_comment_feed() {
385         global $wp_query;
386
387         if ( ! isset( $wp_query ) ) {
388                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
389                 return false;
390         }
391
392         return $wp_query->is_comment_feed();
393 }
394
395 /**
396  * Is the query for the front page of the site?
397  *
398  * This is for what is displayed at your site's main URL.
399  *
400  * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_on_front'.
401  *
402  * If you set a static page for the front page of your site, this function will return
403  * true when viewing that page.
404  *
405  * Otherwise the same as @see is_home()
406  *
407  * @see WP_Query::is_front_page()
408  * @since 2.5.0
409  * @uses is_home()
410  * @uses get_option()
411  *
412  * @return bool True, if front of site.
413  */
414 function is_front_page() {
415         global $wp_query;
416
417         if ( ! isset( $wp_query ) ) {
418                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
419                 return false;
420         }
421
422         return $wp_query->is_front_page();
423 }
424
425 /**
426  * Is the query for the blog homepage?
427  *
428  * This is the page which shows the time based blog content of your site.
429  *
430  * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_for_posts'.
431  *
432  * If you set a static page for the front page of your site, this function will return
433  * true only on the page you set as the "Posts page".
434  *
435  * @see is_front_page()
436  *
437  * @see WP_Query::is_home()
438  * @since 1.5.0
439  * @uses $wp_query
440  *
441  * @return bool True if blog view homepage.
442  */
443 function is_home() {
444         global $wp_query;
445
446         if ( ! isset( $wp_query ) ) {
447                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
448                 return false;
449         }
450
451         return $wp_query->is_home();
452 }
453
454 /**
455  * Is the query for an existing month archive?
456  *
457  * @see WP_Query::is_month()
458  * @since 1.5.0
459  * @uses $wp_query
460  *
461  * @return bool
462  */
463 function is_month() {
464         global $wp_query;
465
466         if ( ! isset( $wp_query ) ) {
467                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
468                 return false;
469         }
470
471         return $wp_query->is_month();
472 }
473
474 /**
475  * Is the query for an existing single page?
476  *
477  * If the $page parameter is specified, this function will additionally
478  * check if the query is for one of the pages specified.
479  *
480  * @see is_single()
481  * @see is_singular()
482  *
483  * @see WP_Query::is_page()
484  * @since 1.5.0
485  * @uses $wp_query
486  *
487  * @param mixed $page Page ID, title, slug, or array of such.
488  * @return bool
489  */
490 function is_page( $page = '' ) {
491         global $wp_query;
492
493         if ( ! isset( $wp_query ) ) {
494                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
495                 return false;
496         }
497
498         return $wp_query->is_page( $page );
499 }
500
501 /**
502  * Is the query for paged result and not for the first page?
503  *
504  * @see WP_Query::is_paged()
505  * @since 1.5.0
506  * @uses $wp_query
507  *
508  * @return bool
509  */
510 function is_paged() {
511         global $wp_query;
512
513         if ( ! isset( $wp_query ) ) {
514                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
515                 return false;
516         }
517
518         return $wp_query->is_paged();
519 }
520
521 /**
522  * Is the query for a post or page preview?
523  *
524  * @see WP_Query::is_preview()
525  * @since 2.0.0
526  * @uses $wp_query
527  *
528  * @return bool
529  */
530 function is_preview() {
531         global $wp_query;
532
533         if ( ! isset( $wp_query ) ) {
534                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
535                 return false;
536         }
537
538         return $wp_query->is_preview();
539 }
540
541 /**
542  * Is the query for the robots file?
543  *
544  * @see WP_Query::is_robots()
545  * @since 2.1.0
546  * @uses $wp_query
547  *
548  * @return bool
549  */
550 function is_robots() {
551         global $wp_query;
552
553         if ( ! isset( $wp_query ) ) {
554                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
555                 return false;
556         }
557
558         return $wp_query->is_robots();
559 }
560
561 /**
562  * Is the query for a search?
563  *
564  * @see WP_Query::is_search()
565  * @since 1.5.0
566  * @uses $wp_query
567  *
568  * @return bool
569  */
570 function is_search() {
571         global $wp_query;
572
573         if ( ! isset( $wp_query ) ) {
574                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
575                 return false;
576         }
577
578         return $wp_query->is_search();
579 }
580
581 /**
582  * Is the query for an existing single post?
583  *
584  * Works for any post type, except attachments and pages
585  *
586  * If the $post parameter is specified, this function will additionally
587  * check if the query is for one of the Posts specified.
588  *
589  * @see is_page()
590  * @see is_singular()
591  *
592  * @see WP_Query::is_single()
593  * @since 1.5.0
594  * @uses $wp_query
595  *
596  * @param mixed $post Post ID, title, slug, or array of such.
597  * @return bool
598  */
599 function is_single( $post = '' ) {
600         global $wp_query;
601
602         if ( ! isset( $wp_query ) ) {
603                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
604                 return false;
605         }
606
607         return $wp_query->is_single( $post );
608 }
609
610 /**
611  * Is the query for an existing single post of any post type (post, attachment, page, ... )?
612  *
613  * If the $post_types parameter is specified, this function will additionally
614  * check if the query is for one of the Posts Types specified.
615  *
616  * @see is_page()
617  * @see is_single()
618  *
619  * @see WP_Query::is_singular()
620  * @since 1.5.0
621  * @uses $wp_query
622  *
623  * @param mixed $post_types Optional. Post Type or array of Post Types
624  * @return bool
625  */
626 function is_singular( $post_types = '' ) {
627         global $wp_query;
628
629         if ( ! isset( $wp_query ) ) {
630                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
631                 return false;
632         }
633
634         return $wp_query->is_singular( $post_types );
635 }
636
637 /**
638  * Is the query for a specific time?
639  *
640  * @see WP_Query::is_time()
641  * @since 1.5.0
642  * @uses $wp_query
643  *
644  * @return bool
645  */
646 function is_time() {
647         global $wp_query;
648
649         if ( ! isset( $wp_query ) ) {
650                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
651                 return false;
652         }
653
654         return $wp_query->is_time();
655 }
656
657 /**
658  * Is the query for a trackback endpoint call?
659  *
660  * @see WP_Query::is_trackback()
661  * @since 1.5.0
662  * @uses $wp_query
663  *
664  * @return bool
665  */
666 function is_trackback() {
667         global $wp_query;
668
669         if ( ! isset( $wp_query ) ) {
670                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
671                 return false;
672         }
673
674         return $wp_query->is_trackback();
675 }
676
677 /**
678  * Is the query for an existing year archive?
679  *
680  * @see WP_Query::is_year()
681  * @since 1.5.0
682  * @uses $wp_query
683  *
684  * @return bool
685  */
686 function is_year() {
687         global $wp_query;
688
689         if ( ! isset( $wp_query ) ) {
690                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
691                 return false;
692         }
693
694         return $wp_query->is_year();
695 }
696
697 /**
698  * Is the query a 404 (returns no results)?
699  *
700  * @see WP_Query::is_404()
701  * @since 1.5.0
702  * @uses $wp_query
703  *
704  * @return bool
705  */
706 function is_404() {
707         global $wp_query;
708
709         if ( ! isset( $wp_query ) ) {
710                 _doing_it_wrong( __FUNCTION__, __( 'Conditional query tags do not work before the query is run. Before then, they always return false.' ), '3.1' );
711                 return false;
712         }
713
714         return $wp_query->is_404();
715 }
716
717 /**
718  * Is the query the main query?
719  *
720  * @since 3.3.0
721  *
722  * @return bool
723  */
724 function is_main_query() {
725         if ( 'pre_get_posts' === current_filter() ) {
726                 $message = sprintf( __( 'In <code>%1$s</code>, use the <code>%2$s</code> method, not the <code>%3$s</code> function. See %4$s.' ),
727                         'pre_get_posts', 'WP_Query::is_main_query()', 'is_main_query()', __( 'http://codex.wordpress.org/Function_Reference/is_main_query' ) );
728                 _doing_it_wrong( __FUNCTION__, $message, '3.7' );
729         }
730
731         global $wp_query;
732         return $wp_query->is_main_query();
733 }
734
735 /*
736  * The Loop. Post loop control.
737  */
738
739 /**
740  * Whether current WordPress query has results to loop over.
741  *
742  * @see WP_Query::have_posts()
743  * @since 1.5.0
744  * @uses $wp_query
745  *
746  * @return bool
747  */
748 function have_posts() {
749         global $wp_query;
750
751         return $wp_query->have_posts();
752 }
753
754 /**
755  * Whether the caller is in the Loop.
756  *
757  * @since 2.0.0
758  * @uses $wp_query
759  *
760  * @return bool True if caller is within loop, false if loop hasn't started or ended.
761  */
762 function in_the_loop() {
763         global $wp_query;
764
765         return $wp_query->in_the_loop;
766 }
767
768 /**
769  * Rewind the loop posts.
770  *
771  * @see WP_Query::rewind_posts()
772  * @since 1.5.0
773  * @uses $wp_query
774  *
775  * @return null
776  */
777 function rewind_posts() {
778         global $wp_query;
779
780         return $wp_query->rewind_posts();
781 }
782
783 /**
784  * Iterate the post index in the loop.
785  *
786  * @see WP_Query::the_post()
787  * @since 1.5.0
788  * @uses $wp_query
789  */
790 function the_post() {
791         global $wp_query;
792
793         $wp_query->the_post();
794 }
795
796 /*
797  * Comments loop.
798  */
799
800 /**
801  * Whether there are comments to loop over.
802  *
803  * @see WP_Query::have_comments()
804  * @since 2.2.0
805  * @uses $wp_query
806  *
807  * @return bool
808  */
809 function have_comments() {
810         global $wp_query;
811         return $wp_query->have_comments();
812 }
813
814 /**
815  * Iterate comment index in the comment loop.
816  *
817  * @see WP_Query::the_comment()
818  * @since 2.2.0
819  * @uses $wp_query
820  *
821  * @return object
822  */
823 function the_comment() {
824         global $wp_query;
825         return $wp_query->the_comment();
826 }
827
828 /*
829  * WP_Query
830  */
831
832 /**
833  * The WordPress Query class.
834  *
835  * @link http://codex.wordpress.org/Function_Reference/WP_Query Codex page.
836  *
837  * @since 1.5.0
838  */
839 class WP_Query {
840
841         /**
842          * Query vars set by the user
843          *
844          * @since 1.5.0
845          * @access public
846          * @var array
847          */
848         public $query;
849
850         /**
851          * Query vars, after parsing
852          *
853          * @since 1.5.0
854          * @access public
855          * @var array
856          */
857         public $query_vars = array();
858
859         /**
860          * Taxonomy query, as passed to get_tax_sql()
861          *
862          * @since 3.1.0
863          * @access public
864          * @var object WP_Tax_Query
865          */
866         public $tax_query;
867
868         /**
869          * Metadata query container
870          *
871          * @since 3.2.0
872          * @access public
873          * @var object WP_Meta_Query
874          */
875         public $meta_query = false;
876
877         /**
878          * Date query container
879          *
880          * @since 3.7.0
881          * @access public
882          * @var object WP_Date_Query
883          */
884         public $date_query = false;
885
886         /**
887          * Holds the data for a single object that is queried.
888          *
889          * Holds the contents of a post, page, category, attachment.
890          *
891          * @since 1.5.0
892          * @access public
893          * @var object|array
894          */
895         public $queried_object;
896
897         /**
898          * The ID of the queried object.
899          *
900          * @since 1.5.0
901          * @access public
902          * @var int
903          */
904         public $queried_object_id;
905
906         /**
907          * Get post database query.
908          *
909          * @since 2.0.1
910          * @access public
911          * @var string
912          */
913         public $request;
914
915         /**
916          * List of posts.
917          *
918          * @since 1.5.0
919          * @access public
920          * @var array
921          */
922         public $posts;
923
924         /**
925          * The amount of posts for the current query.
926          *
927          * @since 1.5.0
928          * @access public
929          * @var int
930          */
931         public $post_count = 0;
932
933         /**
934          * Index of the current item in the loop.
935          *
936          * @since 1.5.0
937          * @access public
938          * @var int
939          */
940         public $current_post = -1;
941
942         /**
943          * Whether the loop has started and the caller is in the loop.
944          *
945          * @since 2.0.0
946          * @access public
947          * @var bool
948          */
949         public $in_the_loop = false;
950
951         /**
952          * The current post.
953          *
954          * @since 1.5.0
955          * @access public
956          * @var WP_Post
957          */
958         public $post;
959
960         /**
961          * The list of comments for current post.
962          *
963          * @since 2.2.0
964          * @access public
965          * @var array
966          */
967         public $comments;
968
969         /**
970          * The amount of comments for the posts.
971          *
972          * @since 2.2.0
973          * @access public
974          * @var int
975          */
976         public $comment_count = 0;
977
978         /**
979          * The index of the comment in the comment loop.
980          *
981          * @since 2.2.0
982          * @access public
983          * @var int
984          */
985         public $current_comment = -1;
986
987         /**
988          * Current comment ID.
989          *
990          * @since 2.2.0
991          * @access public
992          * @var int
993          */
994         public $comment;
995
996         /**
997          * The amount of found posts for the current query.
998          *
999          * If limit clause was not used, equals $post_count.
1000          *
1001          * @since 2.1.0
1002          * @access public
1003          * @var int
1004          */
1005         public $found_posts = 0;
1006
1007         /**
1008          * The amount of pages.
1009          *
1010          * @since 2.1.0
1011          * @access public
1012          * @var int
1013          */
1014         public $max_num_pages = 0;
1015
1016         /**
1017          * The amount of comment pages.
1018          *
1019          * @since 2.7.0
1020          * @access public
1021          * @var int
1022          */
1023         public $max_num_comment_pages = 0;
1024
1025         /**
1026          * Set if query is single post.
1027          *
1028          * @since 1.5.0
1029          * @access public
1030          * @var bool
1031          */
1032         public $is_single = false;
1033
1034         /**
1035          * Set if query is preview of blog.
1036          *
1037          * @since 2.0.0
1038          * @access public
1039          * @var bool
1040          */
1041         public $is_preview = false;
1042
1043         /**
1044          * Set if query returns a page.
1045          *
1046          * @since 1.5.0
1047          * @access public
1048          * @var bool
1049          */
1050         public $is_page = false;
1051
1052         /**
1053          * Set if query is an archive list.
1054          *
1055          * @since 1.5.0
1056          * @access public
1057          * @var bool
1058          */
1059         public $is_archive = false;
1060
1061         /**
1062          * Set if query is part of a date.
1063          *
1064          * @since 1.5.0
1065          * @access public
1066          * @var bool
1067          */
1068         public $is_date = false;
1069
1070         /**
1071          * Set if query contains a year.
1072          *
1073          * @since 1.5.0
1074          * @access public
1075          * @var bool
1076          */
1077         public $is_year = false;
1078
1079         /**
1080          * Set if query contains a month.
1081          *
1082          * @since 1.5.0
1083          * @access public
1084          * @var bool
1085          */
1086         public $is_month = false;
1087
1088         /**
1089          * Set if query contains a day.
1090          *
1091          * @since 1.5.0
1092          * @access public
1093          * @var bool
1094          */
1095         public $is_day = false;
1096
1097         /**
1098          * Set if query contains time.
1099          *
1100          * @since 1.5.0
1101          * @access public
1102          * @var bool
1103          */
1104         public $is_time = false;
1105
1106         /**
1107          * Set if query contains an author.
1108          *
1109          * @since 1.5.0
1110          * @access public
1111          * @var bool
1112          */
1113         public $is_author = false;
1114
1115         /**
1116          * Set if query contains category.
1117          *
1118          * @since 1.5.0
1119          * @access public
1120          * @var bool
1121          */
1122         public $is_category = false;
1123
1124         /**
1125          * Set if query contains tag.
1126          *
1127          * @since 2.3.0
1128          * @access public
1129          * @var bool
1130          */
1131         public $is_tag = false;
1132
1133         /**
1134          * Set if query contains taxonomy.
1135          *
1136          * @since 2.5.0
1137          * @access public
1138          * @var bool
1139          */
1140         public $is_tax = false;
1141
1142         /**
1143          * Set if query was part of a search result.
1144          *
1145          * @since 1.5.0
1146          * @access public
1147          * @var bool
1148          */
1149         public $is_search = false;
1150
1151         /**
1152          * Set if query is feed display.
1153          *
1154          * @since 1.5.0
1155          * @access public
1156          * @var bool
1157          */
1158         public $is_feed = false;
1159
1160         /**
1161          * Set if query is comment feed display.
1162          *
1163          * @since 2.2.0
1164          * @access public
1165          * @var bool
1166          */
1167         public $is_comment_feed = false;
1168
1169         /**
1170          * Set if query is trackback.
1171          *
1172          * @since 1.5.0
1173          * @access public
1174          * @var bool
1175          */
1176         public $is_trackback = false;
1177
1178         /**
1179          * Set if query is blog homepage.
1180          *
1181          * @since 1.5.0
1182          * @access public
1183          * @var bool
1184          */
1185         public $is_home = false;
1186
1187         /**
1188          * Set if query couldn't found anything.
1189          *
1190          * @since 1.5.0
1191          * @access public
1192          * @var bool
1193          */
1194         public $is_404 = false;
1195
1196         /**
1197          * Set if query is within comments popup window.
1198          *
1199          * @since 1.5.0
1200          * @access public
1201          * @var bool
1202          */
1203         public $is_comments_popup = false;
1204
1205         /**
1206          * Set if query is paged
1207          *
1208          * @since 1.5.0
1209          * @access public
1210          * @var bool
1211          */
1212         public $is_paged = false;
1213
1214         /**
1215          * Set if query is part of administration page.
1216          *
1217          * @since 1.5.0
1218          * @access public
1219          * @var bool
1220          */
1221         public $is_admin = false;
1222
1223         /**
1224          * Set if query is an attachment.
1225          *
1226          * @since 2.0.0
1227          * @access public
1228          * @var bool
1229          */
1230         public $is_attachment = false;
1231
1232         /**
1233          * Set if is single, is a page, or is an attachment.
1234          *
1235          * @since 2.1.0
1236          * @access public
1237          * @var bool
1238          */
1239         public $is_singular = false;
1240
1241         /**
1242          * Set if query is for robots.
1243          *
1244          * @since 2.1.0
1245          * @access public
1246          * @var bool
1247          */
1248         public $is_robots = false;
1249
1250         /**
1251          * Set if query contains posts.
1252          *
1253          * Basically, the homepage if the option isn't set for the static homepage.
1254          *
1255          * @since 2.1.0
1256          * @access public
1257          * @var bool
1258          */
1259         public $is_posts_page = false;
1260
1261         /**
1262          * Set if query is for a post type archive.
1263          *
1264          * @since 3.1.0
1265          * @access public
1266          * @var bool
1267          */
1268         public $is_post_type_archive = false;
1269
1270         /**
1271          * Stores the ->query_vars state like md5(serialize( $this->query_vars ) ) so we know
1272          * whether we have to re-parse because something has changed
1273          *
1274          * @since 3.1.0
1275          * @access private
1276          */
1277         private $query_vars_hash = false;
1278
1279         /**
1280          * Whether query vars have changed since the initial parse_query() call. Used to catch modifications to query vars made
1281          * via pre_get_posts hooks.
1282          *
1283          * @since 3.1.1
1284          * @access private
1285          */
1286         private $query_vars_changed = true;
1287
1288         /**
1289          * Set if post thumbnails are cached
1290          *
1291          * @since 3.2.0
1292          * @access public
1293          * @var bool
1294          */
1295          public $thumbnails_cached = false;
1296
1297         /**
1298          * Cached list of search stopwords.
1299          *
1300          * @since 3.7.0
1301          * @var array
1302          */
1303         private $stopwords;
1304
1305         /**
1306          * Resets query flags to false.
1307          *
1308          * The query flags are what page info WordPress was able to figure out.
1309          *
1310          * @since 2.0.0
1311          * @access private
1312          */
1313         private function init_query_flags() {
1314                 $this->is_single = false;
1315                 $this->is_preview = false;
1316                 $this->is_page = false;
1317                 $this->is_archive = false;
1318                 $this->is_date = false;
1319                 $this->is_year = false;
1320                 $this->is_month = false;
1321                 $this->is_day = false;
1322                 $this->is_time = false;
1323                 $this->is_author = false;
1324                 $this->is_category = false;
1325                 $this->is_tag = false;
1326                 $this->is_tax = false;
1327                 $this->is_search = false;
1328                 $this->is_feed = false;
1329                 $this->is_comment_feed = false;
1330                 $this->is_trackback = false;
1331                 $this->is_home = false;
1332                 $this->is_404 = false;
1333                 $this->is_comments_popup = false;
1334                 $this->is_paged = false;
1335                 $this->is_admin = false;
1336                 $this->is_attachment = false;
1337                 $this->is_singular = false;
1338                 $this->is_robots = false;
1339                 $this->is_posts_page = false;
1340                 $this->is_post_type_archive = false;
1341         }
1342
1343         /**
1344          * Initiates object properties and sets default values.
1345          *
1346          * @since 1.5.0
1347          * @access public
1348          */
1349         public function init() {
1350                 unset($this->posts);
1351                 unset($this->query);
1352                 $this->query_vars = array();
1353                 unset($this->queried_object);
1354                 unset($this->queried_object_id);
1355                 $this->post_count = 0;
1356                 $this->current_post = -1;
1357                 $this->in_the_loop = false;
1358                 unset( $this->request );
1359                 unset( $this->post );
1360                 unset( $this->comments );
1361                 unset( $this->comment );
1362                 $this->comment_count = 0;
1363                 $this->current_comment = -1;
1364                 $this->found_posts = 0;
1365                 $this->max_num_pages = 0;
1366                 $this->max_num_comment_pages = 0;
1367
1368                 $this->init_query_flags();
1369         }
1370
1371         /**
1372          * Reparse the query vars.
1373          *
1374          * @since 1.5.0
1375          * @access public
1376          */
1377         public function parse_query_vars() {
1378                 $this->parse_query();
1379         }
1380
1381         /**
1382          * Fills in the query variables, which do not exist within the parameter.
1383          *
1384          * @since 2.1.0
1385          * @access public
1386          *
1387          * @param array $array Defined query variables.
1388          * @return array Complete query variables with undefined ones filled in empty.
1389          */
1390         public function fill_query_vars($array) {
1391                 $keys = array(
1392                         'error'
1393                         , 'm'
1394                         , 'p'
1395                         , 'post_parent'
1396                         , 'subpost'
1397                         , 'subpost_id'
1398                         , 'attachment'
1399                         , 'attachment_id'
1400                         , 'name'
1401                         , 'static'
1402                         , 'pagename'
1403                         , 'page_id'
1404                         , 'second'
1405                         , 'minute'
1406                         , 'hour'
1407                         , 'day'
1408                         , 'monthnum'
1409                         , 'year'
1410                         , 'w'
1411                         , 'category_name'
1412                         , 'tag'
1413                         , 'cat'
1414                         , 'tag_id'
1415                         , 'author'
1416                         , 'author_name'
1417                         , 'feed'
1418                         , 'tb'
1419                         , 'paged'
1420                         , 'comments_popup'
1421                         , 'meta_key'
1422                         , 'meta_value'
1423                         , 'preview'
1424                         , 's'
1425                         , 'sentence'
1426                         , 'fields'
1427                         , 'menu_order'
1428                 );
1429
1430                 foreach ( $keys as $key ) {
1431                         if ( !isset($array[$key]) )
1432                                 $array[$key] = '';
1433                 }
1434
1435                 $array_keys = array( 'category__in', 'category__not_in', 'category__and', 'post__in', 'post__not_in',
1436                         'tag__in', 'tag__not_in', 'tag__and', 'tag_slug__in', 'tag_slug__and', 'post_parent__in', 'post_parent__not_in',
1437                         'author__in', 'author__not_in' );
1438
1439                 foreach ( $array_keys as $key ) {
1440                         if ( !isset($array[$key]) )
1441                                 $array[$key] = array();
1442                 }
1443                 return $array;
1444         }
1445
1446         /**
1447          * Parse a query string and set query type booleans.
1448          *
1449          * @since 1.5.0
1450          * @access public
1451          *
1452          * @param string|array $query {
1453          *     Optional. Array or string of Query parameters.
1454          *
1455          *     @type int          $attachment_id           Attachment post ID. Used for 'attachment' post_type.
1456          *     @type int|string   $author                  Author ID, or comma-separated list of IDs.
1457          *     @type string       $author_name             User 'user_nicename'.
1458          *     @type array        $author__in              An array of author IDs to query from.
1459          *     @type array        $author__not_in          An array of author IDs not to query from.
1460          *     @type bool         $cache_results           Whether to cache post information. Default true.
1461          *     @type int|string   $cat                     Category ID or comma-separated list of IDs (this or any children).
1462          *     @type array        $category__and           An array of category IDs (AND in).
1463          *     @type array        $category__in            An array of category IDs (OR in, no children).
1464          *     @type array        $category__not_in        An array of category IDs (NOT in).
1465          *     @type string       $category_name           Use category slug (not name, this or any children).
1466          *     @type int          $comments_per_page       The number of comments to return per page.
1467          *                                                 Default 'comments_per_page' option.
1468          *     @type int|string   $comments_popup          Whether the query is within the comments popup. Default empty.
1469          *     @type array        $date_query              An associative array of WP_Date_Query arguments.
1470          *                                                 {@see WP_Date_Query::__construct()}
1471          *     @type int          $day                     Day of the month. Default empty. Accepts numbers 1-31.
1472          *     @type bool         $exact                   Whether to search by exact keyword. Default false.
1473          *     @type string|array $fields                  Which fields to return. Single field or all fields (string),
1474          *                                                 or array of fields. 'id=>parent' uses 'id' and 'post_parent'.
1475          *                                                 Default all fields. Accepts 'ids', 'id=>parent'.
1476          *     @type int          $hour                    Hour of the day. Default empty. Accepts numbers 0-23.
1477          *     @type bool         $ignore_sticky_posts     Whether to ignore sticky posts or not. Setting this to false
1478          *                                                 excludes stickies from 'post__in'. Accepts 1|true, 0|false.
1479          *                                                 Default 0|false.
1480          *     @type int          $m                       Combination YearMonth. Accepts any four-digit year and month
1481          *                                                 numbers 1-12. Default empty.
1482          *     @type string       $meta_compare            Comparison operator to test the 'meta_value'.
1483          *     @type string       $meta_key                Custom field key.
1484          *     @type array        $meta_query              An associative array of WP_Meta_Query arguments.
1485          *                                                 {@see WP_Meta_Query->queries}
1486          *     @type string       $meta_value              Custom field value.
1487          *     @type int          $meta_value_num          Custom field value number.
1488          *     @type int          $menu_order              The menu order of the posts.
1489          *     @type int          $monthnum                The two-digit month. Default empty. Accepts numbers 1-12.
1490          *     @type string       $name                    Post slug.
1491          *     @type bool         $nopaging                Show all posts (true) or paginate (false). Default false.
1492          *     @type bool         $no_found_rows           Whether to skip counting the total rows found. Enabling can improve
1493          *                                                 performance. Default false.
1494          *     @type int          $offset                  The number of posts to offset before retrieval.
1495          *     @type string       $order                   Designates ascending or descending order of posts. Default 'DESC'.
1496          *                                                 Accepts 'ASC', 'DESC'.
1497          *     @type string       $orderby                 Sort retrieved posts by parameter. One or more options can be
1498          *                                                 passed. To use 'meta_value', or 'meta_value_num',
1499          *                                                 'meta_key=keyname' must be also be defined. Default 'date'.
1500          *                                                 Accepts 'none', 'name', 'author', 'date', 'title', 'modified',
1501          *                                                 'menu_order', 'parent', 'ID', 'rand', 'comment_count'.
1502          *     @type int          $p                       Post ID.
1503          *     @type int          $page                    Show the number of posts that would show up on page X of a
1504          *                                                 static front page.
1505          *     @type int          $paged                   The number of the current page.
1506          *     @type int          $page_id                 Page ID.
1507          *     @type string       $pagename                Page slug.
1508          *     @type string       $perm                    Show posts if user has the appropriate capability.
1509          *     @type array        $post__in                An array of post IDs to retrieve, sticky posts will be included
1510          *     @type string       $post_mime_type          The mime type of the post. Used for 'attachment' post_type.
1511          *     @type array        $post__not_in            An array of post IDs not to retrieve. Note: a string of comma-
1512          *                                                 separated IDs will NOT work.
1513          *     @type int          $post_parent             Page ID to retrieve child pages for. Use 0 to only retrieve
1514          *                                                 top-level pages.
1515          *     @type array        $post_parent__in         An array containing parent page IDs to query child pages from.
1516          *     @type array        $post_parent__not_in     An array containing parent page IDs not to query child pages from.
1517          *     @type string|array $post_type               A post type slug (string) or array of post type slugs.
1518          *                                                 Default 'any' if using 'tax_query'.
1519          *     @type string|array $post_status             A post status (string) or array of post statuses.
1520          *     @type int          $posts_per_page          The number of posts to query for. Use -1 to request all posts.
1521          *     @type int          $posts_per_archive_page  The number of posts to query for by archive page. Overrides
1522          *                                                 'posts_per_page' when is_archive(), or is_search() are true.
1523          *     @type string       $s                       Search keyword.
1524          *     @type int          $second                  Second of the minute. Default empty. Accepts numbers 0-60.
1525          *     @type array        $search_terms            Array of search terms.
1526          *     @type bool         $sentence                Whether to search by phrase. Default false.
1527          *     @type bool         $suppress_filters        Whether to suppress filters. Default false.
1528          *     @type string       $tag                     Tag slug. Comma-separated (either), Plus-separated (all).
1529          *     @type array        $tag__and                An array of tag ids (AND in).
1530          *     @type array        $tag__in                 An array of tag ids (OR in).
1531          *     @type array        $tag__not_in             An array of tag ids (NOT in).
1532          *     @type int          $tag_id                  Tag id or comma-separated list of IDs.
1533          *     @type array        $tag_slug__and           An array of tag slugs (AND in).
1534          *     @type array        $tag_slug__in            An array of tag slugs (OR in). unless 'ignore_sticky_posts' is
1535          *                                                 true. Note: a string of comma-separated IDs will NOT work.
1536          *     @type array        $tax_query               An associative array of WP_Tax_Query arguments.
1537          *                                                 {@see WP_Tax_Query->queries}
1538          *     @type bool         $update_post_meta_cache  Whether to update the post meta cache. Default true.
1539          *     @type bool         $update_post_term_cache  Whether to update the post term cache. Default true.
1540          *     @type int          $w                       The week number of the year. Default empty. Accepts numbers 0-53.
1541          *     @type int          $year                    The four-digit year. Default empty. Accepts any four-digit year.
1542          * }
1543          */
1544         public function parse_query( $query =  '' ) {
1545                 if ( ! empty( $query ) ) {
1546                         $this->init();
1547                         $this->query = $this->query_vars = wp_parse_args( $query );
1548                 } elseif ( ! isset( $this->query ) ) {
1549                         $this->query = $this->query_vars;
1550                 }
1551
1552                 $this->query_vars = $this->fill_query_vars($this->query_vars);
1553                 $qv = &$this->query_vars;
1554                 $this->query_vars_changed = true;
1555
1556                 if ( ! empty($qv['robots']) )
1557                         $this->is_robots = true;
1558
1559                 $qv['p'] =  absint($qv['p']);
1560                 $qv['page_id'] =  absint($qv['page_id']);
1561                 $qv['year'] = absint($qv['year']);
1562                 $qv['monthnum'] = absint($qv['monthnum']);
1563                 $qv['day'] = absint($qv['day']);
1564                 $qv['w'] = absint($qv['w']);
1565                 $qv['m'] = preg_replace( '|[^0-9]|', '', $qv['m'] );
1566                 $qv['paged'] = absint($qv['paged']);
1567                 $qv['cat'] = preg_replace( '|[^0-9,-]|', '', $qv['cat'] ); // comma separated list of positive or negative integers
1568                 $qv['author'] = preg_replace( '|[^0-9,-]|', '', $qv['author'] ); // comma separated list of positive or negative integers
1569                 $qv['pagename'] = trim( $qv['pagename'] );
1570                 $qv['name'] = trim( $qv['name'] );
1571                 if ( '' !== $qv['hour'] ) $qv['hour'] = absint($qv['hour']);
1572                 if ( '' !== $qv['minute'] ) $qv['minute'] = absint($qv['minute']);
1573                 if ( '' !== $qv['second'] ) $qv['second'] = absint($qv['second']);
1574                 if ( '' !== $qv['menu_order'] ) $qv['menu_order'] = absint($qv['menu_order']);
1575
1576                 // Fairly insane upper bound for search string lengths.
1577                 if ( ! empty( $qv['s'] ) && strlen( $qv['s'] ) > 1600 )
1578                         $qv['s'] = '';
1579
1580                 // Compat. Map subpost to attachment.
1581                 if ( '' != $qv['subpost'] )
1582                         $qv['attachment'] = $qv['subpost'];
1583                 if ( '' != $qv['subpost_id'] )
1584                         $qv['attachment_id'] = $qv['subpost_id'];
1585
1586                 $qv['attachment_id'] = absint($qv['attachment_id']);
1587
1588                 if ( ('' != $qv['attachment']) || !empty($qv['attachment_id']) ) {
1589                         $this->is_single = true;
1590                         $this->is_attachment = true;
1591                 } elseif ( '' != $qv['name'] ) {
1592                         $this->is_single = true;
1593                 } elseif ( $qv['p'] ) {
1594                         $this->is_single = true;
1595                 } elseif ( ('' !== $qv['hour']) && ('' !== $qv['minute']) &&('' !== $qv['second']) && ('' != $qv['year']) && ('' != $qv['monthnum']) && ('' != $qv['day']) ) {
1596                         // If year, month, day, hour, minute, and second are set, a single
1597                         // post is being queried.
1598                         $this->is_single = true;
1599                 } elseif ( '' != $qv['static'] || '' != $qv['pagename'] || !empty($qv['page_id']) ) {
1600                         $this->is_page = true;
1601                         $this->is_single = false;
1602                 } else {
1603                         // Look for archive queries. Dates, categories, authors, search, post type archives.
1604
1605                         if ( isset( $this->query['s'] ) ) {
1606                                 $this->is_search = true;
1607                         }
1608
1609                         if ( '' !== $qv['second'] ) {
1610                                 $this->is_time = true;
1611                                 $this->is_date = true;
1612                         }
1613
1614                         if ( '' !== $qv['minute'] ) {
1615                                 $this->is_time = true;
1616                                 $this->is_date = true;
1617                         }
1618
1619                         if ( '' !== $qv['hour'] ) {
1620                                 $this->is_time = true;
1621                                 $this->is_date = true;
1622                         }
1623
1624                         if ( $qv['day'] ) {
1625                                 if ( ! $this->is_date ) {
1626                                         $date = sprintf( '%04d-%02d-%02d', $qv['year'], $qv['monthnum'], $qv['day'] );
1627                                         if ( $qv['monthnum'] && $qv['year'] && ! wp_checkdate( $qv['monthnum'], $qv['day'], $qv['year'], $date ) ) {
1628                                                 $qv['error'] = '404';
1629                                         } else {
1630                                                 $this->is_day = true;
1631                                                 $this->is_date = true;
1632                                         }
1633                                 }
1634                         }
1635
1636                         if ( $qv['monthnum'] ) {
1637                                 if ( ! $this->is_date ) {
1638                                         if ( 12 < $qv['monthnum'] ) {
1639                                                 $qv['error'] = '404';
1640                                         } else {
1641                                                 $this->is_month = true;
1642                                                 $this->is_date = true;
1643                                         }
1644                                 }
1645                         }
1646
1647                         if ( $qv['year'] ) {
1648                                 if ( ! $this->is_date ) {
1649                                         $this->is_year = true;
1650                                         $this->is_date = true;
1651                                 }
1652                         }
1653
1654                         if ( $qv['m'] ) {
1655                                 $this->is_date = true;
1656                                 if ( strlen($qv['m']) > 9 ) {
1657                                         $this->is_time = true;
1658                                 } else if ( strlen($qv['m']) > 7 ) {
1659                                         $this->is_day = true;
1660                                 } else if ( strlen($qv['m']) > 5 ) {
1661                                         $this->is_month = true;
1662                                 } else {
1663                                         $this->is_year = true;
1664                                 }
1665                         }
1666
1667                         if ( '' != $qv['w'] ) {
1668                                 $this->is_date = true;
1669                         }
1670
1671                         $this->query_vars_hash = false;
1672                         $this->parse_tax_query( $qv );
1673
1674                         foreach ( $this->tax_query->queries as $tax_query ) {
1675                                 if ( 'NOT IN' != $tax_query['operator'] ) {
1676                                         switch ( $tax_query['taxonomy'] ) {
1677                                                 case 'category':
1678                                                         $this->is_category = true;
1679                                                         break;
1680                                                 case 'post_tag':
1681                                                         $this->is_tag = true;
1682                                                         break;
1683                                                 default:
1684                                                         $this->is_tax = true;
1685                                         }
1686                                 }
1687                         }
1688                         unset( $tax_query );
1689
1690                         if ( empty($qv['author']) || ($qv['author'] == '0') ) {
1691                                 $this->is_author = false;
1692                         } else {
1693                                 $this->is_author = true;
1694                         }
1695
1696                         if ( '' != $qv['author_name'] )
1697                                 $this->is_author = true;
1698
1699                         if ( !empty( $qv['post_type'] ) && ! is_array( $qv['post_type'] ) ) {
1700                                 $post_type_obj = get_post_type_object( $qv['post_type'] );
1701                                 if ( ! empty( $post_type_obj->has_archive ) )
1702                                         $this->is_post_type_archive = true;
1703                         }
1704
1705                         if ( $this->is_post_type_archive || $this->is_date || $this->is_author || $this->is_category || $this->is_tag || $this->is_tax )
1706                                 $this->is_archive = true;
1707                 }
1708
1709                 if ( '' != $qv['feed'] )
1710                         $this->is_feed = true;
1711
1712                 if ( '' != $qv['tb'] )
1713                         $this->is_trackback = true;
1714
1715                 if ( '' != $qv['paged'] && ( intval($qv['paged']) > 1 ) )
1716                         $this->is_paged = true;
1717
1718                 if ( '' != $qv['comments_popup'] )
1719                         $this->is_comments_popup = true;
1720
1721                 // if we're previewing inside the write screen
1722                 if ( '' != $qv['preview'] )
1723                         $this->is_preview = true;
1724
1725                 if ( is_admin() )
1726                         $this->is_admin = true;
1727
1728                 if ( false !== strpos($qv['feed'], 'comments-') ) {
1729                         $qv['feed'] = str_replace('comments-', '', $qv['feed']);
1730                         $qv['withcomments'] = 1;
1731                 }
1732
1733                 $this->is_singular = $this->is_single || $this->is_page || $this->is_attachment;
1734
1735                 if ( $this->is_feed && ( !empty($qv['withcomments']) || ( empty($qv['withoutcomments']) && $this->is_singular ) ) )
1736                         $this->is_comment_feed = true;
1737
1738                 if ( !( $this->is_singular || $this->is_archive || $this->is_search || $this->is_feed || $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_comments_popup || $this->is_robots ) )
1739                         $this->is_home = true;
1740
1741                 // Correct is_* for page_on_front and page_for_posts
1742                 if ( $this->is_home && 'page' == get_option('show_on_front') && get_option('page_on_front') ) {
1743                         $_query = wp_parse_args($this->query);
1744                         // pagename can be set and empty depending on matched rewrite rules. Ignore an empty pagename.
1745                         if ( isset($_query['pagename']) && '' == $_query['pagename'] )
1746                                 unset($_query['pagename']);
1747                         if ( empty($_query) || !array_diff( array_keys($_query), array('preview', 'page', 'paged', 'cpage') ) ) {
1748                                 $this->is_page = true;
1749                                 $this->is_home = false;
1750                                 $qv['page_id'] = get_option('page_on_front');
1751                                 // Correct <!--nextpage--> for page_on_front
1752                                 if ( !empty($qv['paged']) ) {
1753                                         $qv['page'] = $qv['paged'];
1754                                         unset($qv['paged']);
1755                                 }
1756                         }
1757                 }
1758
1759                 if ( '' != $qv['pagename'] ) {
1760                         $this->queried_object = get_page_by_path($qv['pagename']);
1761                         if ( !empty($this->queried_object) )
1762                                 $this->queried_object_id = (int) $this->queried_object->ID;
1763                         else
1764                                 unset($this->queried_object);
1765
1766                         if  ( 'page' == get_option('show_on_front') && isset($this->queried_object_id) && $this->queried_object_id == get_option('page_for_posts') ) {
1767                                 $this->is_page = false;
1768                                 $this->is_home = true;
1769                                 $this->is_posts_page = true;
1770                         }
1771                 }
1772
1773                 if ( $qv['page_id'] ) {
1774                         if  ( 'page' == get_option('show_on_front') && $qv['page_id'] == get_option('page_for_posts') ) {
1775                                 $this->is_page = false;
1776                                 $this->is_home = true;
1777                                 $this->is_posts_page = true;
1778                         }
1779                 }
1780
1781                 if ( !empty($qv['post_type']) ) {
1782                         if ( is_array($qv['post_type']) )
1783                                 $qv['post_type'] = array_map('sanitize_key', $qv['post_type']);
1784                         else
1785                                 $qv['post_type'] = sanitize_key($qv['post_type']);
1786                 }
1787
1788                 if ( ! empty( $qv['post_status'] ) ) {
1789                         if ( is_array( $qv['post_status'] ) )
1790                                 $qv['post_status'] = array_map('sanitize_key', $qv['post_status']);
1791                         else
1792                                 $qv['post_status'] = preg_replace('|[^a-z0-9_,-]|', '', $qv['post_status']);
1793                 }
1794
1795                 if ( $this->is_posts_page && ( ! isset($qv['withcomments']) || ! $qv['withcomments'] ) )
1796                         $this->is_comment_feed = false;
1797
1798                 $this->is_singular = $this->is_single || $this->is_page || $this->is_attachment;
1799                 // Done correcting is_* for page_on_front and page_for_posts
1800
1801                 if ( '404' == $qv['error'] )
1802                         $this->set_404();
1803
1804                 $this->query_vars_hash = md5( serialize( $this->query_vars ) );
1805                 $this->query_vars_changed = false;
1806
1807                 /**
1808                  * Fires after the main query vars have been parsed.
1809                  *
1810                  * @since 1.5.0
1811                  *
1812                  * @param WP_Query &$this The WP_Query instance (passed by reference).
1813                  */
1814                 do_action_ref_array( 'parse_query', array( &$this ) );
1815         }
1816
1817         /**
1818          * Parses various taxonomy related query vars.
1819          *
1820          * For BC, this method is not marked as protected. See [28987].
1821          *
1822          * @access protected
1823          * @since 3.1.0
1824          *
1825          * @param array &$q The query variables
1826          */
1827         function parse_tax_query( &$q ) {
1828                 if ( ! empty( $q['tax_query'] ) && is_array( $q['tax_query'] ) ) {
1829                         $tax_query = $q['tax_query'];
1830                 } else {
1831                         $tax_query = array();
1832                 }
1833
1834                 if ( !empty($q['taxonomy']) && !empty($q['term']) ) {
1835                         $tax_query[] = array(
1836                                 'taxonomy' => $q['taxonomy'],
1837                                 'terms' => array( $q['term'] ),
1838                                 'field' => 'slug',
1839                         );
1840                 }
1841
1842                 foreach ( get_taxonomies( array() , 'objects' ) as $taxonomy => $t ) {
1843                         if ( 'post_tag' == $taxonomy )
1844                                 continue;       // Handled further down in the $q['tag'] block
1845
1846                         if ( $t->query_var && !empty( $q[$t->query_var] ) ) {
1847                                 $tax_query_defaults = array(
1848                                         'taxonomy' => $taxonomy,
1849                                         'field' => 'slug',
1850                                 );
1851
1852                                 if ( isset( $t->rewrite['hierarchical'] ) && $t->rewrite['hierarchical'] ) {
1853                                         $q[$t->query_var] = wp_basename( $q[$t->query_var] );
1854                                 }
1855
1856                                 $term = $q[$t->query_var];
1857
1858                                 if ( strpos($term, '+') !== false ) {
1859                                         $terms = preg_split( '/[+]+/', $term );
1860                                         foreach ( $terms as $term ) {
1861                                                 $tax_query[] = array_merge( $tax_query_defaults, array(
1862                                                         'terms' => array( $term )
1863                                                 ) );
1864                                         }
1865                                 } else {
1866                                         $tax_query[] = array_merge( $tax_query_defaults, array(
1867                                                 'terms' => preg_split( '/[,]+/', $term )
1868                                         ) );
1869                                 }
1870                         }
1871                 }
1872
1873                 // Category stuff
1874                 if ( ! empty( $q['cat'] ) && ! $this->is_singular ) {
1875                         $cat_in = $cat_not_in = array();
1876
1877                         $cat_array = preg_split( '/[,\s]+/', urldecode( $q['cat'] ) );
1878                         $cat_array = array_map( 'intval', $cat_array );
1879                         $q['cat'] = implode( ',', $cat_array );
1880
1881                         foreach ( $cat_array as $cat ) {
1882                                 if ( $cat > 0 )
1883                                         $cat_in[] = $cat;
1884                                 elseif ( $cat < 0 )
1885                                         $cat_not_in[] = abs( $cat );
1886                         }
1887
1888                         if ( ! empty( $cat_in ) ) {
1889                                 $tax_query[] = array(
1890                                         'taxonomy' => 'category',
1891                                         'terms' => $cat_in,
1892                                         'field' => 'term_id',
1893                                         'include_children' => true
1894                                 );
1895                         }
1896
1897                         if ( ! empty( $cat_not_in ) ) {
1898                                 $tax_query[] = array(
1899                                         'taxonomy' => 'category',
1900                                         'terms' => $cat_not_in,
1901                                         'field' => 'term_id',
1902                                         'operator' => 'NOT IN',
1903                                         'include_children' => true
1904                                 );
1905                         }
1906                         unset( $cat_array, $cat_in, $cat_not_in );
1907                 }
1908
1909                 if ( ! empty( $q['category__and'] ) && 1 === count( (array) $q['category__and'] ) ) {
1910                         $q['category__and'] = (array) $q['category__and'];
1911                         if ( ! isset( $q['category__in'] ) )
1912                                 $q['category__in'] = array();
1913                         $q['category__in'][] = absint( reset( $q['category__and'] ) );
1914                         unset( $q['category__and'] );
1915                 }
1916
1917                 if ( ! empty( $q['category__in'] ) ) {
1918                         $q['category__in'] = array_map( 'absint', array_unique( (array) $q['category__in'] ) );
1919                         $tax_query[] = array(
1920                                 'taxonomy' => 'category',
1921                                 'terms' => $q['category__in'],
1922                                 'field' => 'term_id',
1923                                 'include_children' => false
1924                         );
1925                 }
1926
1927                 if ( ! empty($q['category__not_in']) ) {
1928                         $q['category__not_in'] = array_map( 'absint', array_unique( (array) $q['category__not_in'] ) );
1929                         $tax_query[] = array(
1930                                 'taxonomy' => 'category',
1931                                 'terms' => $q['category__not_in'],
1932                                 'operator' => 'NOT IN',
1933                                 'include_children' => false
1934                         );
1935                 }
1936
1937                 if ( ! empty($q['category__and']) ) {
1938                         $q['category__and'] = array_map( 'absint', array_unique( (array) $q['category__and'] ) );
1939                         $tax_query[] = array(
1940                                 'taxonomy' => 'category',
1941                                 'terms' => $q['category__and'],
1942                                 'field' => 'term_id',
1943                                 'operator' => 'AND',
1944                                 'include_children' => false
1945                         );
1946                 }
1947
1948                 // Tag stuff
1949                 if ( '' != $q['tag'] && !$this->is_singular && $this->query_vars_changed ) {
1950                         if ( strpos($q['tag'], ',') !== false ) {
1951                                 $tags = preg_split('/[,\r\n\t ]+/', $q['tag']);
1952                                 foreach ( (array) $tags as $tag ) {
1953                                         $tag = sanitize_term_field('slug', $tag, 0, 'post_tag', 'db');
1954                                         $q['tag_slug__in'][] = $tag;
1955                                 }
1956                         } else if ( preg_match('/[+\r\n\t ]+/', $q['tag']) || !empty($q['cat']) ) {
1957                                 $tags = preg_split('/[+\r\n\t ]+/', $q['tag']);
1958                                 foreach ( (array) $tags as $tag ) {
1959                                         $tag = sanitize_term_field('slug', $tag, 0, 'post_tag', 'db');
1960                                         $q['tag_slug__and'][] = $tag;
1961                                 }
1962                         } else {
1963                                 $q['tag'] = sanitize_term_field('slug', $q['tag'], 0, 'post_tag', 'db');
1964                                 $q['tag_slug__in'][] = $q['tag'];
1965                         }
1966                 }
1967
1968                 if ( !empty($q['tag_id']) ) {
1969                         $q['tag_id'] = absint( $q['tag_id'] );
1970                         $tax_query[] = array(
1971                                 'taxonomy' => 'post_tag',
1972                                 'terms' => $q['tag_id']
1973                         );
1974                 }
1975
1976                 if ( !empty($q['tag__in']) ) {
1977                         $q['tag__in'] = array_map('absint', array_unique( (array) $q['tag__in'] ) );
1978                         $tax_query[] = array(
1979                                 'taxonomy' => 'post_tag',
1980                                 'terms' => $q['tag__in']
1981                         );
1982                 }
1983
1984                 if ( !empty($q['tag__not_in']) ) {
1985                         $q['tag__not_in'] = array_map('absint', array_unique( (array) $q['tag__not_in'] ) );
1986                         $tax_query[] = array(
1987                                 'taxonomy' => 'post_tag',
1988                                 'terms' => $q['tag__not_in'],
1989                                 'operator' => 'NOT IN'
1990                         );
1991                 }
1992
1993                 if ( !empty($q['tag__and']) ) {
1994                         $q['tag__and'] = array_map('absint', array_unique( (array) $q['tag__and'] ) );
1995                         $tax_query[] = array(
1996                                 'taxonomy' => 'post_tag',
1997                                 'terms' => $q['tag__and'],
1998                                 'operator' => 'AND'
1999                         );
2000                 }
2001
2002                 if ( !empty($q['tag_slug__in']) ) {
2003                         $q['tag_slug__in'] = array_map('sanitize_title_for_query', array_unique( (array) $q['tag_slug__in'] ) );
2004                         $tax_query[] = array(
2005                                 'taxonomy' => 'post_tag',
2006                                 'terms' => $q['tag_slug__in'],
2007                                 'field' => 'slug'
2008                         );
2009                 }
2010
2011                 if ( !empty($q['tag_slug__and']) ) {
2012                         $q['tag_slug__and'] = array_map('sanitize_title_for_query', array_unique( (array) $q['tag_slug__and'] ) );
2013                         $tax_query[] = array(
2014                                 'taxonomy' => 'post_tag',
2015                                 'terms' => $q['tag_slug__and'],
2016                                 'field' => 'slug',
2017                                 'operator' => 'AND'
2018                         );
2019                 }
2020
2021                 $this->tax_query = new WP_Tax_Query( $tax_query );
2022
2023                 /**
2024                  * Fires after taxonomy-related query vars have been parsed.
2025                  *
2026                  * @since 3.7.0
2027                  *
2028                  * @param WP_Query $this The WP_Query instance.
2029                  */
2030                 do_action( 'parse_tax_query', $this );
2031         }
2032
2033         /**
2034          * Generate SQL for the WHERE clause based on passed search terms.
2035          *
2036          * @since 3.7.0
2037          *
2038          * @global wpdb $wpdb
2039          * @param array $q Query variables.
2040          * @return string WHERE clause.
2041          */
2042         protected function parse_search( &$q ) {
2043                 global $wpdb;
2044
2045                 $search = '';
2046
2047                 // added slashes screw with quote grouping when done early, so done later
2048                 $q['s'] = stripslashes( $q['s'] );
2049                 if ( empty( $_GET['s'] ) && $this->is_main_query() )
2050                         $q['s'] = urldecode( $q['s'] );
2051                 // there are no line breaks in <input /> fields
2052                 $q['s'] = str_replace( array( "\r", "\n" ), '', $q['s'] );
2053                 $q['search_terms_count'] = 1;
2054                 if ( ! empty( $q['sentence'] ) ) {
2055                         $q['search_terms'] = array( $q['s'] );
2056                 } else {
2057                         if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $q['s'], $matches ) ) {
2058                                 $q['search_terms_count'] = count( $matches[0] );
2059                                 $q['search_terms'] = $this->parse_search_terms( $matches[0] );
2060                                 // if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence
2061                                 if ( empty( $q['search_terms'] ) || count( $q['search_terms'] ) > 9 )
2062                                         $q['search_terms'] = array( $q['s'] );
2063                         } else {
2064                                 $q['search_terms'] = array( $q['s'] );
2065                         }
2066                 }
2067
2068                 $n = ! empty( $q['exact'] ) ? '' : '%';
2069                 $searchand = '';
2070                 $q['search_orderby_title'] = array();
2071                 foreach ( $q['search_terms'] as $term ) {
2072                         if ( $n ) {
2073                                 $like = '%' . $wpdb->esc_like( $term ) . '%';
2074                                 $q['search_orderby_title'][] = $wpdb->prepare( "$wpdb->posts.post_title LIKE %s", $like );
2075                         }
2076
2077                         $like = $n . $wpdb->esc_like( $term ) . $n;
2078                         $search .= $wpdb->prepare( "{$searchand}(($wpdb->posts.post_title LIKE %s) OR ($wpdb->posts.post_content LIKE %s))", $like, $like );
2079                         $searchand = ' AND ';
2080                 }
2081
2082                 if ( ! empty( $search ) ) {
2083                         $search = " AND ({$search}) ";
2084                         if ( ! is_user_logged_in() )
2085                                 $search .= " AND ($wpdb->posts.post_password = '') ";
2086                 }
2087
2088                 return $search;
2089         }
2090
2091         /**
2092          * Check if the terms are suitable for searching.
2093          *
2094          * Uses an array of stopwords (terms) that are excluded from the separate
2095          * term matching when searching for posts. The list of English stopwords is
2096          * the approximate search engines list, and is translatable.
2097          *
2098          * @since 3.7.0
2099          *
2100          * @param array Terms to check.
2101          * @return array Terms that are not stopwords.
2102          */
2103         protected function parse_search_terms( $terms ) {
2104                 $strtolower = function_exists( 'mb_strtolower' ) ? 'mb_strtolower' : 'strtolower';
2105                 $checked = array();
2106
2107                 $stopwords = $this->get_search_stopwords();
2108
2109                 foreach ( $terms as $term ) {
2110                         // keep before/after spaces when term is for exact match
2111                         if ( preg_match( '/^".+"$/', $term ) )
2112                                 $term = trim( $term, "\"'" );
2113                         else
2114                                 $term = trim( $term, "\"' " );
2115
2116                         // Avoid single A-Z.
2117                         if ( ! $term || ( 1 === strlen( $term ) && preg_match( '/^[a-z]$/i', $term ) ) )
2118                                 continue;
2119
2120                         if ( in_array( call_user_func( $strtolower, $term ), $stopwords, true ) )
2121                                 continue;
2122
2123                         $checked[] = $term;
2124                 }
2125
2126                 return $checked;
2127         }
2128
2129         /**
2130          * Retrieve stopwords used when parsing search terms.
2131          *
2132          * @since 3.7.0
2133          *
2134          * @return array Stopwords.
2135          */
2136         protected function get_search_stopwords() {
2137                 if ( isset( $this->stopwords ) )
2138                         return $this->stopwords;
2139
2140                 /* translators: This is a comma-separated list of very common words that should be excluded from a search,
2141                  * like a, an, and the. These are usually called "stopwords". You should not simply translate these individual
2142                  * words into your language. Instead, look for and provide commonly accepted stopwords in your language.
2143                  */
2144                 $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',
2145                         'Comma-separated list of search stopwords in your language' ) );
2146
2147                 $stopwords = array();
2148                 foreach( $words as $word ) {
2149                         $word = trim( $word, "\r\n\t " );
2150                         if ( $word )
2151                                 $stopwords[] = $word;
2152                 }
2153
2154                 /**
2155                  * Filter stopwords used when parsing search terms.
2156                  *
2157                  * @since 3.7.0
2158                  *
2159                  * @param array $stopwords Stopwords.
2160                  */
2161                 $this->stopwords = apply_filters( 'wp_search_stopwords', $stopwords );
2162                 return $this->stopwords;
2163         }
2164
2165         /**
2166          * Generate SQL for the ORDER BY condition based on passed search terms.
2167          *
2168          * @global wpdb $wpdb
2169          * @param array $q Query variables.
2170          * @return string ORDER BY clause.
2171          */
2172         protected function parse_search_order( &$q ) {
2173                 global $wpdb;
2174
2175                 if ( $q['search_terms_count'] > 1 ) {
2176                         $num_terms = count( $q['search_orderby_title'] );
2177                         $like = '%' . $wpdb->esc_like( $q['s'] ) . '%';
2178
2179                         $search_orderby = '(CASE ';
2180                         // sentence match in 'post_title'
2181                         $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_title LIKE %s THEN 1 ", $like );
2182
2183                         // sanity limit, sort as sentence when more than 6 terms
2184                         // (few searches are longer than 6 terms and most titles are not)
2185                         if ( $num_terms < 7 ) {
2186                                 // all words in title
2187                                 $search_orderby .= 'WHEN ' . implode( ' AND ', $q['search_orderby_title'] ) . ' THEN 2 ';
2188                                 // any word in title, not needed when $num_terms == 1
2189                                 if ( $num_terms > 1 )
2190                                         $search_orderby .= 'WHEN ' . implode( ' OR ', $q['search_orderby_title'] ) . ' THEN 3 ';
2191                         }
2192
2193                         // sentence match in 'post_content'
2194                         $search_orderby .= $wpdb->prepare( "WHEN $wpdb->posts.post_content LIKE %s THEN 4 ", $like );
2195                         $search_orderby .= 'ELSE 5 END)';
2196                 } else {
2197                         // single word or sentence search
2198                         $search_orderby = reset( $q['search_orderby_title'] ) . ' DESC';
2199                 }
2200
2201                 return $search_orderby;
2202         }
2203
2204         /**
2205          * If the passed orderby value is allowed, convert the alias to a
2206          * properly-prefixed orderby value.
2207          *
2208          * @since 4.0.0
2209          * @access protected
2210          *
2211          * @global wpdb $wpdb WordPress database access abstraction object.
2212          *
2213          * @param string $orderby Alias for the field to order by.
2214          * @return string|bool Table-prefixed value to used in the ORDER clause. False otherwise.
2215          */
2216         protected function parse_orderby( $orderby ) {
2217                 global $wpdb;
2218
2219                 // Used to filter values.
2220                 $allowed_keys = array(
2221                         'post_name', 'post_author', 'post_date', 'post_title', 'post_modified',
2222                         'post_parent', 'post_type', 'name', 'author', 'date', 'title', 'modified',
2223                         'parent', 'type', 'ID', 'menu_order', 'comment_count', 'rand',
2224                 );
2225
2226                 $meta_key = $this->get( 'meta_key' );
2227                 if ( ! empty( $meta_key ) ) {
2228                         $allowed_keys[] = $meta_key;
2229                         $allowed_keys[] = 'meta_value';
2230                         $allowed_keys[] = 'meta_value_num';
2231                 }
2232
2233                 if ( ! in_array( $orderby, $allowed_keys ) ) {
2234                         return false;
2235                 }
2236
2237                 switch ( $orderby ) {
2238                         case 'post_name':
2239                         case 'post_author':
2240                         case 'post_date':
2241                         case 'post_title':
2242                         case 'post_modified':
2243                         case 'post_parent':
2244                         case 'post_type':
2245                         case 'ID':
2246                         case 'menu_order':
2247                         case 'comment_count':
2248                                 $orderby = "$wpdb->posts.{$orderby}";
2249                                 break;
2250                         case 'rand':
2251                                 $orderby = 'RAND()';
2252                                 break;
2253                         case $meta_key:
2254                         case 'meta_value':
2255                                 $type = $this->get( 'meta_type' );
2256                                 if ( ! empty( $type ) ) {
2257                                         $meta_type = $this->meta_query->get_cast_for_type( $type );
2258                                         $orderby = "CAST($wpdb->postmeta.meta_value AS {$meta_type})";
2259                                 } else {
2260                                         $orderby = "$wpdb->postmeta.meta_value";
2261                                 }
2262                                 break;
2263                         case 'meta_value_num':
2264                                 $orderby = "$wpdb->postmeta.meta_value+0";
2265                                 break;
2266                         default:
2267                                 $orderby = "$wpdb->posts.post_" . $orderby;
2268                                 break;
2269                 }
2270
2271                 return $orderby;
2272         }
2273
2274         /**
2275          * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
2276          *
2277          * @since 4.0.0
2278          * @access protected
2279          *
2280          * @param string $order The 'order' query variable.
2281          * @return string The sanitized 'order' query variable.
2282          */
2283         protected function parse_order( $order ) {
2284                 if ( ! is_string( $order ) || empty( $order ) ) {
2285                         return 'DESC';
2286                 }
2287
2288                 if ( 'ASC' === strtoupper( $order ) ) {
2289                         return 'ASC';
2290                 } else {
2291                         return 'DESC';
2292                 }
2293         }
2294
2295         /**
2296          * Sets the 404 property and saves whether query is feed.
2297          *
2298          * @since 2.0.0
2299          * @access public
2300          */
2301         public function set_404() {
2302                 $is_feed = $this->is_feed;
2303
2304                 $this->init_query_flags();
2305                 $this->is_404 = true;
2306
2307                 $this->is_feed = $is_feed;
2308         }
2309
2310         /**
2311          * Retrieve query variable.
2312          *
2313          * @since 1.5.0
2314          * @access public
2315          *
2316          * @param string $query_var Query variable key.
2317          * @param mixed  $default   Value to return if the query variable is not set. Default ''.
2318          * @return mixed
2319          */
2320         public function get( $query_var, $default = '' ) {
2321                 if ( isset( $this->query_vars[ $query_var ] ) ) {
2322                         return $this->query_vars[ $query_var ];
2323                 }
2324
2325                 return $default;
2326         }
2327
2328         /**
2329          * Set query variable.
2330          *
2331          * @since 1.5.0
2332          * @access public
2333          *
2334          * @param string $query_var Query variable key.
2335          * @param mixed $value Query variable value.
2336          */
2337         public function set($query_var, $value) {
2338                 $this->query_vars[$query_var] = $value;
2339         }
2340
2341         /**
2342          * Retrieve the posts based on query variables.
2343          *
2344          * There are a few filters and actions that can be used to modify the post
2345          * database query.
2346          *
2347          * @since 1.5.0
2348          * @access public
2349          * @uses do_action_ref_array() Calls 'pre_get_posts' hook before retrieving posts.
2350          *
2351          * @return array List of posts.
2352          */
2353         public function get_posts() {
2354                 global $wpdb;
2355
2356                 $this->parse_query();
2357
2358                 /**
2359                  * Fires after the query variable object is created, but before the actual query is run.
2360                  *
2361                  * Note: If using conditional tags, use the method versions within the passed instance
2362                  * (e.g. $this->is_main_query() instead of is_main_query()). This is because the functions
2363                  * like is_main_query() test against the global $wp_query instance, not the passed one.
2364                  *
2365                  * @since 2.0.0
2366                  *
2367                  * @param WP_Query &$this The WP_Query instance (passed by reference).
2368                  */
2369                 do_action_ref_array( 'pre_get_posts', array( &$this ) );
2370
2371                 // Shorthand.
2372                 $q = &$this->query_vars;
2373
2374                 // Fill again in case pre_get_posts unset some vars.
2375                 $q = $this->fill_query_vars($q);
2376
2377                 // Parse meta query
2378                 $this->meta_query = new WP_Meta_Query();
2379                 $this->meta_query->parse_query_vars( $q );
2380
2381                 // Set a flag if a pre_get_posts hook changed the query vars.
2382                 $hash = md5( serialize( $this->query_vars ) );
2383                 if ( $hash != $this->query_vars_hash ) {
2384                         $this->query_vars_changed = true;
2385                         $this->query_vars_hash = $hash;
2386                 }
2387                 unset($hash);
2388
2389                 // First let's clear some variables
2390                 $distinct = '';
2391                 $whichauthor = '';
2392                 $whichmimetype = '';
2393                 $where = '';
2394                 $limits = '';
2395                 $join = '';
2396                 $search = '';
2397                 $groupby = '';
2398                 $post_status_join = false;
2399                 $page = 1;
2400
2401                 if ( isset( $q['caller_get_posts'] ) ) {
2402                         _deprecated_argument( 'WP_Query', '3.1', __( '"caller_get_posts" is deprecated. Use "ignore_sticky_posts" instead.' ) );
2403                         if ( !isset( $q['ignore_sticky_posts'] ) )
2404                                 $q['ignore_sticky_posts'] = $q['caller_get_posts'];
2405                 }
2406
2407                 if ( !isset( $q['ignore_sticky_posts'] ) )
2408                         $q['ignore_sticky_posts'] = false;
2409
2410                 if ( !isset($q['suppress_filters']) )
2411                         $q['suppress_filters'] = false;
2412
2413                 if ( !isset($q['cache_results']) ) {
2414                         if ( wp_using_ext_object_cache() )
2415                                 $q['cache_results'] = false;
2416                         else
2417                                 $q['cache_results'] = true;
2418                 }
2419
2420                 if ( !isset($q['update_post_term_cache']) )
2421                         $q['update_post_term_cache'] = true;
2422
2423                 if ( !isset($q['update_post_meta_cache']) )
2424                         $q['update_post_meta_cache'] = true;
2425
2426                 if ( !isset($q['post_type']) ) {
2427                         if ( $this->is_search )
2428                                 $q['post_type'] = 'any';
2429                         else
2430                                 $q['post_type'] = '';
2431                 }
2432                 $post_type = $q['post_type'];
2433                 if ( empty( $q['posts_per_page'] ) ) {
2434                         $q['posts_per_page'] = get_option( 'posts_per_page' );
2435                 }
2436                 if ( isset($q['showposts']) && $q['showposts'] ) {
2437                         $q['showposts'] = (int) $q['showposts'];
2438                         $q['posts_per_page'] = $q['showposts'];
2439                 }
2440                 if ( (isset($q['posts_per_archive_page']) && $q['posts_per_archive_page'] != 0) && ($this->is_archive || $this->is_search) )
2441                         $q['posts_per_page'] = $q['posts_per_archive_page'];
2442                 if ( !isset($q['nopaging']) ) {
2443                         if ( $q['posts_per_page'] == -1 ) {
2444                                 $q['nopaging'] = true;
2445                         } else {
2446                                 $q['nopaging'] = false;
2447                         }
2448                 }
2449
2450                 if ( $this->is_feed ) {
2451                         // This overrides posts_per_page.
2452                         if ( ! empty( $q['posts_per_rss'] ) ) {
2453                                 $q['posts_per_page'] = $q['posts_per_rss'];
2454                         } else {
2455                                 $q['posts_per_page'] = get_option( 'posts_per_rss' );
2456                         }
2457                         $q['nopaging'] = false;
2458                 }
2459                 $q['posts_per_page'] = (int) $q['posts_per_page'];
2460                 if ( $q['posts_per_page'] < -1 )
2461                         $q['posts_per_page'] = abs($q['posts_per_page']);
2462                 else if ( $q['posts_per_page'] == 0 )
2463                         $q['posts_per_page'] = 1;
2464
2465                 if ( !isset($q['comments_per_page']) || $q['comments_per_page'] == 0 )
2466                         $q['comments_per_page'] = get_option('comments_per_page');
2467
2468                 if ( $this->is_home && (empty($this->query) || $q['preview'] == 'true') && ( 'page' == get_option('show_on_front') ) && get_option('page_on_front') ) {
2469                         $this->is_page = true;
2470                         $this->is_home = false;
2471                         $q['page_id'] = get_option('page_on_front');
2472                 }
2473
2474                 if ( isset($q['page']) ) {
2475                         $q['page'] = trim($q['page'], '/');
2476                         $q['page'] = absint($q['page']);
2477                 }
2478
2479                 // If true, forcibly turns off SQL_CALC_FOUND_ROWS even when limits are present.
2480                 if ( isset($q['no_found_rows']) )
2481                         $q['no_found_rows'] = (bool) $q['no_found_rows'];
2482                 else
2483                         $q['no_found_rows'] = false;
2484
2485                 switch ( $q['fields'] ) {
2486                         case 'ids':
2487                                 $fields = "$wpdb->posts.ID";
2488                                 break;
2489                         case 'id=>parent':
2490                                 $fields = "$wpdb->posts.ID, $wpdb->posts.post_parent";
2491                                 break;
2492                         default:
2493                                 $fields = "$wpdb->posts.*";
2494                 }
2495
2496                 if ( '' !== $q['menu_order'] )
2497                         $where .= " AND $wpdb->posts.menu_order = " . $q['menu_order'];
2498
2499                 // The "m" parameter is meant for months but accepts datetimes of varying specificity
2500                 if ( $q['m'] ) {
2501                         $where .= " AND YEAR($wpdb->posts.post_date)=" . substr($q['m'], 0, 4);
2502                         if ( strlen($q['m']) > 5 )
2503                                 $where .= " AND MONTH($wpdb->posts.post_date)=" . substr($q['m'], 4, 2);
2504                         if ( strlen($q['m']) > 7 )
2505                                 $where .= " AND DAYOFMONTH($wpdb->posts.post_date)=" . substr($q['m'], 6, 2);
2506                         if ( strlen($q['m']) > 9 )
2507                                 $where .= " AND HOUR($wpdb->posts.post_date)=" . substr($q['m'], 8, 2);
2508                         if ( strlen($q['m']) > 11 )
2509                                 $where .= " AND MINUTE($wpdb->posts.post_date)=" . substr($q['m'], 10, 2);
2510                         if ( strlen($q['m']) > 13 )
2511                                 $where .= " AND SECOND($wpdb->posts.post_date)=" . substr($q['m'], 12, 2);
2512                 }
2513
2514                 // Handle the other individual date parameters
2515                 $date_parameters = array();
2516
2517                 if ( '' !== $q['hour'] )
2518                         $date_parameters['hour'] = $q['hour'];
2519
2520                 if ( '' !== $q['minute'] )
2521                         $date_parameters['minute'] = $q['minute'];
2522
2523                 if ( '' !== $q['second'] )
2524                         $date_parameters['second'] = $q['second'];
2525
2526                 if ( $q['year'] )
2527                         $date_parameters['year'] = $q['year'];
2528
2529                 if ( $q['monthnum'] )
2530                         $date_parameters['monthnum'] = $q['monthnum'];
2531
2532                 if ( $q['w'] )
2533                         $date_parameters['week'] = $q['w'];
2534
2535                 if ( $q['day'] )
2536                         $date_parameters['day'] = $q['day'];
2537
2538                 if ( $date_parameters ) {
2539                         $date_query = new WP_Date_Query( array( $date_parameters ) );
2540                         $where .= $date_query->get_sql();
2541                 }
2542                 unset( $date_parameters, $date_query );
2543
2544                 // Handle complex date queries
2545                 if ( ! empty( $q['date_query'] ) ) {
2546                         $this->date_query = new WP_Date_Query( $q['date_query'] );
2547                         $where .= $this->date_query->get_sql();
2548                 }
2549
2550
2551                 // If we've got a post_type AND it's not "any" post_type.
2552                 if ( !empty($q['post_type']) && 'any' != $q['post_type'] ) {
2553                         foreach ( (array)$q['post_type'] as $_post_type ) {
2554                                 $ptype_obj = get_post_type_object($_post_type);
2555                                 if ( !$ptype_obj || !$ptype_obj->query_var || empty($q[ $ptype_obj->query_var ]) )
2556                                         continue;
2557
2558                                 if ( ! $ptype_obj->hierarchical ) {
2559                                         // Non-hierarchical post types can directly use 'name'.
2560                                         $q['name'] = $q[ $ptype_obj->query_var ];
2561                                 } else {
2562                                         // Hierarchical post types will operate through 'pagename'.
2563                                         $q['pagename'] = $q[ $ptype_obj->query_var ];
2564                                         $q['name'] = '';
2565                                 }
2566
2567                                 // Only one request for a slug is possible, this is why name & pagename are overwritten above.
2568                                 break;
2569                         } //end foreach
2570                         unset($ptype_obj);
2571                 }
2572
2573                 if ( '' != $q['name'] ) {
2574                         $q['name'] = sanitize_title_for_query( $q['name'] );
2575                         $where .= " AND $wpdb->posts.post_name = '" . $q['name'] . "'";
2576                 } elseif ( '' != $q['pagename'] ) {
2577                         if ( isset($this->queried_object_id) ) {
2578                                 $reqpage = $this->queried_object_id;
2579                         } else {
2580                                 if ( 'page' != $q['post_type'] ) {
2581                                         foreach ( (array)$q['post_type'] as $_post_type ) {
2582                                                 $ptype_obj = get_post_type_object($_post_type);
2583                                                 if ( !$ptype_obj || !$ptype_obj->hierarchical )
2584                                                         continue;
2585
2586                                                 $reqpage = get_page_by_path($q['pagename'], OBJECT, $_post_type);
2587                                                 if ( $reqpage )
2588                                                         break;
2589                                         }
2590                                         unset($ptype_obj);
2591                                 } else {
2592                                         $reqpage = get_page_by_path($q['pagename']);
2593                                 }
2594                                 if ( !empty($reqpage) )
2595                                         $reqpage = $reqpage->ID;
2596                                 else
2597                                         $reqpage = 0;
2598                         }
2599
2600                         $page_for_posts = get_option('page_for_posts');
2601                         if  ( ('page' != get_option('show_on_front') ) || empty($page_for_posts) || ( $reqpage != $page_for_posts ) ) {
2602                                 $q['pagename'] = sanitize_title_for_query( wp_basename( $q['pagename'] ) );
2603                                 $q['name'] = $q['pagename'];
2604                                 $where .= " AND ($wpdb->posts.ID = '$reqpage')";
2605                                 $reqpage_obj = get_post( $reqpage );
2606                                 if ( is_object($reqpage_obj) && 'attachment' == $reqpage_obj->post_type ) {
2607                                         $this->is_attachment = true;
2608                                         $post_type = $q['post_type'] = 'attachment';
2609                                         $this->is_page = true;
2610                                         $q['attachment_id'] = $reqpage;
2611                                 }
2612                         }
2613                 } elseif ( '' != $q['attachment'] ) {
2614                         $q['attachment'] = sanitize_title_for_query( wp_basename( $q['attachment'] ) );
2615                         $q['name'] = $q['attachment'];
2616                         $where .= " AND $wpdb->posts.post_name = '" . $q['attachment'] . "'";
2617                 }
2618
2619
2620                 if ( intval($q['comments_popup']) )
2621                         $q['p'] = absint($q['comments_popup']);
2622
2623                 // If an attachment is requested by number, let it supersede any post number.
2624                 if ( $q['attachment_id'] )
2625                         $q['p'] = absint($q['attachment_id']);
2626
2627                 // If a post number is specified, load that post
2628                 if ( $q['p'] ) {
2629                         $where .= " AND {$wpdb->posts}.ID = " . $q['p'];
2630                 } elseif ( $q['post__in'] ) {
2631                         $post__in = implode(',', array_map( 'absint', $q['post__in'] ));
2632                         $where .= " AND {$wpdb->posts}.ID IN ($post__in)";
2633                 } elseif ( $q['post__not_in'] ) {
2634                         $post__not_in = implode(',',  array_map( 'absint', $q['post__not_in'] ));
2635                         $where .= " AND {$wpdb->posts}.ID NOT IN ($post__not_in)";
2636                 }
2637
2638                 if ( is_numeric( $q['post_parent'] ) ) {
2639                         $where .= $wpdb->prepare( " AND $wpdb->posts.post_parent = %d ", $q['post_parent'] );
2640                 } elseif ( $q['post_parent__in'] ) {
2641                         $post_parent__in = implode( ',', array_map( 'absint', $q['post_parent__in'] ) );
2642                         $where .= " AND {$wpdb->posts}.post_parent IN ($post_parent__in)";
2643                 } elseif ( $q['post_parent__not_in'] ) {
2644                         $post_parent__not_in = implode( ',',  array_map( 'absint', $q['post_parent__not_in'] ) );
2645                         $where .= " AND {$wpdb->posts}.post_parent NOT IN ($post_parent__not_in)";
2646                 }
2647
2648                 if ( $q['page_id'] ) {
2649                         if  ( ('page' != get_option('show_on_front') ) || ( $q['page_id'] != get_option('page_for_posts') ) ) {
2650                                 $q['p'] = $q['page_id'];
2651                                 $where = " AND {$wpdb->posts}.ID = " . $q['page_id'];
2652                         }
2653                 }
2654
2655                 // If a search pattern is specified, load the posts that match.
2656                 if ( ! empty( $q['s'] ) ) {
2657                         $search = $this->parse_search( $q );
2658                 }
2659
2660                 /**
2661                  * Filter the search SQL that is used in the WHERE clause of WP_Query.
2662                  *
2663                  * @since 3.0.0
2664                  *
2665                  * @param string   $search Search SQL for WHERE clause.
2666                  * @param WP_Query $this   The current WP_Query object.
2667                  */
2668                 $search = apply_filters_ref_array( 'posts_search', array( $search, &$this ) );
2669
2670                 // Taxonomies
2671                 if ( !$this->is_singular ) {
2672                         $this->parse_tax_query( $q );
2673
2674                         $clauses = $this->tax_query->get_sql( $wpdb->posts, 'ID' );
2675
2676                         $join .= $clauses['join'];
2677                         $where .= $clauses['where'];
2678                 }
2679
2680                 if ( $this->is_tax ) {
2681                         if ( empty($post_type) ) {
2682                                 // Do a fully inclusive search for currently registered post types of queried taxonomies
2683                                 $post_type = array();
2684                                 $taxonomies = wp_list_pluck( $this->tax_query->queries, 'taxonomy' );
2685                                 foreach ( get_post_types( array( 'exclude_from_search' => false ) ) as $pt ) {
2686                                         $object_taxonomies = $pt === 'attachment' ? get_taxonomies_for_attachments() : get_object_taxonomies( $pt );
2687                                         if ( array_intersect( $taxonomies, $object_taxonomies ) )
2688                                                 $post_type[] = $pt;
2689                                 }
2690                                 if ( ! $post_type )
2691                                         $post_type = 'any';
2692                                 elseif ( count( $post_type ) == 1 )
2693                                         $post_type = $post_type[0];
2694
2695                                 $post_status_join = true;
2696                         } elseif ( in_array('attachment', (array) $post_type) ) {
2697                                 $post_status_join = true;
2698                         }
2699                 }
2700
2701                 // Back-compat
2702                 if ( !empty($this->tax_query->queries) ) {
2703                         $tax_query_in_and = wp_list_filter( $this->tax_query->queries, array( 'operator' => 'NOT IN' ), 'NOT' );
2704                         if ( !empty( $tax_query_in_and ) ) {
2705                                 if ( !isset( $q['taxonomy'] ) ) {
2706                                         foreach ( $tax_query_in_and as $a_tax_query ) {
2707                                                 if ( !in_array( $a_tax_query['taxonomy'], array( 'category', 'post_tag' ) ) ) {
2708                                                         $q['taxonomy'] = $a_tax_query['taxonomy'];
2709                                                         if ( 'slug' == $a_tax_query['field'] )
2710                                                                 $q['term'] = $a_tax_query['terms'][0];
2711                                                         else
2712                                                                 $q['term_id'] = $a_tax_query['terms'][0];
2713
2714                                                         break;
2715                                                 }
2716                                         }
2717                                 }
2718
2719                                 $cat_query = wp_list_filter( $tax_query_in_and, array( 'taxonomy' => 'category' ) );
2720                                 if ( ! empty( $cat_query ) ) {
2721                                         $cat_query = reset( $cat_query );
2722
2723                                         if ( ! empty( $cat_query['terms'][0] ) ) {
2724                                                 $the_cat = get_term_by( $cat_query['field'], $cat_query['terms'][0], 'category' );
2725                                                 if ( $the_cat ) {
2726                                                         $this->set( 'cat', $the_cat->term_id );
2727                                                         $this->set( 'category_name', $the_cat->slug );
2728                                                 }
2729                                                 unset( $the_cat );
2730                                         }
2731                                 }
2732                                 unset( $cat_query );
2733
2734                                 $tag_query = wp_list_filter( $tax_query_in_and, array( 'taxonomy' => 'post_tag' ) );
2735                                 if ( ! empty( $tag_query ) ) {
2736                                         $tag_query = reset( $tag_query );
2737
2738                                         if ( ! empty( $tag_query['terms'][0] ) ) {
2739                                                 $the_tag = get_term_by( $tag_query['field'], $tag_query['terms'][0], 'post_tag' );
2740                                                 if ( $the_tag )
2741                                                         $this->set( 'tag_id', $the_tag->term_id );
2742                                                 unset( $the_tag );
2743                                         }
2744                                 }
2745                                 unset( $tag_query );
2746                         }
2747                 }
2748
2749                 if ( !empty( $this->tax_query->queries ) || !empty( $this->meta_query->queries ) ) {
2750                         $groupby = "{$wpdb->posts}.ID";
2751                 }
2752
2753                 // Author/user stuff
2754
2755                 if ( ! empty( $q['author'] ) && $q['author'] != '0' ) {
2756                         $q['author'] = addslashes_gpc( '' . urldecode( $q['author'] ) );
2757                         $authors = array_unique( array_map( 'intval', preg_split( '/[,\s]+/', $q['author'] ) ) );
2758                         foreach ( $authors as $author ) {
2759                                 $key = $author > 0 ? 'author__in' : 'author__not_in';
2760                                 $q[$key][] = abs( $author );
2761                         }
2762                         $q['author'] = implode( ',', $authors );
2763                 }
2764
2765                 if ( ! empty( $q['author__not_in'] ) ) {
2766                         $author__not_in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__not_in'] ) ) );
2767                         $where .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";
2768                 } elseif ( ! empty( $q['author__in'] ) ) {
2769                         $author__in = implode( ',', array_map( 'absint', array_unique( (array) $q['author__in'] ) ) );
2770                         $where .= " AND {$wpdb->posts}.post_author IN ($author__in) ";
2771                 }
2772
2773                 // Author stuff for nice URLs
2774
2775                 if ( '' != $q['author_name'] ) {
2776                         if ( strpos($q['author_name'], '/') !== false ) {
2777                                 $q['author_name'] = explode('/', $q['author_name']);
2778                                 if ( $q['author_name'][ count($q['author_name'])-1 ] ) {
2779                                         $q['author_name'] = $q['author_name'][count($q['author_name'])-1]; // no trailing slash
2780                                 } else {
2781                                         $q['author_name'] = $q['author_name'][count($q['author_name'])-2]; // there was a trailing slash
2782                                 }
2783                         }
2784                         $q['author_name'] = sanitize_title_for_query( $q['author_name'] );
2785                         $q['author'] = get_user_by('slug', $q['author_name']);
2786                         if ( $q['author'] )
2787                                 $q['author'] = $q['author']->ID;
2788                         $whichauthor .= " AND ($wpdb->posts.post_author = " . absint($q['author']) . ')';
2789                 }
2790
2791                 // MIME-Type stuff for attachment browsing
2792
2793                 if ( isset( $q['post_mime_type'] ) && '' != $q['post_mime_type'] )
2794                         $whichmimetype = wp_post_mime_type_where( $q['post_mime_type'], $wpdb->posts );
2795
2796                 $where .= $search . $whichauthor . $whichmimetype;
2797
2798                 if ( ! isset( $q['order'] ) ) {
2799                         $q['order'] = 'DESC';
2800                 } else {
2801                         $q['order'] = $this->parse_order( $q['order'] );
2802                 }
2803
2804                 // Order by.
2805                 if ( empty( $q['orderby'] ) ) {
2806                         /*
2807                          * Boolean false or empty array blanks out ORDER BY,
2808                          * while leaving the value unset or otherwise empty sets the default.
2809                          */
2810                         if ( isset( $q['orderby'] ) && ( is_array( $q['orderby'] ) || false === $q['orderby'] ) ) {
2811                                 $orderby = '';
2812                         } else {
2813                                 $orderby = "$wpdb->posts.post_date " . $q['order'];
2814                         }
2815                 } elseif ( 'none' == $q['orderby'] ) {
2816                         $orderby = '';
2817                 } elseif ( $q['orderby'] == 'post__in' && ! empty( $post__in ) ) {
2818                         $orderby = "FIELD( {$wpdb->posts}.ID, $post__in )";
2819                 } elseif ( $q['orderby'] == 'post_parent__in' && ! empty( $post_parent__in ) ) {
2820                         $orderby = "FIELD( {$wpdb->posts}.post_parent, $post_parent__in )";
2821                 } else {
2822                         $orderby_array = array();
2823                         if ( is_array( $q['orderby'] ) ) {
2824                                 foreach ( $q['orderby'] as $_orderby => $order ) {
2825                                         $orderby = addslashes_gpc( urldecode( $_orderby ) );
2826                                         $parsed  = $this->parse_orderby( $orderby );
2827
2828                                         if ( ! $parsed ) {
2829                                                 continue;
2830                                         }
2831
2832                                         $orderby_array[] = $parsed . ' ' . $this->parse_order( $order );
2833                                 }
2834                                 $orderby = implode( ', ', $orderby_array );
2835
2836                         } else {
2837                                 $q['orderby'] = urldecode( $q['orderby'] );
2838                                 $q['orderby'] = addslashes_gpc( $q['orderby'] );
2839
2840                                 foreach ( explode( ' ', $q['orderby'] ) as $i => $orderby ) {
2841                                         $parsed = $this->parse_orderby( $orderby );
2842                                         // Only allow certain values for safety.
2843                                         if ( ! $parsed ) {
2844                                                 continue;
2845                                         }
2846
2847                                         $orderby_array[] = $parsed;
2848                                 }
2849                                 $orderby = implode( ' ' . $q['order'] . ', ', $orderby_array );
2850
2851                                 if ( empty( $orderby ) ) {
2852                                         $orderby = "$wpdb->posts.post_date ".$q['order'];
2853                                 } else {
2854                                         $orderby .= " {$q['order']}";
2855                                 }
2856                         }
2857                 }
2858
2859                 // Order search results by relevance only when another "orderby" is not specified in the query.
2860                 if ( ! empty( $q['s'] ) ) {
2861                         $search_orderby = '';
2862                         if ( ! empty( $q['search_orderby_title'] ) && ( empty( $q['orderby'] ) && ! $this->is_feed ) || ( isset( $q['orderby'] ) && 'relevance' === $q['orderby'] ) )
2863                                 $search_orderby = $this->parse_search_order( $q );
2864
2865                         /**
2866                          * Filter the ORDER BY used when ordering search results.
2867                          *
2868                          * @since 3.7.0
2869                          *
2870                          * @param string   $search_orderby The ORDER BY clause.
2871                          * @param WP_Query $this           The current WP_Query instance.
2872                          */
2873                         $search_orderby = apply_filters( 'posts_search_orderby', $search_orderby, $this );
2874                         if ( $search_orderby )
2875                                 $orderby = $orderby ? $search_orderby . ', ' . $orderby : $search_orderby;
2876                 }
2877
2878                 if ( is_array( $post_type ) && count( $post_type ) > 1 ) {
2879                         $post_type_cap = 'multiple_post_type';
2880                 } else {
2881                         if ( is_array( $post_type ) )
2882                                 $post_type = reset( $post_type );
2883                         $post_type_object = get_post_type_object( $post_type );
2884                         if ( empty( $post_type_object ) )
2885                                 $post_type_cap = $post_type;
2886                 }
2887
2888                 if ( isset( $q['post_password'] ) ) {
2889                         $where .= $wpdb->prepare( " AND $wpdb->posts.post_password = %s", $q['post_password'] );
2890                         if ( empty( $q['perm'] ) ) {
2891                                 $q['perm'] = 'readable';
2892                         }
2893                 } elseif ( isset( $q['has_password'] ) ) {
2894                         $where .= sprintf( " AND $wpdb->posts.post_password %s ''", $q['has_password'] ? '!=' : '=' );
2895                 }
2896
2897                 if ( 'any' == $post_type ) {
2898                         $in_search_post_types = get_post_types( array('exclude_from_search' => false) );
2899                         if ( empty( $in_search_post_types ) )
2900                                 $where .= ' AND 1=0 ';
2901                         else
2902                                 $where .= " AND $wpdb->posts.post_type IN ('" . join("', '", $in_search_post_types ) . "')";
2903                 } elseif ( !empty( $post_type ) && is_array( $post_type ) ) {
2904                         $where .= " AND $wpdb->posts.post_type IN ('" . join("', '", $post_type) . "')";
2905                 } elseif ( ! empty( $post_type ) ) {
2906                         $where .= " AND $wpdb->posts.post_type = '$post_type'";
2907                         $post_type_object = get_post_type_object ( $post_type );
2908                 } elseif ( $this->is_attachment ) {
2909                         $where .= " AND $wpdb->posts.post_type = 'attachment'";
2910                         $post_type_object = get_post_type_object ( 'attachment' );
2911                 } elseif ( $this->is_page ) {
2912                         $where .= " AND $wpdb->posts.post_type = 'page'";
2913                         $post_type_object = get_post_type_object ( 'page' );
2914                 } else {
2915                         $where .= " AND $wpdb->posts.post_type = 'post'";
2916                         $post_type_object = get_post_type_object ( 'post' );
2917                 }
2918
2919                 $edit_cap = 'edit_post';
2920                 $read_cap = 'read_post';
2921
2922                 if ( ! empty( $post_type_object ) ) {
2923                         $edit_others_cap = $post_type_object->cap->edit_others_posts;
2924                         $read_private_cap = $post_type_object->cap->read_private_posts;
2925                 } else {
2926                         $edit_others_cap = 'edit_others_' . $post_type_cap . 's';
2927                         $read_private_cap = 'read_private_' . $post_type_cap . 's';
2928                 }
2929
2930                 $user_id = get_current_user_id();
2931
2932                 if ( ! empty( $q['post_status'] ) ) {
2933                         $statuswheres = array();
2934                         $q_status = $q['post_status'];
2935                         if ( ! is_array( $q_status ) )
2936                                 $q_status = explode(',', $q_status);
2937                         $r_status = array();
2938                         $p_status = array();
2939                         $e_status = array();
2940                         if ( in_array( 'any', $q_status ) ) {
2941                                 foreach ( get_post_stati( array( 'exclude_from_search' => true ) ) as $status ) {
2942                                         if ( ! in_array( $status, $q_status ) ) {
2943                                                 $e_status[] = "$wpdb->posts.post_status <> '$status'";
2944                                         }
2945                                 }
2946                         } else {
2947                                 foreach ( get_post_stati() as $status ) {
2948                                         if ( in_array( $status, $q_status ) ) {
2949                                                 if ( 'private' == $status )
2950                                                         $p_status[] = "$wpdb->posts.post_status = '$status'";
2951                                                 else
2952                                                         $r_status[] = "$wpdb->posts.post_status = '$status'";
2953                                         }
2954                                 }
2955                         }
2956
2957                         if ( empty($q['perm'] ) || 'readable' != $q['perm'] ) {
2958                                 $r_status = array_merge($r_status, $p_status);
2959                                 unset($p_status);
2960                         }
2961
2962                         if ( !empty($e_status) ) {
2963                                 $statuswheres[] = "(" . join( ' AND ', $e_status ) . ")";
2964                         }
2965                         if ( !empty($r_status) ) {
2966                                 if ( !empty($q['perm'] ) && 'editable' == $q['perm'] && !current_user_can($edit_others_cap) )
2967                                         $statuswheres[] = "($wpdb->posts.post_author = $user_id " . "AND (" . join( ' OR ', $r_status ) . "))";
2968                                 else
2969                                         $statuswheres[] = "(" . join( ' OR ', $r_status ) . ")";
2970                         }
2971                         if ( !empty($p_status) ) {
2972                                 if ( !empty($q['perm'] ) && 'readable' == $q['perm'] && !current_user_can($read_private_cap) )
2973                                         $statuswheres[] = "($wpdb->posts.post_author = $user_id " . "AND (" . join( ' OR ', $p_status ) . "))";
2974                                 else
2975                                         $statuswheres[] = "(" . join( ' OR ', $p_status ) . ")";
2976                         }
2977                         if ( $post_status_join ) {
2978                                 $join .= " LEFT JOIN $wpdb->posts AS p2 ON ($wpdb->posts.post_parent = p2.ID) ";
2979                                 foreach ( $statuswheres as $index => $statuswhere )
2980                                         $statuswheres[$index] = "($statuswhere OR ($wpdb->posts.post_status = 'inherit' AND " . str_replace($wpdb->posts, 'p2', $statuswhere) . "))";
2981                         }
2982                         $where_status = implode( ' OR ', $statuswheres );
2983                         if ( ! empty( $where_status ) ) {
2984                                 $where .= " AND ($where_status)";
2985                         }
2986                 } elseif ( !$this->is_singular ) {
2987                         $where .= " AND ($wpdb->posts.post_status = 'publish'";
2988
2989                         // Add public states.
2990                         $public_states = get_post_stati( array('public' => true) );
2991                         foreach ( (array) $public_states as $state ) {
2992                                 if ( 'publish' == $state ) // Publish is hard-coded above.
2993                                         continue;
2994                                 $where .= " OR $wpdb->posts.post_status = '$state'";
2995                         }
2996
2997                         if ( $this->is_admin ) {
2998                                 // Add protected states that should show in the admin all list.
2999                                 $admin_all_states = get_post_stati( array('protected' => true, 'show_in_admin_all_list' => true) );
3000                                 foreach ( (array) $admin_all_states as $state )
3001                                         $where .= " OR $wpdb->posts.post_status = '$state'";
3002                         }
3003
3004                         if ( is_user_logged_in() ) {
3005                                 // Add private states that are limited to viewing by the author of a post or someone who has caps to read private states.
3006                                 $private_states = get_post_stati( array('private' => true) );
3007                                 foreach ( (array) $private_states as $state )
3008                                         $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'";
3009                         }
3010
3011                         $where .= ')';
3012                 }
3013
3014                 if ( !empty( $this->meta_query->queries ) ) {
3015                         $clauses = $this->meta_query->get_sql( 'post', $wpdb->posts, 'ID', $this );
3016                         $join .= $clauses['join'];
3017                         $where .= $clauses['where'];
3018                 }
3019
3020                 /*
3021                  * Apply filters on where and join prior to paging so that any
3022                  * manipulations to them are reflected in the paging by day queries.
3023                  */
3024                 if ( !$q['suppress_filters'] ) {
3025                         /**
3026                          * Filter the WHERE clause of the query.
3027                          *
3028                          * @since 1.5.0
3029                          *
3030                          * @param string   $where The WHERE clause of the query.
3031                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3032                          */
3033                         $where = apply_filters_ref_array( 'posts_where', array( $where, &$this ) );
3034
3035                         /**
3036                          * Filter the JOIN clause of the query.
3037                          *
3038                          * @since 1.5.0
3039                          *
3040                          * @param string   $where The JOIN clause of the query.
3041                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3042                          */
3043                         $join = apply_filters_ref_array( 'posts_join', array( $join, &$this ) );
3044                 }
3045
3046                 // Paging
3047                 if ( empty($q['nopaging']) && !$this->is_singular ) {
3048                         $page = absint($q['paged']);
3049                         if ( !$page )
3050                                 $page = 1;
3051
3052                         if ( empty($q['offset']) ) {
3053                                 $pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
3054                         } else { // we're ignoring $page and using 'offset'
3055                                 $q['offset'] = absint($q['offset']);
3056                                 $pgstrt = $q['offset'] . ', ';
3057                         }
3058                         $limits = 'LIMIT ' . $pgstrt . $q['posts_per_page'];
3059                 }
3060
3061                 // Comments feeds
3062                 if ( $this->is_comment_feed && ! $this->is_singular ) {
3063                         if ( $this->is_archive || $this->is_search ) {
3064                                 $cjoin = "JOIN $wpdb->posts ON ($wpdb->comments.comment_post_ID = $wpdb->posts.ID) $join ";
3065                                 $cwhere = "WHERE comment_approved = '1' $where";
3066                                 $cgroupby = "$wpdb->comments.comment_id";
3067                         } else { // Other non singular e.g. front
3068                                 $cjoin = "JOIN $wpdb->posts ON ( $wpdb->comments.comment_post_ID = $wpdb->posts.ID )";
3069                                 $cwhere = "WHERE post_status = 'publish' AND comment_approved = '1'";
3070                                 $cgroupby = '';
3071                         }
3072
3073                         if ( !$q['suppress_filters'] ) {
3074                                 /**
3075                                  * Filter the JOIN clause of the comments feed query before sending.
3076                                  *
3077                                  * @since 2.2.0
3078                                  *
3079                                  * @param string   $cjoin The JOIN clause of the query.
3080                                  * @param WP_Query &$this The WP_Query instance (passed by reference).
3081                                  */
3082                                 $cjoin = apply_filters_ref_array( 'comment_feed_join', array( $cjoin, &$this ) );
3083
3084                                 /**
3085                                  * Filter the WHERE clause of the comments feed query before sending.
3086                                  *
3087                                  * @since 2.2.0
3088                                  *
3089                                  * @param string   $cwhere The WHERE clause of the query.
3090                                  * @param WP_Query &$this  The WP_Query instance (passed by reference).
3091                                  */
3092                                 $cwhere = apply_filters_ref_array( 'comment_feed_where', array( $cwhere, &$this ) );
3093
3094                                 /**
3095                                  * Filter the GROUP BY clause of the comments feed query before sending.
3096                                  *
3097                                  * @since 2.2.0
3098                                  *
3099                                  * @param string   $cgroupby The GROUP BY clause of the query.
3100                                  * @param WP_Query &$this    The WP_Query instance (passed by reference).
3101                                  */
3102                                 $cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( $cgroupby, &$this ) );
3103
3104                                 /**
3105                                  * Filter the ORDER BY clause of the comments feed query before sending.
3106                                  *
3107                                  * @since 2.8.0
3108                                  *
3109                                  * @param string   $corderby The ORDER BY clause of the query.
3110                                  * @param WP_Query &$this    The WP_Query instance (passed by reference).
3111                                  */
3112                                 $corderby = apply_filters_ref_array( 'comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
3113
3114                                 /**
3115                                  * Filter the LIMIT clause of the comments feed query before sending.
3116                                  *
3117                                  * @since 2.8.0
3118                                  *
3119                                  * @param string   $climits The JOIN clause of the query.
3120                                  * @param WP_Query &$this   The WP_Query instance (passed by reference).
3121                                  */
3122                                 $climits = apply_filters_ref_array( 'comment_feed_limits', array( 'LIMIT ' . get_option('posts_per_rss'), &$this ) );
3123                         }
3124                         $cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
3125                         $corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
3126
3127                         $this->comments = (array) $wpdb->get_results("SELECT $distinct $wpdb->comments.* FROM $wpdb->comments $cjoin $cwhere $cgroupby $corderby $climits");
3128                         $this->comment_count = count($this->comments);
3129
3130                         $post_ids = array();
3131
3132                         foreach ( $this->comments as $comment )
3133                                 $post_ids[] = (int) $comment->comment_post_ID;
3134
3135                         $post_ids = join(',', $post_ids);
3136                         $join = '';
3137                         if ( $post_ids )
3138                                 $where = "AND $wpdb->posts.ID IN ($post_ids) ";
3139                         else
3140                                 $where = "AND 0";
3141                 }
3142
3143                 $pieces = array( 'where', 'groupby', 'join', 'orderby', 'distinct', 'fields', 'limits' );
3144
3145                 /*
3146                  * Apply post-paging filters on where and join. Only plugins that
3147                  * manipulate paging queries should use these hooks.
3148                  */
3149                 if ( !$q['suppress_filters'] ) {
3150                         /**
3151                          * Filter the WHERE clause of the query.
3152                          *
3153                          * Specifically for manipulating paging queries.
3154                          *
3155                          * @since 1.5.0
3156                          *
3157                          * @param string   $where The WHERE clause of the query.
3158                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3159                          */
3160                         $where = apply_filters_ref_array( 'posts_where_paged', array( $where, &$this ) );
3161
3162                         /**
3163                          * Filter the GROUP BY clause of the query.
3164                          *
3165                          * @since 2.0.0
3166                          *
3167                          * @param string   $groupby The GROUP BY clause of the query.
3168                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3169                          */
3170                         $groupby = apply_filters_ref_array( 'posts_groupby', array( $groupby, &$this ) );
3171
3172                         /**
3173                          * Filter the JOIN clause of the query.
3174                          *
3175                          * Specifically for manipulating paging queries.
3176                          *
3177                          * @since 1.5.0
3178                          *
3179                          * @param string   $join  The JOIN clause of the query.
3180                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3181                          */
3182                         $join = apply_filters_ref_array( 'posts_join_paged', array( $join, &$this ) );
3183
3184                         /**
3185                          * Filter the ORDER BY clause of the query.
3186                          *
3187                          * @since 1.5.1
3188                          *
3189                          * @param string   $orderby The ORDER BY clause of the query.
3190                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3191                          */
3192                         $orderby = apply_filters_ref_array( 'posts_orderby', array( $orderby, &$this ) );
3193
3194                         /**
3195                          * Filter the DISTINCT clause of the query.
3196                          *
3197                          * @since 2.1.0
3198                          *
3199                          * @param string   $distinct The DISTINCT clause of the query.
3200                          * @param WP_Query &$this    The WP_Query instance (passed by reference).
3201                          */
3202                         $distinct = apply_filters_ref_array( 'posts_distinct', array( $distinct, &$this ) );
3203
3204                         /**
3205                          * Filter the LIMIT clause of the query.
3206                          *
3207                          * @since 2.1.0
3208                          *
3209                          * @param string   $limits The LIMIT clause of the query.
3210                          * @param WP_Query &$this  The WP_Query instance (passed by reference).
3211                          */
3212                         $limits = apply_filters_ref_array( 'post_limits', array( $limits, &$this ) );
3213
3214                         /**
3215                          * Filter the SELECT clause of the query.
3216                          *
3217                          * @since 2.1.0
3218                          *
3219                          * @param string   $fields The SELECT clause of the query.
3220                          * @param WP_Query &$this  The WP_Query instance (passed by reference).
3221                          */
3222                         $fields = apply_filters_ref_array( 'posts_fields', array( $fields, &$this ) );
3223
3224                         /**
3225                          * Filter all query clauses at once, for convenience.
3226                          *
3227                          * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
3228                          * fields (SELECT), and LIMITS clauses.
3229                          *
3230                          * @since 3.1.0
3231                          *
3232                          * @param array    $clauses The list of clauses for the query.
3233                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3234                          */
3235                         $clauses = (array) apply_filters_ref_array( 'posts_clauses', array( compact( $pieces ), &$this ) );
3236
3237                         $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
3238                         $groupby = isset( $clauses[ 'groupby' ] ) ? $clauses[ 'groupby' ] : '';
3239                         $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
3240                         $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
3241                         $distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
3242                         $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
3243                         $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
3244                 }
3245
3246                 /**
3247                  * Fires to announce the query's current selection parameters.
3248                  *
3249                  * For use by caching plugins.
3250                  *
3251                  * @since 2.3.0
3252                  *
3253                  * @param string $selection The assembled selection query.
3254                  */
3255                 do_action( 'posts_selection', $where . $groupby . $orderby . $limits . $join );
3256
3257                 /*
3258                  * Filter again for the benefit of caching plugins.
3259                  * Regular plugins should use the hooks above.
3260                  */
3261                 if ( !$q['suppress_filters'] ) {
3262                         /**
3263                          * Filter the WHERE clause of the query.
3264                          *
3265                          * For use by caching plugins.
3266                          *
3267                          * @since 2.5.0
3268                          *
3269                          * @param string   $where The WHERE clause of the query.
3270                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3271                          */
3272                         $where = apply_filters_ref_array( 'posts_where_request', array( $where, &$this ) );
3273
3274                         /**
3275                          * Filter the GROUP BY clause of the query.
3276                          *
3277                          * For use by caching plugins.
3278                          *
3279                          * @since 2.5.0
3280                          *
3281                          * @param string   $groupby The GROUP BY clause of the query.
3282                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3283                          */
3284                         $groupby = apply_filters_ref_array( 'posts_groupby_request', array( $groupby, &$this ) );
3285
3286                         /**
3287                          * Filter the JOIN clause of the query.
3288                          *
3289                          * For use by caching plugins.
3290                          *
3291                          * @since 2.5.0
3292                          *
3293                          * @param string   $join  The JOIN clause of the query.
3294                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3295                          */
3296                         $join = apply_filters_ref_array( 'posts_join_request', array( $join, &$this ) );
3297
3298                         /**
3299                          * Filter the ORDER BY clause of the query.
3300                          *
3301                          * For use by caching plugins.
3302                          *
3303                          * @since 2.5.0
3304                          *
3305                          * @param string   $orderby The ORDER BY clause of the query.
3306                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3307                          */
3308                         $orderby = apply_filters_ref_array( 'posts_orderby_request', array( $orderby, &$this ) );
3309
3310                         /**
3311                          * Filter the DISTINCT clause of the query.
3312                          *
3313                          * For use by caching plugins.
3314                          *
3315                          * @since 2.5.0
3316                          *
3317                          * @param string   $distinct The DISTINCT clause of the query.
3318                          * @param WP_Query &$this    The WP_Query instance (passed by reference).
3319                          */
3320                         $distinct = apply_filters_ref_array( 'posts_distinct_request', array( $distinct, &$this ) );
3321
3322                         /**
3323                          * Filter the SELECT clause of the query.
3324                          *
3325                          * For use by caching plugins.
3326                          *
3327                          * @since 2.5.0
3328                          *
3329                          * @param string   $fields The SELECT clause of the query.
3330                          * @param WP_Query &$this  The WP_Query instance (passed by reference).
3331                          */
3332                         $fields = apply_filters_ref_array( 'posts_fields_request', array( $fields, &$this ) );
3333
3334                         /**
3335                          * Filter the LIMIT clause of the query.
3336                          *
3337                          * For use by caching plugins.
3338                          *
3339                          * @since 2.5.0
3340                          *
3341                          * @param string   $limits The LIMIT clause of the query.
3342                          * @param WP_Query &$this  The WP_Query instance (passed by reference).
3343                          */
3344                         $limits = apply_filters_ref_array( 'post_limits_request', array( $limits, &$this ) );
3345
3346                         /**
3347                          * Filter all query clauses at once, for convenience.
3348                          *
3349                          * For use by caching plugins.
3350                          *
3351                          * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
3352                          * fields (SELECT), and LIMITS clauses.
3353                          *
3354                          * @since 3.1.0
3355                          *
3356                          * @param array    $pieces The pieces of the query.
3357                          * @param WP_Query &$this  The WP_Query instance (passed by reference).
3358                          */
3359                         $clauses = (array) apply_filters_ref_array( 'posts_clauses_request', array( compact( $pieces ), &$this ) );
3360
3361                         $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
3362                         $groupby = isset( $clauses[ 'groupby' ] ) ? $clauses[ 'groupby' ] : '';
3363                         $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
3364                         $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
3365                         $distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
3366                         $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
3367                         $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
3368                 }
3369
3370                 if ( ! empty($groupby) )
3371                         $groupby = 'GROUP BY ' . $groupby;
3372                 if ( !empty( $orderby ) )
3373                         $orderby = 'ORDER BY ' . $orderby;
3374
3375                 $found_rows = '';
3376                 if ( !$q['no_found_rows'] && !empty($limits) )
3377                         $found_rows = 'SQL_CALC_FOUND_ROWS';
3378
3379                 $this->request = $old_request = "SELECT $found_rows $distinct $fields FROM $wpdb->posts $join WHERE 1=1 $where $groupby $orderby $limits";
3380
3381                 if ( !$q['suppress_filters'] ) {
3382                         /**
3383                          * Filter the completed SQL query before sending.
3384                          *
3385                          * @since 2.0.0
3386                          *
3387                          * @param array    $request The complete SQL query.
3388                          * @param WP_Query &$this   The WP_Query instance (passed by reference).
3389                          */
3390                         $this->request = apply_filters_ref_array( 'posts_request', array( $this->request, &$this ) );
3391                 }
3392
3393                 if ( 'ids' == $q['fields'] ) {
3394                         $this->posts = $wpdb->get_col( $this->request );
3395                         $this->post_count = count( $this->posts );
3396                         $this->set_found_posts( $q, $limits );
3397
3398                         return array_map( 'intval', $this->posts );
3399                 }
3400
3401                 if ( 'id=>parent' == $q['fields'] ) {
3402                         $this->posts = $wpdb->get_results( $this->request );
3403                         $this->post_count = count( $this->posts );
3404                         $this->set_found_posts( $q, $limits );
3405
3406                         $r = array();
3407                         foreach ( $this->posts as $post ) {
3408                                 $r[ (int) $post->ID ] = (int) $post->post_parent;
3409                         }
3410                         return $r;
3411                 }
3412
3413                 $split_the_query = ( $old_request == $this->request && "$wpdb->posts.*" == $fields && !empty( $limits ) && $q['posts_per_page'] < 500 );
3414
3415                 /**
3416                  * Filter whether to split the query.
3417                  *
3418                  * Splitting the query will cause it to fetch just the IDs of the found posts
3419                  * (and then individually fetch each post by ID), rather than fetching every
3420                  * complete row at once. One massive result vs. many small results.
3421                  *
3422                  * @since 3.4.0
3423                  *
3424                  * @param bool     $split_the_query Whether or not to split the query.
3425                  * @param WP_Query $this            The WP_Query instance.
3426                  */
3427                 $split_the_query = apply_filters( 'split_the_query', $split_the_query, $this );
3428
3429                 if ( $split_the_query ) {
3430                         // First get the IDs and then fill in the objects
3431
3432                         $this->request = "SELECT $found_rows $distinct $wpdb->posts.ID FROM $wpdb->posts $join WHERE 1=1 $where $groupby $orderby $limits";
3433
3434                         /**
3435                          * Filter the Post IDs SQL request before sending.
3436                          *
3437                          * @since 3.4.0
3438                          *
3439                          * @param string   $request The post ID request.
3440                          * @param WP_Query $this    The WP_Query instance.
3441                          */
3442                         $this->request = apply_filters( 'posts_request_ids', $this->request, $this );
3443
3444                         $ids = $wpdb->get_col( $this->request );
3445
3446                         if ( $ids ) {
3447                                 $this->posts = $ids;
3448                                 $this->set_found_posts( $q, $limits );
3449                                 _prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
3450                         } else {
3451                                 $this->posts = array();
3452                         }
3453                 } else {
3454                         $this->posts = $wpdb->get_results( $this->request );
3455                         $this->set_found_posts( $q, $limits );
3456                 }
3457
3458                 // Convert to WP_Post objects
3459                 if ( $this->posts )
3460                         $this->posts = array_map( 'get_post', $this->posts );
3461
3462                 if ( ! $q['suppress_filters'] ) {
3463                         /**
3464                          * Filter the raw post results array, prior to status checks.
3465                          *
3466                          * @since 2.3.0
3467                          *
3468                          * @param array    $posts The post results array.
3469                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3470                          */
3471                         $this->posts = apply_filters_ref_array( 'posts_results', array( $this->posts, &$this ) );
3472                 }
3473
3474                 if ( !empty($this->posts) && $this->is_comment_feed && $this->is_singular ) {
3475                         /** This filter is documented in wp-includes/query.php */
3476                         $cjoin = apply_filters_ref_array( 'comment_feed_join', array( '', &$this ) );
3477
3478                         /** This filter is documented in wp-includes/query.php */
3479                         $cwhere = apply_filters_ref_array( 'comment_feed_where', array( "WHERE comment_post_ID = '{$this->posts[0]->ID}' AND comment_approved = '1'", &$this ) );
3480
3481                         /** This filter is documented in wp-includes/query.php */
3482                         $cgroupby = apply_filters_ref_array( 'comment_feed_groupby', array( '', &$this ) );
3483                         $cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
3484
3485                         /** This filter is documented in wp-includes/query.php */
3486                         $corderby = apply_filters_ref_array( 'comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
3487                         $corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
3488
3489                         /** This filter is documented in wp-includes/query.php */
3490                         $climits = apply_filters_ref_array( 'comment_feed_limits', array( 'LIMIT ' . get_option('posts_per_rss'), &$this ) );
3491
3492                         $comments_request = "SELECT $wpdb->comments.* FROM $wpdb->comments $cjoin $cwhere $cgroupby $corderby $climits";
3493                         $this->comments = $wpdb->get_results($comments_request);
3494                         $this->comment_count = count($this->comments);
3495                 }
3496
3497                 // Check post status to determine if post should be displayed.
3498                 if ( !empty($this->posts) && ($this->is_single || $this->is_page) ) {
3499                         $status = get_post_status($this->posts[0]);
3500                         $post_status_obj = get_post_status_object($status);
3501                         //$type = get_post_type($this->posts[0]);
3502                         if ( !$post_status_obj->public ) {
3503                                 if ( ! is_user_logged_in() ) {
3504                                         // User must be logged in to view unpublished posts.
3505                                         $this->posts = array();
3506                                 } else {
3507                                         if  ( $post_status_obj->protected ) {
3508                                                 // User must have edit permissions on the draft to preview.
3509                                                 if ( ! current_user_can($edit_cap, $this->posts[0]->ID) ) {
3510                                                         $this->posts = array();
3511                                                 } else {
3512                                                         $this->is_preview = true;
3513                                                         if ( 'future' != $status )
3514                                                                 $this->posts[0]->post_date = current_time('mysql');
3515                                                 }
3516                                         } elseif ( $post_status_obj->private ) {
3517                                                 if ( ! current_user_can($read_cap, $this->posts[0]->ID) )
3518                                                         $this->posts = array();
3519                                         } else {
3520                                                 $this->posts = array();
3521                                         }
3522                                 }
3523                         }
3524
3525                         if ( $this->is_preview && $this->posts && current_user_can( $edit_cap, $this->posts[0]->ID ) ) {
3526                                 /**
3527                                  * Filter the single post for preview mode.
3528                                  *
3529                                  * @since 2.7.0
3530                                  *
3531                                  * @param WP_Post  $post_preview  The Post object.
3532                                  * @param WP_Query &$this         The WP_Query instance (passed by reference).
3533                                  */
3534                                 $this->posts[0] = get_post( apply_filters_ref_array( 'the_preview', array( $this->posts[0], &$this ) ) );
3535                         }
3536                 }
3537
3538                 // Put sticky posts at the top of the posts array
3539                 $sticky_posts = get_option('sticky_posts');
3540                 if ( $this->is_home && $page <= 1 && is_array($sticky_posts) && !empty($sticky_posts) && !$q['ignore_sticky_posts'] ) {
3541                         $num_posts = count($this->posts);
3542                         $sticky_offset = 0;
3543                         // Loop over posts and relocate stickies to the front.
3544                         for ( $i = 0; $i < $num_posts; $i++ ) {
3545                                 if ( in_array($this->posts[$i]->ID, $sticky_posts) ) {
3546                                         $sticky_post = $this->posts[$i];
3547                                         // Remove sticky from current position
3548                                         array_splice($this->posts, $i, 1);
3549                                         // Move to front, after other stickies
3550                                         array_splice($this->posts, $sticky_offset, 0, array($sticky_post));
3551                                         // Increment the sticky offset. The next sticky will be placed at this offset.
3552                                         $sticky_offset++;
3553                                         // Remove post from sticky posts array
3554                                         $offset = array_search($sticky_post->ID, $sticky_posts);
3555                                         unset( $sticky_posts[$offset] );
3556                                 }
3557                         }
3558
3559                         // If any posts have been excluded specifically, Ignore those that are sticky.
3560                         if ( !empty($sticky_posts) && !empty($q['post__not_in']) )
3561                                 $sticky_posts = array_diff($sticky_posts, $q['post__not_in']);
3562
3563                         // Fetch sticky posts that weren't in the query results
3564                         if ( !empty($sticky_posts) ) {
3565                                 $stickies = get_posts( array(
3566                                         'post__in' => $sticky_posts,
3567                                         'post_type' => $post_type,
3568                                         'post_status' => 'publish',
3569                                         'nopaging' => true
3570                                 ) );
3571
3572                                 foreach ( $stickies as $sticky_post ) {
3573                                         array_splice( $this->posts, $sticky_offset, 0, array( $sticky_post ) );
3574                                         $sticky_offset++;
3575                                 }
3576                         }
3577                 }
3578
3579                 if ( ! $q['suppress_filters'] ) {
3580                         /**
3581                          * Filter the array of retrieved posts after they've been fetched and
3582                          * internally processed.
3583                          *
3584                          * @since 1.5.0
3585                          *
3586                          * @param array    $posts The array of retrieved posts.
3587                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3588                          */
3589                         $this->posts = apply_filters_ref_array( 'the_posts', array( $this->posts, &$this ) );
3590                 }
3591
3592                 // Ensure that any posts added/modified via one of the filters above are
3593                 // of the type WP_Post and are filtered.
3594                 if ( $this->posts ) {
3595                         $this->post_count = count( $this->posts );
3596
3597                         $this->posts = array_map( 'get_post', $this->posts );
3598
3599                         if ( $q['cache_results'] )
3600                                 update_post_caches($this->posts, $post_type, $q['update_post_term_cache'], $q['update_post_meta_cache']);
3601
3602                         $this->post = reset( $this->posts );
3603                 } else {
3604                         $this->post_count = 0;
3605                         $this->posts = array();
3606                 }
3607
3608                 return $this->posts;
3609         }
3610
3611         /**
3612          * Set up the amount of found posts and the number of pages (if limit clause was used)
3613          * for the current query.
3614          *
3615          * @since 3.5.0
3616          * @access private
3617          */
3618         private function set_found_posts( $q, $limits ) {
3619                 global $wpdb;
3620
3621                 // Bail if posts is an empty array. Continue if posts is an empty string,
3622                 // null, or false to accommodate caching plugins that fill posts later.
3623                 if ( $q['no_found_rows'] || ( is_array( $this->posts ) && ! $this->posts ) )
3624                         return;
3625
3626                 if ( ! empty( $limits ) ) {
3627                         /**
3628                          * Filter the query to run for retrieving the found posts.
3629                          *
3630                          * @since 2.1.0
3631                          *
3632                          * @param string   $found_posts The query to run to find the found posts.
3633                          * @param WP_Query &$this       The WP_Query instance (passed by reference).
3634                          */
3635                         $this->found_posts = $wpdb->get_var( apply_filters_ref_array( 'found_posts_query', array( 'SELECT FOUND_ROWS()', &$this ) ) );
3636                 } else {
3637                         $this->found_posts = count( $this->posts );
3638                 }
3639
3640                 /**
3641                  * Filter the number of found posts for the query.
3642                  *
3643                  * @since 2.1.0
3644                  *
3645                  * @param int      $found_posts The number of posts found.
3646                  * @param WP_Query &$this       The WP_Query instance (passed by reference).
3647                  */
3648                 $this->found_posts = apply_filters_ref_array( 'found_posts', array( $this->found_posts, &$this ) );
3649
3650                 if ( ! empty( $limits ) )
3651                         $this->max_num_pages = ceil( $this->found_posts / $q['posts_per_page'] );
3652         }
3653
3654         /**
3655          * Set up the next post and iterate current post index.
3656          *
3657          * @since 1.5.0
3658          * @access public
3659          *
3660          * @return WP_Post Next post.
3661          */
3662         public function next_post() {
3663
3664                 $this->current_post++;
3665
3666                 $this->post = $this->posts[$this->current_post];
3667                 return $this->post;
3668         }
3669
3670         /**
3671          * Sets up the current post.
3672          *
3673          * Retrieves the next post, sets up the post, sets the 'in the loop'
3674          * property to true.
3675          *
3676          * @since 1.5.0
3677          * @access public
3678          * @uses $post
3679          * @uses do_action_ref_array() Calls 'loop_start' if loop has just started
3680          */
3681         public function the_post() {
3682                 global $post;
3683                 $this->in_the_loop = true;
3684
3685                 if ( $this->current_post == -1 ) // loop has just started
3686                         /**
3687                          * Fires once the loop is started.
3688                          *
3689                          * @since 2.0.0
3690                          *
3691                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3692                          */
3693                         do_action_ref_array( 'loop_start', array( &$this ) );
3694
3695                 $post = $this->next_post();
3696                 setup_postdata($post);
3697         }
3698
3699         /**
3700          * Whether there are more posts available in the loop.
3701          *
3702          * Calls action 'loop_end', when the loop is complete.
3703          *
3704          * @since 1.5.0
3705          * @access public
3706          * @uses do_action_ref_array() Calls 'loop_end' if loop is ended
3707          *
3708          * @return bool True if posts are available, false if end of loop.
3709          */
3710         public function have_posts() {
3711                 if ( $this->current_post + 1 < $this->post_count ) {
3712                         return true;
3713                 } elseif ( $this->current_post + 1 == $this->post_count && $this->post_count > 0 ) {
3714                         /**
3715                          * Fires once the loop has ended.
3716                          *
3717                          * @since 2.0.0
3718                          *
3719                          * @param WP_Query &$this The WP_Query instance (passed by reference).
3720                          */
3721                         do_action_ref_array( 'loop_end', array( &$this ) );
3722                         // Do some cleaning up after the loop
3723                         $this->rewind_posts();
3724                 }
3725
3726                 $this->in_the_loop = false;
3727                 return false;
3728         }
3729
3730         /**
3731          * Rewind the posts and reset post index.
3732          *
3733          * @since 1.5.0
3734          * @access public
3735          */
3736         public function rewind_posts() {
3737                 $this->current_post = -1;
3738                 if ( $this->post_count > 0 ) {
3739                         $this->post = $this->posts[0];
3740                 }
3741         }
3742
3743         /**
3744          * Iterate current comment index and return comment object.
3745          *
3746          * @since 2.2.0
3747          * @access public
3748          *
3749          * @return object Comment object.
3750          */
3751         public function next_comment() {
3752                 $this->current_comment++;
3753
3754                 $this->comment = $this->comments[$this->current_comment];
3755                 return $this->comment;
3756         }
3757
3758         /**
3759          * Sets up the current comment.
3760          *
3761          * @since 2.2.0
3762          * @access public
3763          * @global object $comment Current comment.
3764          * @uses do_action() Calls 'comment_loop_start' hook when first comment is processed.
3765          */
3766         public function the_comment() {
3767                 global $comment;
3768
3769                 $comment = $this->next_comment();
3770
3771                 if ( $this->current_comment == 0 ) {
3772                         /**
3773                          * Fires once the comment loop is started.
3774                          *
3775                          * @since 2.2.0
3776                          */
3777                         do_action( 'comment_loop_start' );
3778                 }
3779         }
3780
3781         /**
3782          * Whether there are more comments available.
3783          *
3784          * Automatically rewinds comments when finished.
3785          *
3786          * @since 2.2.0
3787          * @access public
3788          *
3789          * @return bool True, if more comments. False, if no more posts.
3790          */
3791         public function have_comments() {
3792                 if ( $this->current_comment + 1 < $this->comment_count ) {
3793                         return true;
3794                 } elseif ( $this->current_comment + 1 == $this->comment_count ) {
3795                         $this->rewind_comments();
3796                 }
3797
3798                 return false;
3799         }
3800
3801         /**
3802          * Rewind the comments, resets the comment index and comment to first.
3803          *
3804          * @since 2.2.0
3805          * @access public
3806          */
3807         public function rewind_comments() {
3808                 $this->current_comment = -1;
3809                 if ( $this->comment_count > 0 ) {
3810                         $this->comment = $this->comments[0];
3811                 }
3812         }
3813
3814         /**
3815          * Sets up the WordPress query by parsing query string.
3816          *
3817          * @since 1.5.0
3818          * @access public
3819          *
3820          * @param string $query URL query string.
3821          * @return array List of posts.
3822          */
3823         public function query( $query ) {
3824                 $this->init();
3825                 $this->query = $this->query_vars = wp_parse_args( $query );
3826                 return $this->get_posts();
3827         }
3828
3829         /**
3830          * Retrieve queried object.
3831          *
3832          * If queried object is not set, then the queried object will be set from
3833          * the category, tag, taxonomy, posts page, single post, page, or author
3834          * query variable. After it is set up, it will be returned.
3835          *
3836          * @since 1.5.0
3837          * @access public
3838          *
3839          * @return object
3840          */
3841         public function get_queried_object() {
3842                 if ( isset($this->queried_object) )
3843                         return $this->queried_object;
3844
3845                 $this->queried_object = null;
3846                 $this->queried_object_id = 0;
3847
3848                 if ( $this->is_category || $this->is_tag || $this->is_tax ) {
3849                         if ( $this->is_category ) {
3850                                 if ( $this->get( 'cat' ) ) {
3851                                         $term = get_term( $this->get( 'cat' ), 'category' );
3852                                 } elseif ( $this->get( 'category_name' ) ) {
3853                                         $term = get_term_by( 'slug', $this->get( 'category_name' ), 'category' );
3854                                 }
3855                         } elseif ( $this->is_tag ) {
3856                                 if ( $this->get( 'tag_id' ) ) {
3857                                         $term = get_term( $this->get( 'tag_id' ), 'post_tag' );
3858                                 } elseif ( $this->get( 'tag' ) ) {
3859                                         $term = get_term_by( 'slug', $this->get( 'tag' ), 'post_tag' );
3860                                 }
3861                         } else {
3862                                 $tax_query_in_and = wp_list_filter( $this->tax_query->queries, array( 'operator' => 'NOT IN' ), 'NOT' );
3863                                 $query = reset( $tax_query_in_and );
3864
3865                                 if ( $query['terms'] ) {
3866                                         if ( 'term_id' == $query['field'] ) {
3867                                                 $term = get_term( reset( $query['terms'] ), $query['taxonomy'] );
3868                                         } else {
3869                                                 $term = get_term_by( $query['field'], reset( $query['terms'] ), $query['taxonomy'] );
3870                                         }
3871                                 }
3872                         }
3873
3874                         if ( ! empty( $term ) && ! is_wp_error( $term ) )  {
3875                                 $this->queried_object = $term;
3876                                 $this->queried_object_id = (int) $term->term_id;
3877
3878                                 if ( $this->is_category && 'category' === $this->queried_object->taxonomy )
3879                                         _make_cat_compat( $this->queried_object );
3880                         }
3881                 } elseif ( $this->is_post_type_archive ) {
3882                         $post_type = $this->get( 'post_type' );
3883                         if ( is_array( $post_type ) )
3884                                 $post_type = reset( $post_type );
3885                         $this->queried_object = get_post_type_object( $post_type );
3886                 } elseif ( $this->is_posts_page ) {
3887                         $page_for_posts = get_option('page_for_posts');
3888                         $this->queried_object = get_post( $page_for_posts );
3889                         $this->queried_object_id = (int) $this->queried_object->ID;
3890                 } elseif ( $this->is_singular && ! empty( $this->post ) ) {
3891                         $this->queried_object = $this->post;
3892                         $this->queried_object_id = (int) $this->post->ID;
3893                 } elseif ( $this->is_author ) {
3894                         $this->queried_object_id = (int) $this->get('author');
3895                         $this->queried_object = get_userdata( $this->queried_object_id );
3896                 }
3897
3898                 return $this->queried_object;
3899         }
3900
3901         /**
3902          * Retrieve ID of the current queried object.
3903          *
3904          * @since 1.5.0
3905          * @access public
3906          *
3907          * @return int
3908          */
3909         public function get_queried_object_id() {
3910                 $this->get_queried_object();
3911
3912                 if ( isset($this->queried_object_id) ) {
3913                         return $this->queried_object_id;
3914                 }
3915
3916                 return 0;
3917         }
3918
3919         /**
3920          * Constructor.
3921          *
3922          * Sets up the WordPress query, if parameter is not empty.
3923          *
3924          * @since 1.5.0
3925          * @access public
3926          *
3927          * @param string $query URL query string.
3928          * @return WP_Query
3929          */
3930         public function __construct($query = '') {
3931                 if ( ! empty($query) ) {
3932                         $this->query($query);
3933                 }
3934         }
3935
3936         /**
3937          * Make private properties readable for backwards compatibility.
3938          *
3939          * @since 4.0.0
3940          * @access public
3941          *
3942          * @param string $name Property to get.
3943          * @return mixed Property.
3944          */
3945         public function __get( $name ) {
3946                 return $this->$name;
3947         }
3948
3949         /**
3950          * Make private properties settable for backwards compatibility.
3951          *
3952          * @since 4.0.0
3953          * @access public
3954          *
3955          * @param string $name Property to check if set.
3956          * @return bool Whether the property is set.
3957          */
3958         public function __isset( $name ) {
3959                 return isset( $this->$name );
3960         }
3961
3962         /**
3963          * Make private properties settable for backwards compatibility.
3964          *
3965          * @since 4.0.0
3966          * @access public
3967          *
3968          * @param string $name Property to unset.
3969          */
3970         public function __unset( $name ) {
3971                 unset( $this->$name );
3972         }
3973
3974         /**
3975          * Make private/protected methods readable for backwards compatibility.
3976          *
3977          * @since 4.0.0
3978          * @access public
3979          *
3980          * @param callable $name      Method to call.
3981          * @param array    $arguments Arguments to pass when calling.
3982          * @return mixed|bool Return value of the callback, otherwise false.
3983          */
3984         public function __call( $name, $arguments ) {
3985                 return call_user_func_array( array( $this, $name ), $arguments );
3986         }
3987
3988         /**
3989          * Is the query for an existing archive page?
3990          *
3991          * Month, Year, Category, Author, Post Type archive...
3992          *
3993          * @since 3.1.0
3994          *
3995          * @return bool
3996          */
3997         public function is_archive() {
3998                 return (bool) $this->is_archive;
3999         }
4000
4001         /**
4002          * Is the query for an existing post type archive page?
4003          *
4004          * @since 3.1.0
4005          *
4006          * @param mixed $post_types Optional. Post type or array of posts types to check against.
4007          * @return bool
4008          */
4009         public function is_post_type_archive( $post_types = '' ) {
4010                 if ( empty( $post_types ) || ! $this->is_post_type_archive )
4011                         return (bool) $this->is_post_type_archive;
4012
4013                 $post_type = $this->get( 'post_type' );
4014                 if ( is_array( $post_type ) )
4015                         $post_type = reset( $post_type );
4016                 $post_type_object = get_post_type_object( $post_type );
4017
4018                 return in_array( $post_type_object->name, (array) $post_types );
4019         }
4020
4021         /**
4022          * Is the query for an existing attachment page?
4023          *
4024          * @since 3.1.0
4025          *
4026          * @param mixed $attachment Attachment ID, title, slug, or array of such.
4027          * @return bool
4028          */
4029         public function is_attachment( $attachment = '' ) {
4030                 if ( ! $this->is_attachment ) {
4031                         return false;
4032                 }
4033
4034                 if ( empty( $attachment ) ) {
4035                         return true;
4036                 }
4037
4038                 $attachment = (array) $attachment;
4039
4040                 $post_obj = $this->get_queried_object();
4041
4042                 if ( in_array( $post_obj->ID, $attachment ) ) {
4043                         return true;
4044                 } elseif ( in_array( $post_obj->post_title, $attachment ) ) {
4045                         return true;
4046                 } elseif ( in_array( $post_obj->post_name, $attachment ) ) {
4047                         return true;
4048                 }
4049                 return false;
4050         }
4051
4052         /**
4053          * Is the query for an existing author archive page?
4054          *
4055          * If the $author parameter is specified, this function will additionally
4056          * check if the query is for one of the authors specified.
4057          *
4058          * @since 3.1.0
4059          *
4060          * @param mixed $author Optional. User ID, nickname, nicename, or array of User IDs, nicknames, and nicenames
4061          * @return bool
4062          */
4063         public function is_author( $author = '' ) {
4064                 if ( !$this->is_author )
4065                         return false;
4066
4067                 if ( empty($author) )
4068                         return true;
4069
4070                 $author_obj = $this->get_queried_object();
4071
4072                 $author = (array) $author;
4073
4074                 if ( in_array( $author_obj->ID, $author ) )
4075                         return true;
4076                 elseif ( in_array( $author_obj->nickname, $author ) )
4077                         return true;
4078                 elseif ( in_array( $author_obj->user_nicename, $author ) )
4079                         return true;
4080
4081                 return false;
4082         }
4083
4084         /**
4085          * Is the query for an existing category archive page?
4086          *
4087          * If the $category parameter is specified, this function will additionally
4088          * check if the query is for one of the categories specified.
4089          *
4090          * @since 3.1.0
4091          *
4092          * @param mixed $category Optional. Category ID, name, slug, or array of Category IDs, names, and slugs.
4093          * @return bool
4094          */
4095         public function is_category( $category = '' ) {
4096                 if ( !$this->is_category )
4097                         return false;
4098
4099                 if ( empty($category) )
4100                         return true;
4101
4102                 $cat_obj = $this->get_queried_object();
4103
4104                 $category = (array) $category;
4105
4106                 if ( in_array( $cat_obj->term_id, $category ) )
4107                         return true;
4108                 elseif ( in_array( $cat_obj->name, $category ) )
4109                         return true;
4110                 elseif ( in_array( $cat_obj->slug, $category ) )
4111                         return true;
4112
4113                 return false;
4114         }
4115
4116         /**
4117          * Is the query for an existing tag archive page?
4118          *
4119          * If the $tag parameter is specified, this function will additionally
4120          * check if the query is for one of the tags specified.
4121          *
4122          * @since 3.1.0
4123          *
4124          * @param mixed $tag Optional. Tag ID, name, slug, or array of Tag IDs, names, and slugs.
4125          * @return bool
4126          */
4127         public function is_tag( $tag = '' ) {
4128                 if ( ! $this->is_tag )
4129                         return false;
4130
4131                 if ( empty( $tag ) )
4132                         return true;
4133
4134                 $tag_obj = $this->get_queried_object();
4135
4136                 $tag = (array) $tag;
4137
4138                 if ( in_array( $tag_obj->term_id, $tag ) )
4139                         return true;
4140                 elseif ( in_array( $tag_obj->name, $tag ) )
4141                         return true;
4142                 elseif ( in_array( $tag_obj->slug, $tag ) )
4143                         return true;
4144
4145                 return false;
4146         }
4147
4148         /**
4149          * Is the query for an existing taxonomy archive page?
4150          *
4151          * If the $taxonomy parameter is specified, this function will additionally
4152          * check if the query is for that specific $taxonomy.
4153          *
4154          * If the $term parameter is specified in addition to the $taxonomy parameter,
4155          * this function will additionally check if the query is for one of the terms
4156          * specified.
4157          *
4158          * @since 3.1.0
4159          *
4160          * @param mixed $taxonomy Optional. Taxonomy slug or slugs.
4161          * @param mixed $term. Optional. Term ID, name, slug or array of Term IDs, names, and slugs.
4162          * @return bool
4163          */
4164         public function is_tax( $taxonomy = '', $term = '' ) {
4165                 global $wp_taxonomies;
4166
4167                 if ( !$this->is_tax )
4168                         return false;
4169
4170                 if ( empty( $taxonomy ) )
4171                         return true;
4172
4173                 $queried_object = $this->get_queried_object();
4174                 $tax_array = array_intersect( array_keys( $wp_taxonomies ), (array) $taxonomy );
4175                 $term_array = (array) $term;
4176
4177                 // Check that the taxonomy matches.
4178                 if ( ! ( isset( $queried_object->taxonomy ) && count( $tax_array ) && in_array( $queried_object->taxonomy, $tax_array ) ) )
4179                         return false;
4180
4181                 // Only a Taxonomy provided.
4182                 if ( empty( $term ) )
4183                         return true;
4184
4185                 return isset( $queried_object->term_id ) &&
4186                         count( array_intersect(
4187                                 array( $queried_object->term_id, $queried_object->name, $queried_object->slug ),
4188                                 $term_array
4189                         ) );
4190         }
4191
4192         /**
4193          * Whether the current URL is within the comments popup window.
4194          *
4195          * @since 3.1.0
4196          *
4197          * @return bool
4198          */
4199         public function is_comments_popup() {
4200                 return (bool) $this->is_comments_popup;
4201         }
4202
4203         /**
4204          * Is the query for an existing date archive?
4205          *
4206          * @since 3.1.0
4207          *
4208          * @return bool
4209          */
4210         public function is_date() {
4211                 return (bool) $this->is_date;
4212         }
4213
4214         /**
4215          * Is the query for an existing day archive?
4216          *
4217          * @since 3.1.0
4218          *
4219          * @return bool
4220          */
4221         public function is_day() {
4222                 return (bool) $this->is_day;
4223         }
4224
4225         /**
4226          * Is the query for a feed?
4227          *
4228          * @since 3.1.0
4229          *
4230          * @param string|array $feeds Optional feed types to check.
4231          * @return bool
4232          */
4233         public function is_feed( $feeds = '' ) {
4234                 if ( empty( $feeds ) || ! $this->is_feed )
4235                         return (bool) $this->is_feed;
4236                 $qv = $this->get( 'feed' );
4237                 if ( 'feed' == $qv )
4238                         $qv = get_default_feed();
4239                 return in_array( $qv, (array) $feeds );
4240         }
4241
4242         /**
4243          * Is the query for a comments feed?
4244          *
4245          * @since 3.1.0
4246          *
4247          * @return bool
4248          */
4249         public function is_comment_feed() {
4250                 return (bool) $this->is_comment_feed;
4251         }
4252
4253         /**
4254          * Is the query for the front page of the site?
4255          *
4256          * This is for what is displayed at your site's main URL.
4257          *
4258          * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_on_front'.
4259          *
4260          * If you set a static page for the front page of your site, this function will return
4261          * true when viewing that page.
4262          *
4263          * Otherwise the same as @see WP_Query::is_home()
4264          *
4265          * @since 3.1.0
4266          * @uses is_home()
4267          * @uses get_option()
4268          *
4269          * @return bool True, if front of site.
4270          */
4271         public function is_front_page() {
4272                 // most likely case
4273                 if ( 'posts' == get_option( 'show_on_front') && $this->is_home() )
4274                         return true;
4275                 elseif ( 'page' == get_option( 'show_on_front') && get_option( 'page_on_front' ) && $this->is_page( get_option( 'page_on_front' ) ) )
4276                         return true;
4277                 else
4278                         return false;
4279         }
4280
4281         /**
4282          * Is the query for the blog homepage?
4283          *
4284          * This is the page which shows the time based blog content of your site.
4285          *
4286          * Depends on the site's "Front page displays" Reading Settings 'show_on_front' and 'page_for_posts'.
4287          *
4288          * If you set a static page for the front page of your site, this function will return
4289          * true only on the page you set as the "Posts page".
4290          *
4291          * @see WP_Query::is_front_page()
4292          *
4293          * @since 3.1.0
4294          *
4295          * @return bool True if blog view homepage.
4296          */
4297         public function is_home() {
4298                 return (bool) $this->is_home;
4299         }
4300
4301         /**
4302          * Is the query for an existing month archive?
4303          *
4304          * @since 3.1.0
4305          *
4306          * @return bool
4307          */
4308         public function is_month() {
4309                 return (bool) $this->is_month;
4310         }
4311
4312         /**
4313          * Is the query for an existing single page?
4314          *
4315          * If the $page parameter is specified, this function will additionally
4316          * check if the query is for one of the pages specified.
4317          *
4318          * @see WP_Query::is_single()
4319          * @see WP_Query::is_singular()
4320          *
4321          * @since 3.1.0
4322          *
4323          * @param mixed $page Page ID, title, slug, path, or array of such.
4324          * @return bool
4325          */
4326         public function is_page( $page = '' ) {
4327                 if ( !$this->is_page )
4328                         return false;
4329
4330                 if ( empty( $page ) )
4331                         return true;
4332
4333                 $page_obj = $this->get_queried_object();
4334
4335                 $page = (array) $page;
4336
4337                 if ( in_array( $page_obj->ID, $page ) ) {
4338                         return true;
4339                 } elseif ( in_array( $page_obj->post_title, $page ) ) {
4340                         return true;
4341                 } else if ( in_array( $page_obj->post_name, $page ) ) {
4342                         return true;
4343                 } else {
4344                         foreach ( $page as $pagepath ) {
4345                                 if ( ! strpos( $pagepath, '/' ) ) {
4346                                         continue;
4347                                 }
4348                                 $pagepath_obj = get_page_by_path( $pagepath );
4349
4350                                 if ( $pagepath_obj && ( $pagepath_obj->ID == $page_obj->ID ) ) {
4351                                         return true;
4352                                 }
4353                         }
4354                 }
4355
4356                 return false;
4357         }
4358
4359         /**
4360          * Is the query for paged result and not for the first page?
4361          *
4362          * @since 3.1.0
4363          *
4364          * @return bool
4365          */
4366         public function is_paged() {
4367                 return (bool) $this->is_paged;
4368         }
4369
4370         /**
4371          * Is the query for a post or page preview?
4372          *
4373          * @since 3.1.0
4374          *
4375          * @return bool
4376          */
4377         public function is_preview() {
4378                 return (bool) $this->is_preview;
4379         }
4380
4381         /**
4382          * Is the query for the robots file?
4383          *
4384          * @since 3.1.0
4385          *
4386          * @return bool
4387          */
4388         public function is_robots() {
4389                 return (bool) $this->is_robots;
4390         }
4391
4392         /**
4393          * Is the query for a search?
4394          *
4395          * @since 3.1.0
4396          *
4397          * @return bool
4398          */
4399         public function is_search() {
4400                 return (bool) $this->is_search;
4401         }
4402
4403         /**
4404          * Is the query for an existing single post?
4405          *
4406          * Works for any post type, except attachments and pages
4407          *
4408          * If the $post parameter is specified, this function will additionally
4409          * check if the query is for one of the Posts specified.
4410          *
4411          * @see WP_Query::is_page()
4412          * @see WP_Query::is_singular()
4413          *
4414          * @since 3.1.0
4415          *
4416          * @param mixed $post Post ID, title, slug, path, or array of such.
4417          * @return bool
4418          */
4419         public function is_single( $post = '' ) {
4420                 if ( !$this->is_single )
4421                         return false;
4422
4423                 if ( empty($post) )
4424                         return true;
4425
4426                 $post_obj = $this->get_queried_object();
4427
4428                 $post = (array) $post;
4429
4430                 if ( in_array( $post_obj->ID, $post ) ) {
4431                         return true;
4432                 } elseif ( in_array( $post_obj->post_title, $post ) ) {
4433                         return true;
4434                 } elseif ( in_array( $post_obj->post_name, $post ) ) {
4435                         return true;
4436                 } else {
4437                         foreach ( $post as $postpath ) {
4438                                 if ( ! strpos( $postpath, '/' ) ) {
4439                                         continue;
4440                                 }
4441                                 $postpath_obj = get_page_by_path( $postpath, OBJECT, $post_obj->post_type );
4442
4443                                 if ( $postpath_obj && ( $postpath_obj->ID == $post_obj->ID ) ) {
4444                                         return true;
4445                                 }
4446                         }
4447                 }
4448                 return false;
4449         }
4450
4451         /**
4452          * Is the query for an existing single post of any post type (post, attachment, page, ... )?
4453          *
4454          * If the $post_types parameter is specified, this function will additionally
4455          * check if the query is for one of the Posts Types specified.
4456          *
4457          * @see WP_Query::is_page()
4458          * @see WP_Query::is_single()
4459          *
4460          * @since 3.1.0
4461          *
4462          * @param mixed $post_types Optional. Post Type or array of Post Types
4463          * @return bool
4464          */
4465         public function is_singular( $post_types = '' ) {
4466                 if ( empty( $post_types ) || !$this->is_singular )
4467                         return (bool) $this->is_singular;
4468
4469                 $post_obj = $this->get_queried_object();
4470
4471                 return in_array( $post_obj->post_type, (array) $post_types );
4472         }
4473
4474         /**
4475          * Is the query for a specific time?
4476          *
4477          * @since 3.1.0
4478          *
4479          * @return bool
4480          */
4481         public function is_time() {
4482                 return (bool) $this->is_time;
4483         }
4484
4485         /**
4486          * Is the query for a trackback endpoint call?
4487          *
4488          * @since 3.1.0
4489          *
4490          * @return bool
4491          */
4492         public function is_trackback() {
4493                 return (bool) $this->is_trackback;
4494         }
4495
4496         /**
4497          * Is the query for an existing year archive?
4498          *
4499          * @since 3.1.0
4500          *
4501          * @return bool
4502          */
4503         public function is_year() {
4504                 return (bool) $this->is_year;
4505         }
4506
4507         /**
4508          * Is the query a 404 (returns no results)?
4509          *
4510          * @since 3.1.0
4511          *
4512          * @return bool
4513          */
4514         public function is_404() {
4515                 return (bool) $this->is_404;
4516         }
4517
4518         /**
4519          * Is the query the main query?
4520          *
4521          * @since 3.3.0
4522          *
4523          * @return bool
4524          */
4525         public function is_main_query() {
4526                 global $wp_the_query;
4527                 return $wp_the_query === $this;
4528         }
4529
4530         /**
4531          * After looping through a nested query, this function
4532          * restores the $post global to the current post in this query.
4533          *
4534          * @since 3.7.0
4535          *
4536          * @return bool
4537          */
4538         public function reset_postdata() {
4539                 if ( ! empty( $this->post ) ) {
4540                         $GLOBALS['post'] = $this->post;
4541                         setup_postdata( $this->post );
4542                 }
4543         }
4544 }
4545
4546 /**
4547  * Redirect old slugs to the correct permalink.
4548  *
4549  * Attempts to find the current slug from the past slugs.
4550  *
4551  * @since 2.1.0
4552  * @uses $wp_query
4553  * @uses $wpdb
4554  *
4555  * @return null If no link is found, null is returned.
4556  */
4557 function wp_old_slug_redirect() {
4558         global $wp_query;
4559         if ( is_404() && '' != $wp_query->query_vars['name'] ) :
4560                 global $wpdb;
4561
4562                 // Guess the current post_type based on the query vars.
4563                 if ( get_query_var('post_type') )
4564                         $post_type = get_query_var('post_type');
4565                 elseif ( !empty($wp_query->query_vars['pagename']) )
4566                         $post_type = 'page';
4567                 else
4568                         $post_type = 'post';
4569
4570                 if ( is_array( $post_type ) ) {
4571                         if ( count( $post_type ) > 1 )
4572                                 return;
4573                         $post_type = array_shift( $post_type );
4574                 }
4575
4576                 // Do not attempt redirect for hierarchical post types
4577                 if ( is_post_type_hierarchical( $post_type ) )
4578                         return;
4579
4580                 $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']);
4581
4582                 // if year, monthnum, or day have been specified, make our query more precise
4583                 // just in case there are multiple identical _wp_old_slug values
4584                 if ( '' != $wp_query->query_vars['year'] )
4585                         $query .= $wpdb->prepare(" AND YEAR(post_date) = %d", $wp_query->query_vars['year']);
4586                 if ( '' != $wp_query->query_vars['monthnum'] )
4587                         $query .= $wpdb->prepare(" AND MONTH(post_date) = %d", $wp_query->query_vars['monthnum']);
4588                 if ( '' != $wp_query->query_vars['day'] )
4589                         $query .= $wpdb->prepare(" AND DAYOFMONTH(post_date) = %d", $wp_query->query_vars['day']);
4590
4591                 $id = (int) $wpdb->get_var($query);
4592
4593                 if ( ! $id )
4594                         return;
4595
4596                 $link = get_permalink($id);
4597
4598                 if ( !$link )
4599                         return;
4600
4601                 wp_redirect( $link, 301 ); // Permanent redirect
4602                 exit;
4603         endif;
4604 }
4605
4606 /**
4607  * Set up global post data.
4608  *
4609  * @since 1.5.0
4610  *
4611  * @param object $post Post data.
4612  * @uses do_action_ref_array() Calls 'the_post'
4613  * @return bool True when finished.
4614  */
4615 function setup_postdata( $post ) {
4616         global $id, $authordata, $currentday, $currentmonth, $page, $pages, $multipage, $more, $numpages;
4617
4618         $id = (int) $post->ID;
4619
4620         $authordata = get_userdata($post->post_author);
4621
4622         $currentday = mysql2date('d.m.y', $post->post_date, false);
4623         $currentmonth = mysql2date('m', $post->post_date, false);
4624         $numpages = 1;
4625         $multipage = 0;
4626         $page = get_query_var('page');
4627         if ( ! $page )
4628                 $page = 1;
4629         if ( is_single() || is_page() || is_feed() )
4630                 $more = 1;
4631         $content = $post->post_content;
4632         if ( false !== strpos( $content, '<!--nextpage-->' ) ) {
4633                 if ( $page > 1 )
4634                         $more = 1;
4635                 $content = str_replace( "\n<!--nextpage-->\n", '<!--nextpage-->', $content );
4636                 $content = str_replace( "\n<!--nextpage-->", '<!--nextpage-->', $content );
4637                 $content = str_replace( "<!--nextpage-->\n", '<!--nextpage-->', $content );
4638                 // Ignore nextpage at the beginning of the content.
4639                 if ( 0 === strpos( $content, '<!--nextpage-->' ) )
4640                         $content = substr( $content, 15 );
4641                 $pages = explode('<!--nextpage-->', $content);
4642                 $numpages = count($pages);
4643                 if ( $numpages > 1 )
4644                         $multipage = 1;
4645         } else {
4646                 $pages = array( $post->post_content );
4647         }
4648
4649         /**
4650          * Fires once the post data has been setup.
4651          *
4652          * @since 2.8.0
4653          *
4654          * @param WP_Post &$post The Post object (passed by reference).
4655          */
4656         do_action_ref_array( 'the_post', array( &$post ) );
4657
4658         return true;
4659 }