Make WordPress Core

source: branches/4.4/src/wp-includes/query.php @ 36354

Last change on this file since 36354 was 36354, checked in by dd32, 6 years ago

Query: Avoid invalid SQL when building ORDER BY clause using long search strings.

The introduction of negative search terms in 4.4 [34934] introduced the
possibility that the ORDER BY clause of a search query could be assembled in
such a way as to create invalid syntax. The current changeset fixes this by
ensuring that the ORDER BY clause corresponding to the search terms is
excluded when it would otherwise be empty.

Merges [36251] to the 4.4 branch.
Props salvoaranzulla, boonebgorges.
Fixes #35361.

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