[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> class-wp-query.php (source)

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