angelovcom.net

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

class-wp-query.php (134784B)


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